/* global React, Icon, Training, AppFieldLabel, PlugStyleWidgetPreview, WidgetSettingsPanel, BuilderFormRow, SettingsCard, CustomizeFormRow, CustomizeToggleRow, SettingsSummary, PreviewInstallPanel, InstallFormRow, getStoredWorkspaceId, useSelectedWorkspaceId, getEmbeddedBuilderDraft, saveEmbeddedBuilderDraft, getEmbeddedStarterList, getEmbeddedPositionLabel, getEmbeddedTemplateName, EMBEDDED_MODEL_OPTIONS, EMBEDDED_VOICE_OPTIONS, embeddedModelSupportsTemperature, capitalizeEmbedded, uniqueModels, parseAllowedDomains, validateAllowedDomain */
const {
  useEffect: useEffectAIBuilder,
  useMemo: useMemoAIBuilder,
  useState: useStateAIBuilder,
} = React;

// Resize a picked image file to a small square avatar and return a compact
// base64 data: URI (webp when supported, else jpeg). Keeps the stored row tiny.
function resizeImageToAvatarDataUrl(file, size = 128) {
  return new Promise((resolve, reject) => {
    if (!file || !/^image\//.test(file.type)) {
      reject(new Error('Please choose an image file.'));
      return;
    }
    const reader = new FileReader();
    reader.onerror = () => reject(new Error('Could not read the image.'));
    reader.onload = () => {
      const img = new Image();
      img.onerror = () => reject(new Error('That image could not be loaded.'));
      img.onload = () => {
        const canvas = document.createElement('canvas');
        canvas.width = size;
        canvas.height = size;
        const ctx = canvas.getContext('2d');
        // Cover-fit: crop to a centered square, then scale into the canvas.
        const side = Math.min(img.width, img.height);
        const sx = (img.width - side) / 2;
        const sy = (img.height - side) / 2;
        ctx.drawImage(img, sx, sy, side, side, 0, 0, size, size);
        let dataUrl = '';
        try {
          dataUrl = canvas.toDataURL('image/webp', 0.85);
          if (!dataUrl.startsWith('data:image/webp')) {
            dataUrl = canvas.toDataURL('image/jpeg', 0.85);
          }
        } catch (error) {
          dataUrl = canvas.toDataURL('image/jpeg', 0.85);
        }
        resolve(dataUrl);
      };
      img.src = reader.result;
    };
    reader.readAsDataURL(file);
  });
}

function AIBuilderPage() {
  const workspaceId = useSelectedWorkspaceId();
  const [settings, setSettings] = useStateAIBuilder(() => getEmbeddedBuilderDraft());
  const [avatarError, setAvatarError] = useStateAIBuilder('');
  const [savedDraft, setSavedDraft] = useStateAIBuilder(false);
  const [copied, setCopied] = useStateAIBuilder(false);
  const [generated, setGenerated] = useStateAIBuilder(false);
  const [isGenerating, setIsGenerating] = useStateAIBuilder(false);
  const [apiError, setApiError] = useStateAIBuilder('');
  const [generatedBotId, setGeneratedBotId] = useStateAIBuilder(() => getEmbeddedBuilderDraft().editingBotId || '');
  const [generatedEmbedCode, setGeneratedEmbedCode] = useStateAIBuilder('');
  const [domainInput, setDomainInput] = useStateAIBuilder('');
  const [domainError, setDomainError] = useStateAIBuilder('');
  const [editingDomain, setEditingDomain] = useStateAIBuilder('');
  const [modelOptions, setModelOptions] = useStateAIBuilder(() => uniqueModels([
    getEmbeddedBuilderDraft().model,
    ...EMBEDDED_MODEL_OPTIONS,
  ]));
  const [modelsLoading, setModelsLoading] = useStateAIBuilder(false);
  const [modelError, setModelError] = useStateAIBuilder('');
  const [openSections, setOpenSections] = useStateAIBuilder({
    widget: true,
    customize: true,
    knowledgeBase: true,
    install: true,
  });

  useEffectAIBuilder(() => {
    const nextSettings = getEmbeddedBuilderDraft();
    setSettings(nextSettings);
    setSavedDraft(false);
    setCopied(false);
    setGenerated(false);
    setGeneratedBotId('');
    setGeneratedEmbedCode('');
    setApiError('');
    setDomainInput('');
    setDomainError('');
    setEditingDomain('');
    setModelOptions(uniqueModels([nextSettings.model, ...EMBEDDED_MODEL_OPTIONS]));
    setModelError('');
  }, [workspaceId]);

  const starterList = useMemoAIBuilder(() => getEmbeddedStarterList(settings.starters), [settings.starters]);
  const positionLabel = getEmbeddedPositionLabel(settings.position);
  const isEditing = Boolean(settings.editingBotId);

  const [toast, setToast] = useStateAIBuilder(null);
  useEffectAIBuilder(() => {
    if (!toast) return undefined;
    const timer = window.setTimeout(() => setToast(null), 4200);
    return () => window.clearTimeout(timer);
  }, [toast]);
  const supportsTemperature = embeddedModelSupportsTemperature(settings.model);

  const allowedDomainList = useMemoAIBuilder(() => {
    return parseAllowedDomains(settings.allowedDomains);
  }, [settings.allowedDomains]);

  const resolvedEmbedBotId = generatedBotId || settings.editingBotId || 'pcbot_generated_after_backend';
  const productionEmbedOrigin = getAIBuilderProductionEmbedOrigin();

  const productionEmbedCode = useMemoAIBuilder(() => {
    return buildAIBuilderEmbedCode({
      scriptOrigin: productionEmbedOrigin,
      apiOrigin: productionEmbedOrigin,
      botId: resolvedEmbedBotId,
    });
  }, [productionEmbedOrigin, resolvedEmbedBotId]);

  const embedCode = productionEmbedCode || generatedEmbedCode;

  const toggleSection = key => {
    setOpenSections(current => ({ ...current, [key]: !current[key] }));
  };

  const updateSetting = (key, value) => {
    setSavedDraft(false);
    setCopied(false);
    setGenerated(false);
    setApiError('');
    setSettings(previous => ({ ...previous, [key]: value }));
  };

  const saveDraft = () => {
    if (!String(settings.model || '').trim()) {
      setSavedDraft(false);
      setModelError('Error, Select Model.');
      setOpenSections(current => ({ ...current, customize: true }));
      return false;
    }

    const normalizedSettings = {
      ...settings,
      colorMode: 'solid',
      gradientColor: settings.color,
      chatBackgroundColor: '#000000',
    };

    setSettings(normalizedSettings);
    setModelError('');
    saveEmbeddedBuilderDraft(normalizedSettings);
    setSavedDraft(true);
    return true;
  };

  const setAllowedDomainList = domains => {
    updateSetting('allowedDomains', domains.join(', '));
  };

  const addAllowedDomain = () => {
    const validation = validateAllowedDomain(domainInput);

    if (!validation.ok) {
      setDomainError(validation.error);
      return;
    }

    if (allowedDomainList.includes(validation.domain)) {
      setDomainError('This domain is already added.');
      return;
    }

    setAllowedDomainList([...allowedDomainList, validation.domain]);
    setDomainInput('');
    setDomainError('');
  };

  const editAllowedDomain = domain => {
    setEditingDomain(domain);
    setDomainInput(domain);
    setDomainError('');
  };

  const deleteAllowedDomain = domain => {
    setAllowedDomainList(allowedDomainList.filter(item => item !== domain));

    if (editingDomain === domain) {
      setEditingDomain('');
      setDomainInput('');
      setDomainError('');
    }
  };

  const saveEditedDomain = () => {
    const validation = validateAllowedDomain(domainInput);

    if (!validation.ok) {
      setDomainError(validation.error);
      return;
    }

    if (allowedDomainList.some(domain => domain !== editingDomain && domain === validation.domain)) {
      setDomainError('This domain is already added.');
      return;
    }

    setAllowedDomainList(allowedDomainList.map(domain => (
      domain === editingDomain ? validation.domain : domain
    )));
    setEditingDomain('');
    setDomainInput('');
    setDomainError('');
  };

  const cancelEditingDomain = () => {
    setEditingDomain('');
    setDomainInput('');
    setDomainError('');
  };

  const loadAvailableModels = async () => {
    if (!settings.editingBotId) {
      setModelError('Save this website first. PluginChatBot will provision its private AI key before live models can be refreshed.');
      return;
    }

    setModelsLoading(true);
    setModelError('');

    try {
      const response = await fetch('/api/openai/models', {
        method: 'POST',
        credentials: 'include',
        cache: 'no-store',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify({ botId: settings.editingBotId }),
      });

      const data = await response.json().catch(() => ({}));

      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);
        return;
      }

      if (!response.ok || !data.ok) {
        throw new Error(data.error || 'Unable to load OpenAI models.');
      }

      const nextModels = uniqueModels([settings.model, ...(Array.isArray(data.models) ? data.models : [])]);
      setModelOptions(nextModels);

      if (!settings.model && nextModels.length) {
        updateSetting('model', nextModels[0]);
      }
    } catch (error) {
      setModelError(error.message || 'Unable to load OpenAI models.');
    } finally {
      setModelsLoading(false);
    }
  };

  const generateEmbedCode = async () => {
    setCopied(false);
    setGenerated(false);
    setGeneratedBotId('');
    setGeneratedEmbedCode('');
    setApiError('');

    const templateName = getEmbeddedTemplateName(settings);
    let validationError = '';
    if (!templateName.trim()) {
      validationError = 'Please enter a template name before saving this template.';
    } else if (!String(settings.model || '').trim()) {
      validationError = 'Please select a GPT model before saving this template.';
    } else if (!String(settings.allowedDomains || '').trim()) {
      validationError = 'Please add at least one allowed domain before saving this template.';
    }

    if (validationError) {
      setApiError(validationError);
      setOpenSections(current => ({ ...current, customize: true, install: true }));
      setToast({ type: 'error', message: validationError });
      return;
    }

    setIsGenerating(true);

    try {
      const payload = {
        ...settings,
        colorMode: 'solid',
        gradientColor: settings.color,
        chatBackgroundColor: '#000000',
        workspaceId: getStoredWorkspaceId(),
        templateName,
      };

      const endpoint = isEditing
        ? `/api/embedded-bots/${encodeURIComponent(settings.editingBotId)}`
        : '/api/embedded-bots';

      const response = await fetch(endpoint, {
        method: isEditing ? 'PATCH' : 'POST',
        credentials: 'include',
        headers: {
          'content-type': 'application/json',
        },
        body: JSON.stringify(payload),
      });

      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);
        return;
      }

      const data = await response.json();

      if (!response.ok || !data.ok) {
        throw new Error(data.error || data.details || 'Unable to save this template.');
      }

      const nextSettings = {
        ...settings,
        colorMode: 'solid',
        gradientColor: settings.color,
        chatBackgroundColor: '#000000',
        templateName,
        editingBotId: data.botId || settings.editingBotId || '',
      };

      setSettings(nextSettings);
      setGenerated(true);
      setSavedDraft(true);
      setGeneratedBotId(data.botId || settings.editingBotId || '');
      setGeneratedEmbedCode(data.embedCode || '');
      saveEmbeddedBuilderDraft(nextSettings);
      window.dispatchEvent(new CustomEvent('pluginchatbot:bot-status-changed', {
        detail: {
          workspaceId: getStoredWorkspaceId(),
          botId: data.botId || settings.editingBotId || '',
        },
      }));
      setToast({ type: 'success', message: isEditing ? 'Template updated and saved successfully.' : 'Template saved successfully.' });
    } catch (error) {
      const message = error.message || 'Unable to save this template.';
      setApiError(message);
      setToast({ type: 'error', message });
    } finally {
      setIsGenerating(false);
    }
  };

  const copyEmbedCode = async () => {
    setCopied(false);

    try {
      if (navigator.clipboard && navigator.clipboard.writeText) {
        await navigator.clipboard.writeText(embedCode);
      } else {
        const textarea = document.createElement('textarea');
        textarea.value = embedCode;
        textarea.setAttribute('readonly', '');
        textarea.style.position = 'fixed';
        textarea.style.opacity = '0';
        document.body.appendChild(textarea);
        textarea.select();
        document.execCommand('copy');
        textarea.remove();
      }

      setCopied(true);
    } catch (error) {
      setCopied(false);
      setApiError('Unable to copy the embed code. Please copy it manually.');
    }
  };

  return (
    <div className="view ai-builder-view">
      {toast && (
        <div
          role="status"
          aria-live="polite"
          style={{
            position: 'fixed', top: '20px', right: '20px', zIndex: 9999,
            display: 'flex', alignItems: 'flex-start', gap: '10px',
            maxWidth: '380px', padding: '13px 15px', borderRadius: '12px',
            background: '#ffffff',
            border: `1px solid ${toast.type === 'success' ? '#a6f4c5' : '#fecdca'}`,
            borderLeft: `4px solid ${toast.type === 'success' ? '#067647' : '#d92d20'}`,
            boxShadow: '0 14px 34px rgba(16, 24, 40, .16)',
            font: '500 14px/1.45 inherit', color: '#101828',
          }}
        >
          <span style={{ flex: '0 0 auto', marginTop: '1px', color: toast.type === 'success' ? '#067647' : '#d92d20' }}>
            <Icon name={toast.type === 'success' ? 'checkCircle' : 'alertTriangle'} size={18} />
          </span>
          <span style={{ flex: '1 1 auto' }}>{toast.message}</span>
          <button
            type="button"
            onClick={() => setToast(null)}
            aria-label="Dismiss notification"
            style={{ flex: '0 0 auto', background: 'transparent', border: 0, color: '#667085', cursor: 'pointer', fontSize: '18px', lineHeight: 1, padding: 0 }}
          >
            &times;
          </button>
        </div>
      )}
      <div className="view-head">
        <div>
          <h1>AI Builder</h1>
          <div className="sub">Build, customize, save, and install one editable chatbot template</div>
        </div>
        <div className="view-head-actions">
          <button className="btn btn-secondary" type="button" onClick={saveDraft}>
            <Icon name="checkCircle" size={14}/>Save Draft
          </button>
          <button className="btn btn-primary" type="button" onClick={generateEmbedCode} disabled={isGenerating}>
            {isGenerating ? 'Saving...' : isEditing ? 'Update Template' : 'Save Template'}
          </button>
        </div>
      </div>

      {savedDraft && (
        <div className="app-save-notice">
          Builder saved. Saved templates can be reopened from Templates and edited here later.
        </div>
      )}

      <div className="ai-builder-section-stack">
        <AIBuilderSection
          id="widget"
          title="Widget"
          icon="panelTop"
          description="Appearance, placement, and conversation content"
          isOpen={openSections.widget}
          onToggle={() => toggleSection('widget')}
        >
          <div className="widget-editor-grid app-builder-grid">
            <div className="app-builder-stack">
              <WidgetSettingsPanel title="Widget Appearance" icon="palette" description="Match the visible chat experience to your website brand.">
                <BuilderFormRow label="Brand Color" help="This colour is used for the launcher, status, send button, and widget highlights.">
                  <div className="embedded-color-row">
                    <input
                      className="embedded-color"
                      type="color"
                      value={isAIBuilderHexColor(settings.color) ? settings.color : '#D92F24'}
                      onChange={event => updateSetting('color', event.target.value)}
                      aria-label="Choose widget brand color"
                    />
                    <input
                      className="embedded-input"
                      value={settings.color}
                      maxLength="7"
                      onChange={event => updateSetting('color', event.target.value)}
                      aria-label="Widget brand color hex value"
                    />
                  </div>
                </BuilderFormRow>

                <BuilderFormRow label="Widget Position" help="Choose the corner where the launcher appears. The preview moves to the same position.">
                  <select
                    className="embedded-input"
                    value={settings.position}
                    onChange={event => updateSetting('position', event.target.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>
                  </select>
                </BuilderFormRow>

                <BuilderFormRow
                  label="Maximum Reply Length"
                  help="A lower value encourages shorter answers. A higher value allows more complete explanations and may use more of the connected OpenAI account allowance."
                  hint="Recommended starting range: 400-800."
                >
                  <input
                    className="embedded-input"
                    type="number"
                    min="64"
                    max="4096"
                    step="16"
                    value={settings.maxReplyTokens}
                    onChange={event => updateSetting('maxReplyTokens', event.target.value)}
                  />
                </BuilderFormRow>
              </WidgetSettingsPanel>

              <WidgetSettingsPanel title="Conversation" icon="messageCircle" description="Control the identity and first steps of each visitor conversation.">
                <BuilderFormRow label="Bot Name" help="This name appears at the top of the chat widget.">
                  <input
                    className="embedded-input"
                    value={settings.botName}
                    onChange={event => {
                      const nextBotName = event.target.value;
                      setSavedDraft(false);
                      setCopied(false);
                      setGenerated(false);
                      setApiError('');
                      setSettings(previous => ({
                        ...previous,
                        botName: nextBotName,
                        templateName: `${nextBotName || 'Website'} template`,
                      }));
                    }}
                  />
                </BuilderFormRow>

                <BuilderFormRow label="Profile Image" help="Shown at the top of the chat widget. Square images work best; leave empty to use the animated orb.">
                  <div style={{display:'flex', alignItems:'center', gap:14}}>
                    <div style={{flex:'0 0 56px', width:56, height:56, borderRadius:'50%', overflow:'hidden', border:'1px solid var(--pc-border)', background:'#ffffff', display:'flex', alignItems:'center', justifyContent:'center'}}>
                      {settings.avatarUrl
                        ? <img src={settings.avatarUrl} alt="Bot avatar" style={{width:'100%', height:'100%', objectFit:'cover'}}/>
                        : <img src="/assets/pluginchatbot-logo.svg" alt="PluginChatBot" style={{width:'70%', height:'70%', objectFit:'contain'}}/>}
                    </div>
                    <div style={{display:'flex', flexDirection:'column', gap:6}}>
                      <div style={{display:'flex', gap:8, alignItems:'center'}}>
                        <label className="btn btn-secondary" style={{cursor:'pointer', margin:0}}>
                          <Icon name="upload" size={13}/>{settings.avatarUrl ? 'Change image' : 'Upload image'}
                          <input
                            type="file"
                            accept="image/png,image/jpeg,image/webp,image/gif"
                            style={{display:'none'}}
                            onChange={async event => {
                              const file = event.target.files && event.target.files[0];
                              event.target.value = '';
                              if (!file) return;
                              try {
                                const dataUrl = await resizeImageToAvatarDataUrl(file, 128);
                                if (dataUrl && dataUrl.length > 300000) {
                                  setAvatarError('Image is too large after processing. Try a simpler image.');
                                  return;
                                }
                                setAvatarError('');
                                updateSetting('avatarUrl', dataUrl);
                              } catch (uploadError) {
                                setAvatarError(uploadError.message || 'Could not process that image.');
                              }
                            }}
                          />
                        </label>
                        {settings.avatarUrl && (
                          <button type="button" className="btn btn-secondary" onClick={() => { setAvatarError(''); updateSetting('avatarUrl', ''); }}>
                            <Icon name="x" size={13}/>Remove
                          </button>
                        )}
                      </div>
                      {avatarError && <div style={{fontSize:12, color:'#D92F24'}}>{avatarError}</div>}
                      <div style={{fontSize:11.5, color:'var(--pc-text-muted)'}}>PNG, JPG, WEBP or GIF · resized to 128×128</div>
                    </div>
                  </div>
                </BuilderFormRow>

                <BuilderFormRow label="Opening Message" help="The first message visitors see when they open the chatbot.">
                  <textarea
                    className="embedded-input"
                    rows="3"
                    value={settings.openingMessage}
                    onChange={event => updateSetting('openingMessage', event.target.value)}
                  />
                </BuilderFormRow>

                <BuilderFormRow label="Conversation Starters" help="Add short clickable questions that help visitors reach useful information quickly." hint="Add one starter per line. Leave the field empty to hide starters.">
                  <textarea
                    className="embedded-input"
                    rows="4"
                    value={settings.starters}
                    onChange={event => updateSetting('starters', event.target.value)}
                  />
                </BuilderFormRow>

                <BuilderFormRow label="System Instruction" help="Describe how the chatbot should represent the business and prepare useful answers.">
                  <textarea
                    className="embedded-input"
                    rows="5"
                    value={settings.systemInstruction}
                    onChange={event => updateSetting('systemInstruction', event.target.value)}
                  />
                </BuilderFormRow>

                <BuilderFormRow label="Guardrails" help="Add boundaries that keep the chatbot focused on safe, relevant business support.">
                  <textarea
                    className="embedded-input"
                    rows="4"
                    value={settings.guardrails}
                    onChange={event => updateSetting('guardrails', event.target.value)}
                  />
                </BuilderFormRow>

                <BuilderFormRow label="Fallback message" help="Shown when the chatbot cannot answer from its instructions or Knowledge Base. Leave blank to use the default polite decline.">
                  <textarea
                    className="embedded-input"
                    rows="2"
                    maxLength="600"
                    placeholder="Sorry, I don't have that information. Please contact us at help@yourcompany.com."
                    value={settings.fallbackMessage || ''}
                    onChange={event => updateSetting('fallbackMessage', event.target.value)}
                  />
                </BuilderFormRow>
              </WidgetSettingsPanel>
            </div>

            <PlugStyleWidgetPreview
              settings={settings}
              starters={starterList}
              positionLabel={positionLabel}
              className="app-builder-preview"
            />
          </div>
        </AIBuilderSection>

        <AIBuilderSection
          id="customize"
          title="Customize"
          icon="slidersHorizontal"
          description="AI behavior, website access, visitor capture, and voice settings"
          isOpen={openSections.customize}
          onToggle={() => toggleSection('customize')}
        >
          <div className="customize-settings-grid ai-builder-customize-grid">
            <SettingsCard
              className="customize-openai-card"
              title="AI Setup"
              icon="brain"
              description="Choose how your website chatbot prepares replies. PluginChatBot provisions the private AI key for each registered website."
            >
              <p className="app-info-note">Your website’s private AI key is managed securely by PluginChatBot and is never shown in this dashboard.</p>

              <CustomizeFormRow
                label="GPT Model"
                help="Choose the model that will answer visitors. After your website’s private AI key is provisioned, refresh to load its available models."
                hint={modelError || (modelsLoading ? 'Loading available models...' : 'The list is filtered to models suitable for chatbot text responses.')}
                hintTone={modelError ? 'error' : ''}
              >
                <div className="app-model-picker">
                  <select
                    className="embedded-input"
                    value={settings.model}
                    onChange={event => updateSetting('model', event.target.value)}
                  >
                    <option value="">Choose a reply model</option>
                    {modelOptions.map(model => <option key={model} value={model}>{model}</option>)}
                  </select>
                  <button
                    className="btn btn-secondary"
                    type="button"
                    onClick={loadAvailableModels}
                    disabled={modelsLoading || !settings.editingBotId}
                  >
                    <Icon name="refresh" size={14}/>{modelsLoading ? 'Loading' : 'Refresh Models'}
                  </button>
                </div>
              </CustomizeFormRow>

              <CustomizeFormRow
                label="Creativity"
                help="Lower values keep replies more consistent and focused. Higher values allow more variation. Some reasoning models manage this automatically."
                hint={!supportsTemperature ? 'The selected reasoning model manages creativity automatically.' : ''}
              >
                <div className={`embedded-range-row ${supportsTemperature ? '' : 'is-disabled'}`}>
                  <input
                    type="range"
                    min="0"
                    max="1"
                    step="0.1"
                    value={settings.temperature}
                    disabled={!supportsTemperature}
                    onChange={event => updateSetting('temperature', event.target.value)}
                  />
                  <span>{supportsTemperature ? settings.temperature : 'Auto'}</span>
                </div>
              </CustomizeFormRow>

              <CustomizeFormRow
                label="Rate Limit"
                help="Limit how many messages one visitor can send each second. This helps reduce automated abuse and unexpected API usage."
                hint="Set the value to 0 to disable this limit."
              >
                <div className="embedded-range-row">
                  <input
                    type="range"
                    min="0"
                    max="20"
                    step="1"
                    value={settings.rateLimit}
                    onChange={event => updateSetting('rateLimit', event.target.value)}
                  />
                  <span>{settings.rateLimit}</span>
                </div>
              </CustomizeFormRow>
            </SettingsCard>

            <SettingsCard
              className="customize-access-card"
              title="Website Access"
              icon="globe"
              description="Choose which websites are allowed to load this chatbot."
            >
              <div className="embedded-form-row">
                <AppFieldLabel
                  label="Allowed Domains"
                  help="Add each approved website without http, https, www, a path, or spaces."
                />

                <div className="domain-list">
                  {allowedDomainList.length > 0 ? allowedDomainList.map(domain => (
                    <div className="domain-item" key={domain}>
                      <span>{domain}</span>
                      <div>
                        <button type="button" onClick={() => editAllowedDomain(domain)}>Edit</button>
                        <button type="button" onClick={() => deleteAllowedDomain(domain)}>Delete</button>
                      </div>
                    </div>
                  )) : <div className="domain-empty">No domains added yet.</div>}
                </div>

                <div className="domain-add-row">
                  <input
                    className="embedded-input"
                    type="text"
                    value={domainInput}
                    placeholder="example.com"
                    onChange={event => {
                      setDomainInput(event.target.value);
                      setDomainError('');
                    }}
                    onKeyDown={event => {
                      if (event.key === 'Enter') {
                        event.preventDefault();
                        editingDomain ? saveEditedDomain() : addAllowedDomain();
                      }
                    }}
                  />
                  <button className="btn btn-primary" type="button" onClick={editingDomain ? saveEditedDomain : addAllowedDomain}>
                    {editingDomain ? 'Update' : 'Add'}
                  </button>
                  {editingDomain && <button className="btn btn-secondary" type="button" onClick={cancelEditingDomain}>Cancel</button>}
                </div>

                {domainError && <div className="domain-error">{domainError}</div>}
              </div>
            </SettingsCard>

            <SettingsCard
              className="customize-visitor-card"
              title="Visitor Capture"
              icon="contact"
              description="Collect useful contact details before or during a conversation."
            >
              <CustomizeToggleRow
                label="Enable Visitor Capture"
                help="When enabled, the chatbot asks visitors for contact details so your team can follow up after the conversation."
                description="Ask visitors for contact details before or during chat."
                checked={settings.visitorCaptureEnabled}
                onChange={value => updateSetting('visitorCaptureEnabled', value)}
              />

              <CustomizeFormRow
                label="Capture Mode"
                help="Choose whether details are collected inside the first chat message or through a separate form when the widget opens."
              >
                <div className="embedded-radio-grid">
                  <label>
                    <input
                      type="radio"
                      name="ai_builder_visitor_capture_mode"
                      value="chat"
                      checked={settings.visitorCaptureMode === 'chat'}
                      disabled={!settings.visitorCaptureEnabled}
                      onChange={event => updateSetting('visitorCaptureMode', event.target.value)}
                    />
                    <span>Chat (capture on first message)</span>
                  </label>
                  <label>
                    <input
                      type="radio"
                      name="ai_builder_visitor_capture_mode"
                      value="form"
                      checked={settings.visitorCaptureMode === 'form'}
                      disabled={!settings.visitorCaptureEnabled}
                      onChange={event => updateSetting('visitorCaptureMode', event.target.value)}
                    />
                    <span>Popup form (when opening the chatbot)</span>
                  </label>
                </div>
              </CustomizeFormRow>

              <CustomizeFormRow
                label="Capture Message"
                help="Use a short, welcoming explanation so visitors understand why their details are being requested."
              >
                <textarea
                  className="embedded-input"
                  rows="5"
                  disabled={!settings.visitorCaptureEnabled}
                  value={settings.visitorCapturePrompt}
                  onChange={event => updateSetting('visitorCapturePrompt', event.target.value)}
                />
              </CustomizeFormRow>
            </SettingsCard>

            <SettingsCard
              className="customize-voice-card"
              title="Voice Settings"
              icon="sparkles"
              description="Offer an optional AI-generated audio version of chatbot replies."
            >
              <CustomizeToggleRow
                label="Enable Voice Replies"
                help="Eligible chatbot replies include an audio playback option. Voice generation uses the connected OpenAI account."
                description="Let visitors listen to an AI-generated version of a reply."
                checked={settings.enableTts}
                onChange={value => updateSetting('enableTts', value)}
              />

              <CustomizeFormRow
                label="TTS Model"
                help="Choose the OpenAI text-to-speech model used to create audio replies."
              >
                <select
                  className="embedded-input"
                  value={settings.ttsModel}
                  disabled={!settings.enableTts}
                  onChange={event => updateSetting('ttsModel', event.target.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>
                </select>
              </CustomizeFormRow>

              <CustomizeFormRow
                label="Voice"
                help="Select the speaking style visitors will hear when they play an audio reply."
              >
                <select
                  className="embedded-input"
                  value={settings.voice}
                  disabled={!settings.enableTts}
                  onChange={event => updateSetting('voice', event.target.value)}
                >
                  {EMBEDDED_VOICE_OPTIONS.map(voice => (
                    <option key={voice} value={voice}>{capitalizeEmbedded(voice)}</option>
                  ))}
                </select>
              </CustomizeFormRow>
            </SettingsCard>
          </div>
        </AIBuilderSection>

        <AIBuilderSection
          id="knowledge-base"
          title="Knowledge Base"
          icon="bookOpen"
          description="Upload business files and manage the knowledge used by your chatbot templates"
          isOpen={openSections.knowledgeBase}
          onToggle={() => toggleSection('knowledgeBase')}
        >
          <Training embedded />
        </AIBuilderSection>

        <AIBuilderSection
          id="install"
          title="Install"
          icon="download"
          description="Template name, final preview, embed code, and update action"
          isOpen={openSections.install}
          onToggle={() => toggleSection('install')}
        >
          <div className="embedded-preview-install-wrap ai-builder-install-wrap">
            <div className="embedded-preview-install-card ai-builder-install-card">
              <div className="embedded-preview-install-head">
                <div>
                  <span className="eyebrow">Template</span>
                  <h3>{isEditing ? 'Update this chatbot template' : 'Create a chatbot template'}</h3>
                </div>
                <div className="embedded-preview-install-actions">
                  <button
                    className="btn btn-secondary btn-sm"
                    type="button"
                    onClick={saveDraft}
                  >
                    Save Draft
                  </button>
                  <button
                    className="btn btn-primary btn-sm"
                    type="button"
                    onClick={generateEmbedCode}
                    disabled={isGenerating}
                  >
                    {isGenerating ? 'Saving...' : isEditing ? 'Update Embed Code' : 'Generate Embed Code'}
                  </button>
                </div>
              </div>

              <div className="embedded-template-name-row">
                <InstallFormRow label="Template Name" help="Use a clear website-specific name so this template is easy to recognise in Integrations and Templates." hint="Examples: Main website, Service site, or Demo landing page.">
                  <input
                    className="embedded-input"
                    value={settings.templateName}
                    placeholder="Main website chatbot"
                    onChange={event => updateSetting('templateName', event.target.value)}
                  />
                </InstallFormRow>
                <div className="embedded-install-note">
                  <Icon name="info" size={16}/>
                  <span>{isEditing
                    ? `Editing template ID ${settings.editingBotId}. The existing embed code keeps the same bot ID.`
                    : 'A new template will be saved in Templates after the embed code is generated.'}</span>
                </div>
              </div>

              <div className="embedded-preview-install-grid">
                <PreviewInstallPanel
                  settings={settings}
                  starters={starterList}
                  positionLabel={positionLabel}
                />

                <div className="embedded-install-column">
                  <SettingsSummary settings={settings} starters={starterList} positionLabel={positionLabel} />

                  {generatedBotId && (
                    <div className="embedded-api-feedback is-success">
                      <strong>{isEditing ? 'Template updated successfully' : 'Template generated successfully'}</strong>
                      <span>Bot ID: {generatedBotId}</span>
                    </div>
                  )}

                  {apiError && (
                    <div className="embedded-api-feedback is-error">
                      <strong>Unable to save template</strong>
                      <span>{apiError}</span>
                    </div>
                  )}

                  <div className="embedded-environment-note">
                    Use this code on live websites. The widget and bot API both run from app.pluginchatbot.com.
                  </div>

                  <div className="embedded-code-box">
                    <pre>{embedCode}</pre>
                  </div>

                  <div className="embedded-code-actions">
                    <button className="btn btn-primary btn-sm" type="button" onClick={copyEmbedCode}>
                      {copied ? 'Copied' : 'Copy Code'}
                    </button>
                    <span>
                      {generated ? 'Saved in Templates. Copy this script into the target website.' : 'Generate the code after reviewing your builder settings.'}
                    </span>
                  </div>

                  <ol className="embedded-install-steps">
                    <li>Confirm Widget and Customize settings.</li>
                    <li>Name the template for this specific website.</li>
                    <li>Copy the production embed code.</li>
                    <li>Reopen saved templates from Templates to edit them later.</li>
                  </ol>

                  <div className="embedded-backend-note">
                    PluginChatBot securely manages a private OpenAI key for each registered website. It is never stored in your browser or exposed in the embed code.
                  </div>
                </div>
              </div>
            </div>
          </div>
        </AIBuilderSection>
      </div>
    </div>
  );
}

function AIBuilderSection({ id, title, icon, description, isOpen, onToggle, children }) {
  const panelId = `ai-builder-${id}`;

  return (
    <section className={`app-settings-panel ai-builder-main-section ${isOpen ? 'is-open' : ''}`}>
      <button
        className="app-settings-panel-summary ai-builder-main-summary"
        type="button"
        onClick={onToggle}
        aria-expanded={isOpen}
        aria-controls={panelId}
        data-tour={id === 'widget'
          ? 'ai-builder-widget'
          : id === 'customize'
            ? 'ai-builder-customize'
            : id === 'knowledge-base'
              ? 'ai-builder-knowledge-base'
              : 'ai-builder-install'}
      >
        <span className="icon-chip"><Icon name={icon} size={20}/></span>
        <span className="app-settings-panel-heading">
          <span className="app-settings-panel-title">{title}</span>
          <small>{description}</small>
        </span>
        <span className="app-settings-panel-toggle"><Icon name={isOpen ? 'chevronUp' : 'chevronDown'} size={18}/></span>
      </button>
      <div id={panelId} className="app-settings-panel-body ai-builder-main-body" hidden={!isOpen}>
        {children}
      </div>
    </section>
  );
}

function isAIBuilderHexColor(value) {
  return /^#[0-9a-f]{6}$/i.test(String(value || '').trim());
}


function getAIBuilderProductionEmbedOrigin() {
  return 'https://app.pluginchatbot.com';
}

function buildAIBuilderEmbedCode({ scriptOrigin, apiOrigin, botId }) {
  const safeScriptOrigin = String(scriptOrigin || '').replace(/\/+$/, '');
  const safeApiOrigin = String(apiOrigin || safeScriptOrigin).replace(/\/+$/, '');

  return `<script\n  src="${safeScriptOrigin}/embed.js"\n  data-bot-id="${botId}"\n  data-api-origin="${safeApiOrigin}"\n  async>\n</script>`;
}

window.AIBuilderPage = AIBuilderPage;
