/* global React, Icon, AppFieldLabel, useSelectedWorkspaceId, getWorkspaceSearchParams, getEmbeddedStarterList, getEmbeddedPositionLabel, capitalizeEmbedded, EMBEDDED_MODEL_OPTIONS, EMBEDDED_VOICE_OPTIONS, embeddedModelSupportsTemperature */
const {
  useEffect: useEffectPlayground,
  useMemo: useMemoPlayground,
  useRef: useRefPlayground,
  useState: useStatePlayground,
} = React;

function PlaygroundPage() {
  const workspaceId = useSelectedWorkspaceId();
  const [templates, setTemplates] = useStatePlayground([]);
  const [selectedBotId, setSelectedBotId] = useStatePlayground('');
  const [editingSettings, setEditingSettings] = useStatePlayground(null);
  const [loadedTemplate, setLoadedTemplate] = useStatePlayground(null);
  const [loading, setLoading] = useStatePlayground(true);
  const [saving, setSaving] = useStatePlayground(false);
  const [notice, setNotice] = useStatePlayground('');
  const [error, setError] = useStatePlayground('');
  const [isDirty, setIsDirty] = useStatePlayground(false);
  const [runKey, setRunKey] = useStatePlayground(0);

  const selectedTemplate = useMemoPlayground(() => {
    return templates.find(template => template.botId === selectedBotId) || null;
  }, [templates, selectedBotId]);

  const selectedSettings = selectedTemplate?.settings || {};
  const activeSettings = editingSettings || normalizePlaygroundSettings(selectedTemplate);
  const selectedDomains = useMemoPlayground(() => parsePlaygroundDomains(selectedSettings.allowedDomains), [selectedSettings.allowedDomains]);
  const activeDomains = useMemoPlayground(() => parsePlaygroundDomains(activeSettings?.allowedDomains), [activeSettings?.allowedDomains]);

  const loadTemplates = async () => {
    setLoading(true);
    setError('');

    try {
      const data = await fetchPlaygroundJson(`/api/embedded-bots${getWorkspaceSearchParams(workspaceId)}`);
      const bots = Array.isArray(data.bots) ? data.bots : [];
      setTemplates(bots);

      const nextSelected = bots.some(template => template.botId === selectedBotId)
        ? selectedBotId
        : bots[0]?.botId || '';

      setSelectedBotId(nextSelected);

      if (nextSelected && !editingSettings) {
        const nextTemplate = bots.find(template => template.botId === nextSelected) || null;
        setEditingSettings(normalizePlaygroundSettings(nextTemplate));
      }
    } catch (fetchError) {
      setError(fetchError.message || 'Unable to load templates.');
    } finally {
      setLoading(false);
    }
  };

  useEffectPlayground(() => {
    setLoadedTemplate(null);
    setEditingSettings(null);
    setIsDirty(false);
    setNotice('');
    setError('');
    loadTemplates();
  }, [workspaceId]);

  useEffectPlayground(() => {
    if (!selectedTemplate) {
      setEditingSettings(null);
      setIsDirty(false);
      return;
    }

    setEditingSettings(normalizePlaygroundSettings(selectedTemplate));
    setIsDirty(false);
    setNotice('');
    setError('');
  }, [selectedBotId]);

  const updateSetting = (key, value) => {
    setEditingSettings(current => ({
      ...normalizePlaygroundSettings(selectedTemplate),
      ...(current || {}),
      [key]: value,
    }));
    setIsDirty(true);
    setNotice('');
    setError('');
  };

  const loadForEdit = () => {
    if (!selectedTemplate) {
      setError('Choose a saved template first.');
      return;
    }

    setEditingSettings(normalizePlaygroundSettings(selectedTemplate));
    setLoadedTemplate(buildPlaygroundTemplate(selectedTemplate, normalizePlaygroundSettings(selectedTemplate)));
    setRunKey(current => current + 1);
    setIsDirty(false);
    setNotice('Template loaded for testing.');
    setError('');
  };

  const saveTemplate = async (options = {}) => {
    if (!selectedTemplate || !editingSettings) {
      setError('Choose a saved template first.');
      return null;
    }

    const validationError = validatePlaygroundSettings(editingSettings);
    if (validationError) {
      setError(validationError);
      return null;
    }

    setSaving(true);
    setError('');
    setNotice('');

    try {
      const payload = {
        ...editingSettings,
        workspaceId,
        colorMode: 'solid',
        gradientColor: editingSettings.color,
        chatBackgroundColor: '#000000',
      };

      const data = await fetchPlaygroundJson(`/api/embedded-bots/${encodeURIComponent(selectedTemplate.botId)}`, {
        method: 'PATCH',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify(payload),
      });

      const nextTemplate = buildPlaygroundTemplate(selectedTemplate, {
        ...editingSettings,
        editingBotId: data.botId || selectedTemplate.botId,
      }, data);

      setTemplates(current => current.map(template => (
        template.botId === selectedTemplate.botId ? nextTemplate : template
      )));
      setEditingSettings(nextTemplate.settings);
      setLoadedTemplate(nextTemplate);
      setIsDirty(false);
      setNotice(options.test ? 'Template saved. Widget preview refreshed for testing.' : 'Template saved successfully.');

      if (options.test) {
        setRunKey(current => current + 1);
      }

      return nextTemplate;
    } catch (saveError) {
      setError(saveError.message || 'Unable to save template.');
      return null;
    } finally {
      setSaving(false);
    }
  };

  const testSavedVersion = () => {
    if (!selectedTemplate) {
      setError('Choose a saved template first.');
      return;
    }

    const savedSettings = normalizePlaygroundSettings(selectedTemplate);
    const nextTemplate = buildPlaygroundTemplate(selectedTemplate, savedSettings);
    setEditingSettings(savedSettings);
    setLoadedTemplate(nextTemplate);
    setRunKey(current => current + 1);
    setIsDirty(false);
    setNotice('Saved version loaded in the widget preview.');
    setError('');
  };

  const resetTest = () => {
    if (!loadedTemplate && !selectedTemplate) return;

    if (loadedTemplate) {
      setRunKey(current => current + 1);
    } else {
      testSavedVersion();
    }
  };

  return (
    <div className="view playground-view playground-v2-view">
      <div className="view-head playground-v2-head">
        <div>
          <h1>Playground</h1>
          <div className="sub">Edit saved templates and test a real chatbot-style experience.</div>
        </div>
        <div className="view-head-actions">
          <button className="btn btn-secondary" type="button" onClick={loadTemplates} disabled={loading || saving}>
            <Icon name="refresh" size={14}/>{loading ? 'Refreshing' : 'Refresh'}
          </button>
          <button className="btn btn-secondary" type="button" onClick={resetTest} disabled={!selectedTemplate || saving}>
            <Icon name="refresh" size={14}/>Reset Test
          </button>
          <button className="btn btn-primary" type="button" onClick={() => saveTemplate({ test: true })} disabled={!selectedTemplate || saving}>
            <Icon name="messageCircle" size={14}/>{saving ? 'Saving' : 'Save & Test'}
          </button>
        </div>
      </div>

      {(notice || error || isDirty) && (
        <div className={`playground-v2-feedback ${error ? 'is-error' : isDirty ? 'is-warning' : 'is-success'}`}>
          <strong>{error ? 'Playground issue' : isDirty ? 'Unsaved changes' : 'Playground update'}</strong>
          <span>{error || (isDirty ? 'Save the template before testing the latest changes.' : notice)}</span>
        </div>
      )}

      <div className="playground-v2-shell">
        <aside className="card playground-v2-editor" data-tour="playground">
          <div className="playground-v2-panel-head">
            <span className="icon-chip"><Icon name="fileCode" size={19}/></span>
            <div>
              <h2>Template Editor</h2>
              <span>{loading ? 'Loading templates' : `${templates.length} saved templates`}</span>
            </div>
          </div>

          {templates.length === 0 && !loading ? (
            <div className="playground-empty">
              <span className="playground-empty-icon"><Icon name="bot" size={22}/></span>
              <strong>No templates yet</strong>
              <span>Create and save a template in AI Builder first.</span>
              <a className="btn btn-primary btn-sm" href="/ai-builder">Open AI Builder</a>
            </div>
          ) : (
            <>
              <div className="embedded-form-row playground-v2-picker">
                <AppFieldLabel label="Saved Template" htmlFor="playground-template"/>
                <select
                  id="playground-template"
                  className="embedded-input"
                  value={selectedBotId}
                  onChange={event => setSelectedBotId(event.target.value)}
                  disabled={loading || saving}
                >
                  {templates.map(template => (
                    <option key={template.botId} value={template.botId}>
                      {template.templateName || template.botName || template.botId}
                    </option>
                  ))}
                </select>
              </div>

              <div className="playground-v2-editor-actions">
                <button className="btn btn-secondary" type="button" onClick={loadForEdit} disabled={!selectedTemplate || saving}>
                  <Icon name="download" size={14}/>Load for Edit
                </button>
                <button className="btn btn-primary" type="button" onClick={() => saveTemplate({ test: true })} disabled={!selectedTemplate || saving}>
                  <Icon name="checkCircle" size={14}/>{saving ? 'Saving' : 'Save & Test'}
                </button>
              </div>
            </>
          )}

          {activeSettings && (
            <div className="playground-v2-editor-scroll">
              <PlaygroundEditorSection title="Template" icon="briefcase">
                <PlaygroundTextInput label="Template Name" value={activeSettings.templateName} onChange={value => updateSetting('templateName', value)} />
                <PlaygroundTextInput label="Bot Name" value={activeSettings.botName} onChange={value => updateSetting('botName', value)} />
                <PlaygroundTextArea label="Opening Message" rows="3" value={activeSettings.openingMessage} onChange={value => updateSetting('openingMessage', value)} />
                <PlaygroundTextArea label="Conversation Starters" rows="4" value={activeSettings.starters} onChange={value => updateSetting('starters', value)} help="One starter per line." />
              </PlaygroundEditorSection>

              <PlaygroundEditorSection title="AI Settings" icon="brain">
                <PlaygroundSelect label="Model" value={activeSettings.model} onChange={value => updateSetting('model', value)}>
                  <option value="">Choose a reply model</option>
                  {(EMBEDDED_MODEL_OPTIONS || []).map(model => <option key={model} value={model}>{model}</option>)}
                </PlaygroundSelect>
                <PlaygroundRange label="Temperature" value={activeSettings.temperature} min="0" max="1" step="0.1" disabled={!embeddedModelSupportsTemperature(activeSettings.model)} onChange={value => updateSetting('temperature', value)} />
                <PlaygroundNumberInput label="Max Reply Tokens" value={activeSettings.maxReplyTokens} min="64" max="4096" onChange={value => updateSetting('maxReplyTokens', value)} />
                <PlaygroundNumberInput label="Rate Limit" value={activeSettings.rateLimit} min="0" max="20" onChange={value => updateSetting('rateLimit', value)} />
                <PlaygroundTextArea label="System Instruction" rows="5" value={activeSettings.systemInstruction} onChange={value => updateSetting('systemInstruction', value)} />
                <PlaygroundTextArea label="Guardrails" rows="4" value={activeSettings.guardrails} onChange={value => updateSetting('guardrails', value)} />
              </PlaygroundEditorSection>

              <PlaygroundEditorSection title="Capture & Access" icon="contact">
                <PlaygroundToggle label="Visitor Capture" checked={Boolean(activeSettings.visitorCaptureEnabled)} onChange={value => updateSetting('visitorCaptureEnabled', value)} />
                <PlaygroundSelect label="Capture Mode" value={activeSettings.visitorCaptureMode} disabled={!activeSettings.visitorCaptureEnabled} onChange={value => updateSetting('visitorCaptureMode', value)}>
                  <option value="chat">Chat capture</option>
                  <option value="form">Popup form</option>
                </PlaygroundSelect>
                <PlaygroundTextArea label="Capture Message" rows="4" value={activeSettings.visitorCapturePrompt} disabled={!activeSettings.visitorCaptureEnabled} onChange={value => updateSetting('visitorCapturePrompt', value)} />
                <PlaygroundTextArea label="Allowed Domains" rows="3" value={activeSettings.allowedDomains} onChange={value => updateSetting('allowedDomains', value)} help="Comma-separated approved website domains." />
              </PlaygroundEditorSection>

              <PlaygroundEditorSection title="Widget & Voice" icon="palette">
                <PlaygroundColorInput label="Brand Color" value={activeSettings.color} onChange={value => updateSetting('color', value)} />
                <PlaygroundSelect label="Position" value={activeSettings.position} onChange={value => updateSetting('position', value)}>
                  <option value="right">Bottom Right</option>
                  <option value="left">Bottom Left</option>
                  <option value="top_right">Top Right</option>
                  <option value="top_left">Top Left</option>
                </PlaygroundSelect>
                <PlaygroundToggle label="Voice Replies" checked={Boolean(activeSettings.enableTts)} onChange={value => updateSetting('enableTts', value)} />
                <PlaygroundSelect label="TTS Model" value={activeSettings.ttsModel} disabled={!activeSettings.enableTts} onChange={value => updateSetting('ttsModel', value)}>
                  <option value="gpt-4o-mini-tts">gpt-4o-mini-tts</option>
                  <option value="tts-1">tts-1</option>
                  <option value="tts-1-hd">tts-1-hd</option>
                </PlaygroundSelect>
                <PlaygroundSelect label="Voice" value={activeSettings.voice} disabled={!activeSettings.enableTts} onChange={value => updateSetting('voice', value)}>
                  {(EMBEDDED_VOICE_OPTIONS || []).map(voice => <option key={voice} value={voice}>{capitalizeEmbedded(voice)}</option>)}
                </PlaygroundSelect>
              </PlaygroundEditorSection>
            </div>
          )}
        </aside>

        <section className="card playground-v2-preview-card">
          <div className="playground-v2-preview-head">
            <div>
              <span className="eyebrow">Real Widget Test</span>
              <h2>{loadedTemplate?.templateName || selectedTemplate?.templateName || 'Load a template'}</h2>
            </div>
            <span className={`pill ${loadedTemplate ? 'success' : 'dim'}`}>{loadedTemplate ? 'Live Preview' : 'Idle'}</span>
          </div>

          <div className="playground-v2-preview-note">
            <Icon name="info" size={15}/>
            <span>This test widget uses the same chat API path and supports visitor capture, human handoff, starters, reset, export, and voice playback.</span>
          </div>

          <PlaygroundWidgetPreview
            template={loadedTemplate}
            fallbackTemplate={selectedTemplate}
            settings={loadedTemplate?.settings || activeSettings}
            runKey={runKey}
          />

          <div className="playground-v2-preview-actions">
            <button className="btn btn-secondary" type="button" onClick={testSavedVersion} disabled={!selectedTemplate || saving}>
              <Icon name="refresh" size={14}/>Test Saved Version
            </button>
            <button className="btn btn-primary" type="button" onClick={() => saveTemplate({ test: true })} disabled={!selectedTemplate || saving}>
              <Icon name="messageCircle" size={14}/>{saving ? 'Saving' : 'Save & Test Latest'}
            </button>
          </div>
        </section>

        <aside className="card playground-v2-checklist">
          <div className="playground-v2-panel-head">
            <span className="icon-chip"><Icon name="shield" size={19}/></span>
            <div>
              <h2>Test Checklist</h2>
              <span>{selectedTemplate?.botId || 'No template selected'}</span>
            </div>
          </div>

          <div className="playground-v2-summary-grid">
            <PlaygroundSummaryItem label="Model" value={activeSettings?.model || 'Not selected'} />
            <PlaygroundSummaryItem label="Bot" value={activeSettings?.botName || 'Assistant'} />
            <PlaygroundSummaryItem label="Domains" value={activeDomains.length ? String(activeDomains.length) : 'None'} />
            <PlaygroundSummaryItem label="Position" value={getEmbeddedPositionLabel(activeSettings?.position)} />
          </div>

          <div className="playground-v2-tool-list">
            <PlaygroundToolState label="Opening Message" active={Boolean(activeSettings?.openingMessage)} />
            <PlaygroundToolState label="Conversation Starters" active={getEmbeddedStarterList(activeSettings?.starters).length > 0} />
            <PlaygroundToolState label="Visitor Capture" active={Boolean(activeSettings?.visitorCaptureEnabled)} />
            <PlaygroundToolState label="Capture Mode: Form" active={activeSettings?.visitorCaptureEnabled && activeSettings?.visitorCaptureMode === 'form'} />
            <PlaygroundToolState label="Voice Replies" active={Boolean(activeSettings?.enableTts)} />
            <PlaygroundToolState label="File Search" active={Boolean(activeSettings?.enableFileSearch)} />
            <PlaygroundToolState label="Code Interpreter" active={Boolean(activeSettings?.enableCodeInterpreter)} />
          </div>

          <div className="playground-v2-help-box">
            <strong>How to test</strong>
            <span>Save changes, then chat in the widget. For popup capture, open/reset the widget and submit the visitor form. For human handoff, click Talk to a human and submit the consent form.</span>
          </div>

          <div className="playground-v2-help-box">
            <strong>Saved domains</strong>
            <span>{activeDomains.length ? activeDomains.join(', ') : selectedDomains.join(', ') || 'No domains saved'}</span>
          </div>
        </aside>
      </div>
    </div>
  );
}

function PlaygroundWidgetPreview({ template, fallbackTemplate, settings, runKey }) {
  const activeTemplate = template || fallbackTemplate;
  const activeSettings = normalizePlaygroundSettings({ ...(activeTemplate || {}), settings });
  const botId = activeTemplate?.botId || activeSettings.editingBotId || '';
  const starters = useMemoPlayground(() => getEmbeddedStarterList(activeSettings.starters), [activeSettings.starters]);
  const accent = sanitizePlaygroundColor(activeSettings.color);
  const bodyRef = useRefPlayground(null);
  const [messages, setMessages] = useStatePlayground([]);
  const [draft, setDraft] = useStatePlayground('');
  const [sending, setSending] = useStatePlayground(false);
  const [widgetError, setWidgetError] = useStatePlayground('');
  const [visitorId, setVisitorId] = useStatePlayground(() => createPlaygroundId('visitor'));
  const [conversationId, setConversationId] = useStatePlayground(() => createPlaygroundId('conversation'));
  const [captureState, setCaptureState] = useStatePlayground({ step: 'none' });
  const [visitorCaptured, setVisitorCaptured] = useStatePlayground(false);
  const [captureModalOpen, setCaptureModalOpen] = useStatePlayground(false);
  const [handoffOpen, setHandoffOpen] = useStatePlayground(false);
  const [humanMode, setHumanMode] = useStatePlayground(false);
  const [publicConfig, setPublicConfig] = useStatePlayground(null);
  const [captureForm, setCaptureForm] = useStatePlayground({ name: '', email: '', phone: '' });
  const [handoffForm, setHandoffForm] = useStatePlayground({ name: '', email: '', phone: '', consent: false });
  const [formError, setFormError] = useStatePlayground('');
  const [toast, setToast] = useStatePlayground('');
  const [widgetTheme, setWidgetTheme] = useStatePlayground('dark');

  const resetWidget = () => {
    const openingMessage = activeSettings.openingMessage || 'Hello! How can I help you today?';
    const nextVisitorId = createPlaygroundId('visitor');
    const nextConversationId = createPlaygroundId(`conversation_${botId || 'preview'}`);

    setMessages([{ id: createPlaygroundId('message'), role: 'assistant', content: openingMessage, meta: 'Opening message' }]);
    setDraft('');
    setSending(false);
    setWidgetError('');
    setVisitorId(nextVisitorId);
    setConversationId(nextConversationId);
    setCaptureState({ step: 'none' });
    setVisitorCaptured(false);
    setCaptureForm({ name: '', email: '', phone: '' });
    setHandoffForm({ name: '', email: '', phone: '', consent: false });
    setHumanMode(false);
    setHandoffOpen(false);
    setFormError('');
    setCaptureModalOpen(Boolean(activeSettings.visitorCaptureEnabled && activeSettings.visitorCaptureMode === 'form'));
  };

  useEffectPlayground(() => {
    resetWidget();
  }, [botId, runKey]);

  useEffectPlayground(() => {
    if (!botId) {
      setPublicConfig(null);
      return;
    }

    let cancelled = false;
    fetchPlaygroundJson(`/api/embed/${encodeURIComponent(botId)}/config`, { credentials: 'same-origin' })
      .then(data => {
        if (!cancelled) setPublicConfig(data.bot || null);
      })
      .catch(() => {
        if (!cancelled) setPublicConfig(null);
      });

    return () => {
      cancelled = true;
    };
  }, [botId, runKey]);

  useEffectPlayground(() => {
    if (bodyRef.current) {
      bodyRef.current.scrollTop = bodyRef.current.scrollHeight;
    }
  }, [messages, sending]);

  const addToast = message => {
    setToast(message);
    window.setTimeout(() => setToast(''), 2200);
  };

  const appendMessage = message => {
    setMessages(current => [...current, {
      id: createPlaygroundId('message'),
      ...message,
    }]);
  };

  const sendMessage = async value => {
    const text = String(value ?? draft).trim();

    if (!botId) {
      setWidgetError('Save or load a template before testing the widget.');
      return;
    }

    if (!text || sending) return;

    const userMessageId = createPlaygroundId('message');
    setMessages(current => [...current, { id: userMessageId, role: 'user', content: text, meta: 'You' }]);
    setDraft('');
    setSending(true);
    setWidgetError('');

    try {
      const data = await fetchPlaygroundJson(`/api/embedded-bots/${encodeURIComponent(botId)}/playground-chat`, {
        method: 'POST',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify({
          message: text,
          visitorId,
          conversationId,
          clientMessageId: userMessageId,
          sourceUrl: window.location.href,
          captureState,
          playgroundVisitorCaptured: visitorCaptured,
        }),
      });

      if (data.captureState) {
        setCaptureState(data.captureState);
      } else if (data.captureComplete) {
        setCaptureState({ step: 'done' });
        setVisitorCaptured(true);
      }

      if (data.requireCapture && data.captureMode === 'form' && !visitorCaptured) {
        setCaptureModalOpen(true);
      }

      if (data.humanMode) {
        setHumanMode(true);
      }

      appendMessage({
        role: 'assistant',
        content: data.humanMode
          ? 'This test conversation is currently in human support mode.'
          : data.answer || 'No answer returned.',
        meta: data.requireCapture ? 'Visitor capture' : data.suggestHandoff ? 'Human support' : 'Assistant',
        audioUrl: data.audioUrl || '',
        sources: Array.isArray(data.sources) ? data.sources : [],
        tone: data.liveChatUnavailable ? 'warning' : '',
      });
    } catch (sendError) {
      const message = sendError.message || 'Unable to send message.';
      setWidgetError(message);
      appendMessage({ role: 'assistant', content: message, meta: 'Error', tone: 'error' });
    } finally {
      setSending(false);
    }
  };

  const submitCaptureForm = () => {
    const normalized = normalizePlaygroundLead(captureForm);
    if (!normalized.ok) {
      setFormError(normalized.error);
      return;
    }

    setVisitorCaptured(true);
    setCaptureModalOpen(false);
    setFormError('');
    appendMessage({
      role: 'assistant',
      content: `Thanks ${normalized.lead.name}. Your visitor details are captured for this playground test.`,
      meta: 'Visitor capture',
    });
  };

  const submitHandoff = async () => {
    const normalized = normalizePlaygroundLead(handoffForm);
    if (!normalized.ok) {
      setFormError(normalized.error);
      return;
    }

    if (!handoffForm.consent) {
      setFormError('Please confirm consent before requesting human support.');
      return;
    }

    if (!botId) {
      setFormError('Save or load a template before testing handoff.');
      return;
    }

    setFormError('');
    setSending(true);

    try {
      const data = await fetchPlaygroundJson(`/api/embedded-bots/${encodeURIComponent(botId)}/playground-handoff`, {
        method: 'POST',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify({
          ...normalized.lead,
          consent: true,
          visitorId,
          conversationId,
          sourceUrl: window.location.href,
        }),
      });

      setVisitorCaptured(true);
      setHumanMode(Boolean(data.handoff));
      setHandoffOpen(false);
      setHandoffForm({ ...handoffForm, ...normalized.lead, consent: true });
      appendMessage({
        role: 'assistant',
        content: data.answer || 'Human support test completed.',
        meta: data.handoff ? 'Human support' : 'Support status',
        tone: data.unavailable ? 'warning' : '',
      });
    } catch (handoffError) {
      setFormError(handoffError.message || 'Unable to test handoff.');
    } finally {
      setSending(false);
    }
  };

  const exportTranscript = () => {
    const text = messages.map(message => `${message.meta || message.role}: ${message.content}`).join('\n\n');
    const blob = new Blob([text], { type: 'text/plain;charset=utf-8' });
    const url = URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.href = url;
    link.download = `${botId || 'playground'}-transcript.txt`;
    link.click();
    URL.revokeObjectURL(url);
    addToast('Transcript exported.');
  };

  const liveChat = publicConfig?.liveChat || {};
  const supportLabel = liveChat.buttonLabel || 'Talk to a human';
  const accentText = getPlaygroundAccentTextColor(accent);

  if (!activeTemplate) {
    return (
      <div className="playground-v2-widget-stage is-empty">
        <Icon name="messagesSquare" size={30}/>
        <strong>Select a template to start testing.</strong>
        <span>The real-style widget will appear here after loading a saved template.</span>
      </div>
    );
  }

  return (
    <div
      className="playground-v2-widget-stage"
      style={{
        '--playground-widget-accent': accent,
        '--playground-widget-accent-text': accentText,
      }}
    >
      <div className={`playground-test-widget is-${widgetTheme}-theme`}>
        <div className="playground-test-head">
          <div>
            {activeSettings.avatarUrl
              ? <img className="playground-test-avatar" src={activeSettings.avatarUrl} alt=""/>
              : <span className="playground-test-orb" />}
            <div>
              <strong>{activeSettings.botName || 'PluginChatBot Assistant'}</strong>
              <small>{humanMode ? 'Human support mode' : 'Online · Playground test'}</small>
            </div>
          </div>
          <div className="playground-test-head-actions">
            <button
              type="button"
              onClick={() => setWidgetTheme(current => current === 'dark' ? 'light' : 'dark')}
            >
              {widgetTheme === 'dark' ? 'Light' : 'Dark'}
            </button>
            <button type="button" onClick={resetWidget}>Reset</button>
          </div>
        </div>

        {widgetError && <div className="playground-test-error">{widgetError}</div>}

        <div className="playground-test-body" ref={bodyRef}>
          {messages.map(message => (
            <div key={message.id} className={`playground-test-message is-${message.role} ${message.tone ? `is-${message.tone}` : ''}`}>
              <span>{message.meta}</span>
              <div>{message.content}</div>
              {Array.isArray(message.sources) && message.sources.length > 0 && (
                <div className="playground-test-sources">
                  <span className="playground-test-sources-label">{message.sources.length > 1 ? 'Sources' : 'Source'}</span>
                  {message.sources.map((source, index) => (
                    <span key={index} className="playground-test-source-chip" title={source}>
                      {String(source).replace(/\.[a-z0-9]{1,5}$/i, '').replace(/^website-/i, '')}
                    </span>
                  ))}
                </div>
              )}
              {message.audioUrl && <audio controls src={message.audioUrl}/>}
            </div>
          ))}
          {sending && (
            <div className="playground-test-message is-assistant is-thinking">
              <span>Assistant</span>
              <div>Typing...</div>
            </div>
          )}
        </div>

        {starters.length > 0 && (
          <div className="playground-test-starters">
            {starters.map(starter => (
              <button key={starter} type="button" onClick={() => sendMessage(starter)} disabled={sending}>{starter}</button>
            ))}
          </div>
        )}

        <div className="playground-test-support-bar">
          <button type="button" onClick={() => setHandoffOpen(true)}>{supportLabel}</button>
          {activeSettings.visitorCaptureEnabled && activeSettings.visitorCaptureMode === 'form' && (
            <button type="button" onClick={() => setCaptureModalOpen(true)}>Visitor form</button>
          )}
          {liveChat.whatsapp?.enabled && <button type="button" onClick={() => addToast('WhatsApp handoff can be opened from the live widget after installation.')}>WhatsApp</button>}
          {liveChat.messenger?.enabled && <button type="button" onClick={() => window.open(liveChat.messenger.externalUrl, '_blank', 'noopener')}>Messenger</button>}
          {liveChat.instagram?.enabled && <button type="button" onClick={() => window.open(liveChat.instagram.externalUrl, '_blank', 'noopener')}>Instagram</button>}
        </div>

        <form className="playground-test-form" onSubmit={event => { event.preventDefault(); sendMessage(); }}>
          <input
            type="text"
            value={draft}
            placeholder={humanMode ? 'Message human support...' : 'Type your message...'}
            onChange={event => setDraft(event.target.value)}
            disabled={sending}
          />
          <button type="submit" disabled={sending || !draft.trim()} aria-label="Send message">
            <Icon name="send" size={16}/>
          </button>
        </form>

        <div className="playground-test-tools">
          <button type="button" onClick={exportTranscript}>Export</button>
          <button type="button" onClick={resetWidget}>End chat</button>
        </div>

        {captureModalOpen && (
          <PlaygroundModal title="Visitor Details" onClose={() => setCaptureModalOpen(false)}>
            <p>{activeSettings.visitorCapturePrompt || 'Please share your details so we can help you better.'}</p>
            <PlaygroundModalInput placeholder="Your name" value={captureForm.name} onChange={value => setCaptureForm(current => ({ ...current, name: value }))} />
            <PlaygroundModalInput placeholder="Email address" value={captureForm.email} onChange={value => setCaptureForm(current => ({ ...current, email: value }))} />
            <PlaygroundModalInput placeholder="Phone number" value={captureForm.phone} onChange={value => setCaptureForm(current => ({ ...current, phone: value }))} />
            {formError && <div className="playground-modal-error">{formError}</div>}
            <button type="button" className="playground-modal-submit" onClick={submitCaptureForm}>Submit</button>
          </PlaygroundModal>
        )}

        {handoffOpen && (
          <PlaygroundModal title="Talk to a human" onClose={() => setHandoffOpen(false)}>
            <p>Share your details so the support team can continue this conversation.</p>
            <PlaygroundModalInput placeholder="Your name" value={handoffForm.name} onChange={value => setHandoffForm(current => ({ ...current, name: value }))} />
            <PlaygroundModalInput placeholder="Email address" value={handoffForm.email} onChange={value => setHandoffForm(current => ({ ...current, email: value }))} />
            <PlaygroundModalInput placeholder="Phone number" value={handoffForm.phone} onChange={value => setHandoffForm(current => ({ ...current, phone: value }))} />
            <label className="playground-modal-check">
              <input type="checkbox" checked={handoffForm.consent} onChange={event => setHandoffForm(current => ({ ...current, consent: event.target.checked }))} />
              <span>I agree that this business may contact me about this conversation.</span>
            </label>
            {formError && <div className="playground-modal-error">{formError}</div>}
            <button type="button" className="playground-modal-submit" onClick={submitHandoff} disabled={sending}>{sending ? 'Sending...' : 'Request human support'}</button>
          </PlaygroundModal>
        )}

        {toast && <div className="playground-test-toast">{toast}</div>}
      </div>
    </div>
  );
}

function PlaygroundEditorSection({ title, icon, children }) {
  return (
    <section className="playground-v2-editor-section">
      <div className="playground-v2-editor-section-head">
        <span><Icon name={icon} size={14}/>{title}</span>
      </div>
      <div className="playground-v2-editor-section-body">{children}</div>
    </section>
  );
}

function PlaygroundTextInput({ label, value, onChange, type = 'text', placeholder = '' }) {
  return (
    <label className="playground-v2-field">
      <span>{label}</span>
      <input className="embedded-input" type={type} value={value || ''} placeholder={placeholder} onChange={event => onChange(event.target.value)} />
    </label>
  );
}

function PlaygroundNumberInput({ label, value, min, max, onChange }) {
  return (
    <label className="playground-v2-field">
      <span>{label}</span>
      <input className="embedded-input" type="number" min={min} max={max} value={value || ''} onChange={event => onChange(event.target.value)} />
    </label>
  );
}

function PlaygroundTextArea({ label, value, onChange, rows = '3', help = '', disabled = false }) {
  return (
    <label className="playground-v2-field">
      <span>{label}</span>
      <textarea className="embedded-input" rows={rows} value={value || ''} disabled={disabled} onChange={event => onChange(event.target.value)} />
      {help && <small>{help}</small>}
    </label>
  );
}

function PlaygroundSelect({ label, value, onChange, children, disabled = false }) {
  return (
    <label className="playground-v2-field">
      <span>{label}</span>
      <select className="embedded-input" value={value || ''} disabled={disabled} onChange={event => onChange(event.target.value)}>
        {children}
      </select>
    </label>
  );
}

function PlaygroundRange({ label, value, min, max, step, onChange, disabled = false }) {
  return (
    <label className="playground-v2-field">
      <span>{label}</span>
      <div className={`playground-v2-range ${disabled ? 'is-disabled' : ''}`}>
        <input type="range" min={min} max={max} step={step} value={value || '0.7'} disabled={disabled} onChange={event => onChange(event.target.value)} />
        <strong>{disabled ? 'Auto' : value}</strong>
      </div>
    </label>
  );
}

function PlaygroundToggle({ label, checked, onChange }) {
  return (
    <label className="playground-v2-toggle">
      <span>{label}</span>
      <input type="checkbox" checked={checked} onChange={event => onChange(event.target.checked)} />
    </label>
  );
}

function PlaygroundColorInput({ label, value, onChange }) {
  const safeColor = sanitizePlaygroundColor(value);

  return (
    <label className="playground-v2-field">
      <span>{label}</span>
      <div className="playground-v2-color-row">
        <input type="color" value={safeColor} onChange={event => onChange(event.target.value)} />
        <input className="embedded-input" value={value || safeColor} maxLength="7" onChange={event => onChange(event.target.value)} />
      </div>
    </label>
  );
}

function PlaygroundSummaryItem({ label, value }) {
  return (
    <div>
      <span>{label}</span>
      <strong>{value}</strong>
    </div>
  );
}

function PlaygroundToolState({ label, active }) {
  return (
    <div className={`playground-v2-tool-state ${active ? 'is-active' : ''}`}>
      <Icon name={active ? 'checkCircle' : 'x'} size={15}/>
      <span>{label}</span>
    </div>
  );
}

function PlaygroundModal({ title, children, onClose }) {
  return (
    <div className="playground-modal-backdrop" role="dialog" aria-modal="true" aria-label={title}>
      <div className="playground-modal-card">
        <button type="button" className="playground-modal-close" onClick={onClose} aria-label="Close">×</button>
        <strong>{title}</strong>
        {children}
      </div>
    </div>
  );
}

function PlaygroundModalInput({ value, onChange, placeholder }) {
  return <input className="playground-modal-input" type="text" value={value || ''} placeholder={placeholder} onChange={event => onChange(event.target.value)} />;
}

async function fetchPlaygroundJson(url, options = {}) {
  const response = await fetch(url, {
    credentials: 'include',
    cache: 'no-store',
    ...options,
  });

  if (response.status === 401) {
    window.location.href = (['localhost','127.0.0.1'].includes(location.hostname)?'http://localhost:8788':'https://pluginchatbot.com')+'/login.html?redirect=' + encodeURIComponent(window.location.href);
    throw new Error('Authentication required.');
  }

  const contentType = response.headers.get('content-type') || '';
  if (!contentType.toLowerCase().includes('application/json')) {
    const text = await response.text().catch(() => '');
    const clean = text.replace(/\s+/g, ' ').slice(0, 90);
    throw new Error(clean ? `Unexpected response from server: ${clean}` : 'Unexpected response from server.');
  }

  const data = await response.json();
  if (!response.ok || data.ok === false) {
    throw new Error(data.error || data.details || 'Request failed.');
  }

  return data;
}

function normalizePlaygroundSettings(template) {
  const defaults = typeof DEFAULT_EMBEDDED_BUILDER_SETTINGS === 'object' ? DEFAULT_EMBEDDED_BUILDER_SETTINGS : {};
  const settings = template?.settings || template || {};

  return {
    ...defaults,
    ...settings,
    templateName: settings.templateName || template?.templateName || template?.botName || defaults.templateName || 'Website chatbot',
    editingBotId: settings.editingBotId || template?.botId || '',
    avatarUrl: settings.avatarUrl || template?.avatarUrl || '',
    color: sanitizePlaygroundColor(settings.color || defaults.color || '#D92F24'),
    colorMode: 'solid',
    gradientColor: sanitizePlaygroundColor(settings.color || defaults.color || '#D92F24'),
    chatBackgroundColor: '#000000',
    maxReplyTokens: String(settings.maxReplyTokens || defaults.maxReplyTokens || '512'),
    rateLimit: String(settings.rateLimit || defaults.rateLimit || '20'),
  };
}

function buildPlaygroundTemplate(template, settings, response = {}) {
  const normalized = normalizePlaygroundSettings({ ...(template || {}), settings });
  const botId = response.botId || template?.botId || normalized.editingBotId;

  return {
    ...(template || {}),
    botId,
    templateName: normalized.templateName,
    botName: normalized.botName,
    avatarUrl: normalized.avatarUrl || '',
    embedCode: response.embedCode || template?.embedCode || '',
    settings: {
      ...normalized,
      editingBotId: botId,
    },
  };
}

function validatePlaygroundSettings(settings) {
  if (!String(settings.templateName || '').trim()) return 'Template name is required.';
  if (!String(settings.botName || '').trim()) return 'Bot name is required.';
  if (!String(settings.model || '').trim()) return 'Select a GPT model before saving.';
  if (!String(settings.allowedDomains || '').trim()) return 'Add at least one allowed domain before saving.';
  return '';
}

function parsePlaygroundDomains(value) {
  return String(value || '')
    .split(',')
    .map(item => item.trim())
    .filter(Boolean);
}

function sanitizePlaygroundColor(value) {
  return /^#[0-9a-f]{6}$/i.test(String(value || '').trim()) ? String(value).trim() : '#D92F24';
}

function normalizePlaygroundLead(value) {
  const name = String(value.name || '').trim();
  const email = String(value.email || '').trim().toLowerCase();
  const phone = String(value.phone || '').trim();

  if (name.length < 2) return { ok: false, error: 'Please enter your name.' };
  if (!/^\S+@\S+\.\S+$/.test(email)) return { ok: false, error: 'Please enter a valid email address.' };
  if (phone.replace(/\D/g, '').length < 7) return { ok: false, error: 'Please enter a valid phone number.' };

  return {
    ok: true,
    lead: { name, email, phone },
  };
}

function getPlaygroundAccentTextColor(value) {
  const color = sanitizePlaygroundColor(value).slice(1);
  const red = Number.parseInt(color.slice(0, 2), 16);
  const green = Number.parseInt(color.slice(2, 4), 16);
  const blue = Number.parseInt(color.slice(4, 6), 16);
  const luminance = (0.299 * red) + (0.587 * green) + (0.114 * blue);

  return luminance > 150 ? '#111111' : '#FFFFFF';
}

function createPlaygroundId(prefix) {
  return `${prefix}_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
}

window.PlaygroundPage = PlaygroundPage;
