/* global React, Icon, AppInfoTip, AppFieldLabel, getEmbeddedBuilderDraft, saveEmbeddedBuilderDraft, loadEmbeddedTemplateForEdit, getEmbeddedStarterList, getEmbeddedPositionLabel, getWidgetAccentBackground, useSelectedWorkspaceId, getWorkspaceSearchParams */
const { useEffect: useEffectViews2, useMemo: useMemoViews2, useRef: useRefViews2, useState: useStateViews2 } = React;

function Leads() {
  const workspaceId = useSelectedWorkspaceId();
  const [leads, setLeads] = useStateViews2([]);
  const [loading, setLoading] = useStateViews2(true);
  const [error, setError] = useStateViews2('');

  useEffectViews2(() => {
    let isMounted = true;
    setLoading(true);

    fetch(`/api/leads${getWorkspaceSearchParams(workspaceId)}`, {
      credentials: 'include',
      cache: 'no-store',
      headers: {
        'cache-control': 'no-cache',
      },
    })
      .then(async response => {
        const data = await response.json();
        if (!response.ok || !data.ok) {
          throw new Error(data && data.error ? data.error : 'Unable to load leads.');
        }
        return Array.isArray(data.leads) ? data.leads : [];
      })
      .then(items => {
        if (isMounted) {
          setLeads(items);
          setError('');
        }
      })
      .catch(err => {
        if (isMounted) {
          setError(err.message || 'Unable to load leads.');
        }
      })
      .finally(() => {
        if (isMounted) {
          setLoading(false);
        }
      });

    return () => {
      isMounted = false;
    };
  }, [workspaceId]);

  const exportCsv = () => {
    const headers = ['Name', 'Email', 'Phone', 'Bot', 'Source URL', 'Captured At'];
    const rows = leads.map(lead => [
      lead.name,
      lead.email,
      lead.phone,
      lead.botName,
      lead.sourceUrl,
      formatLeadDate(lead.updatedAt || lead.createdAt),
    ]);
    const csv = [headers, ...rows]
      .map(row => row.map(value => `"${String(value || '').replace(/"/g, '""')}"`).join(','))
      .join('\n');
    const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' });
    const url = URL.createObjectURL(blob);
    const link = document.createElement('a');

    link.href = url;
    link.download = `pluginchatbot-leads-${Date.now()}.csv`;
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
    URL.revokeObjectURL(url);
  };

  return (
    <div className="view">
      <div className="view-head">
        <div>
          <h1>Leads</h1>
          <div className="sub">{loading ? 'Loading captured leads…' : `${leads.length} captured lead${leads.length === 1 ? '' : 's'}`}</div>
        </div>
        <div className="view-actions" style={{display:'flex', gap:10}}>
          <button className="btn btn-secondary" type="button" onClick={exportCsv} disabled={!leads.length}>
            <Icon name="link" size={13}/>Export CSV
          </button>
        </div>
      </div>

      {error && (
        <div className="card" style={{borderColor:'rgba(251,191,36,0.35)', marginBottom:16}}>
          <div style={{fontSize:13.5, color:'var(--pc-text-muted)'}}>{error}</div>
        </div>
      )}

      {/* Desktop: table (scrolls horizontally only if truly needed). */}
      <div className="card app-table-scroll leads-table-view" style={{padding:0, overflowX:'auto'}}>
        <table style={{width:'100%', minWidth: 760, borderCollapse:'collapse'}}>
          <thead>
            <tr style={{background:'rgba(15,23,42,0.5)', borderBottom:'1px solid var(--pc-border)'}}>
              {['Name','Email','Phone','Bot','Source','Captured','Status'].map(h=>(
                <th key={h} style={{textAlign:'left', padding:'13px 18px', fontSize:11.5, fontWeight:600, color:'var(--pc-text-muted)', textTransform:'uppercase', letterSpacing:'0.08em'}}>{h}</th>
              ))}
            </tr>
          </thead>
          <tbody>
            {loading ? (
              <tr>
                <td colSpan="7" style={{padding:'22px 18px', fontSize:13, color:'var(--pc-text-muted)'}}>Loading leads…</td>
              </tr>
            ) : leads.length ? (
              leads.map((lead, i)=>(
                <tr key={`${lead.botId}-${lead.visitorId}-${i}`} style={{borderBottom: i<leads.length-1?'1px solid var(--pc-border)':'none'}}>
                  <td style={{padding:'14px 18px', fontSize:13.5, fontWeight:500}}>{lead.name || 'Unknown'}</td>
                  <td style={{padding:'14px 18px', fontSize:13, color:'var(--pc-text-muted)', fontFamily:'var(--pc-font-mono)'}}>{lead.email || '—'}</td>
                  <td style={{padding:'14px 18px', fontSize:13, color:'var(--pc-text-muted)', fontFamily:'var(--pc-font-mono)'}}>{lead.phone || '—'}</td>
                  <td style={{padding:'14px 18px', fontSize:13}}>{lead.botName || 'PluginChatBot Assistant'}</td>
                  <td style={{padding:'14px 18px', fontSize:12.5, color:'var(--pc-text-muted)', maxWidth:240, whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis'}} title={lead.sourceUrl || ''}>{lead.sourceUrl || '—'}</td>
                  <td style={{padding:'14px 18px', fontSize:12.5, color:'var(--pc-text-muted)'}}>{formatLeadDate(lead.updatedAt || lead.createdAt)}</td>
                  <td style={{padding:'14px 18px'}}>
                    <span className="pill success">New</span>
                  </td>
                </tr>
              ))
            ) : (
              <tr>
                <td colSpan="7" style={{padding:'22px 18px', fontSize:13, color:'var(--pc-text-muted)'}}>No captured leads yet.</td>
              </tr>
            )}
          </tbody>
        </table>
      </div>

      {/* Mobile/tablet: stacked cards (no horizontal scrolling). */}
      <div className="leads-card-view">
        {loading ? (
          <div className="card" style={{fontSize:13, color:'var(--pc-text-muted)'}}>Loading leads…</div>
        ) : leads.length ? (
          leads.map((lead, i) => (
            <div key={`c-${lead.botId}-${lead.visitorId}-${i}`} className="lead-card card">
              <div className="lead-card-head">
                <span className="lead-card-avatar">{(lead.name || '?').trim().charAt(0).toUpperCase() || '?'}</span>
                <div className="lead-card-title">
                  <div className="lead-card-name">{lead.name || 'Unknown'}</div>
                  <div className="lead-card-bot">{lead.botName || 'PluginChatBot Assistant'}</div>
                </div>
                <span className="pill success">New</span>
              </div>
              <div className="lead-card-rows">
                <div className="lead-card-row"><span>Email</span><b>{lead.email || '—'}</b></div>
                <div className="lead-card-row"><span>Phone</span><b>{lead.phone || '—'}</b></div>
                <div className="lead-card-row"><span>Source</span><b title={lead.sourceUrl || ''}>{lead.sourceUrl || '—'}</b></div>
                <div className="lead-card-row"><span>Captured</span><b>{formatLeadDate(lead.updatedAt || lead.createdAt)}</b></div>
              </div>
            </div>
          ))
        ) : (
          <div className="card" style={{fontSize:13, color:'var(--pc-text-muted)'}}>No captured leads yet.</div>
        )}
      </div>
    </div>
  );
}

function formatLeadDate(value) {
  if (!value) return '—';

  // Server timestamps are UTC without a zone suffix; browsers parse that
  // space-separated form as local time, skewing the shown time by the viewer's
  // UTC offset. Normalize to an explicit UTC instant first.
  const raw = String(value).trim();
  const normalized = /[zZ]|[+-]\d{2}:?\d{2}$/.test(raw) ? raw : `${raw.replace(' ', 'T')}Z`;
  const date = new Date(normalized);
  if (Number.isNaN(date.getTime())) {
    return value;
  }

  return date.toLocaleString();
}

function Training({ embedded = false }) {
  const workspaceId = useSelectedWorkspaceId();
  const fileInputRef = useRefViews2(null);
  const [templates, setTemplates] = useStateViews2([]);
  const [selectedBotId, setSelectedBotId] = useStateViews2(() => new URLSearchParams(window.location.search).get('botId') || '');
  const [files, setFiles] = useStateViews2([]);
  const [stores, setStores] = useStateViews2([]);
  const [settings, setSettings] = useStateViews2({
    openaiVectorStoreId: '',
    enableFileSearch: false,
    enableCodeInterpreter: false,
  });
  const [settingsDirty, setSettingsDirty] = useStateViews2(false);
  const [showAll, setShowAll] = useStateViews2(false);
  const [loading, setLoading] = useStateViews2(true);
  const [filesLoading, setFilesLoading] = useStateViews2(false);
  const [uploading, setUploading] = useStateViews2(false);
  const [dragging, setDragging] = useStateViews2(false);
  const [websiteUrl, setWebsiteUrl] = useStateViews2('');
  const [training, setTraining] = useStateViews2(false);
  const [saving, setSaving] = useStateViews2(false);
  const [error, setError] = useStateViews2('');
  const [notice, setNotice] = useStateViews2('');
  const [newStoreName, setNewStoreName] = useStateViews2('');
  const [filter, setFilter] = useStateViews2('');

  const selectedTemplate = useMemoViews2(() => {
    return templates.find(item => item.botId === selectedBotId) || null;
  }, [templates, selectedBotId]);

  const visibleFiles = useMemoViews2(() => {
    const needle = filter.trim().toLowerCase();
    if (!needle) return files;
    return files.filter(file => String(file.filename || '').toLowerCase().includes(needle));
  }, [files, filter]);

  useEffectViews2(() => {
    let isMounted = true;
    setLoading(true);
    setSelectedBotId('');
    setTrainingBotQuery('');
    setFiles([]);
    setStores([]);
    setSettingsDirty(false);

    fetch(`/api/embedded-bots${getWorkspaceSearchParams(workspaceId)}`, {
      credentials: 'include',
      cache: 'no-store',
    })
      .then(async response => {
        const data = await response.json();
        if (!response.ok || !data.ok) throw new Error(data.error || 'Unable to load templates.');
        return Array.isArray(data.bots) ? data.bots : [];
      })
      .then(items => {
        if (!isMounted) return;
        setTemplates(items);
        if (items.length) {
          const requestedBotId = new URLSearchParams(window.location.search).get('botId') || '';
          const firstBotId = items.some(item => item.botId === requestedBotId) ? requestedBotId : items[0].botId;
          setSelectedBotId(firstBotId);
          setTrainingBotQuery(firstBotId);
        }
      })
      .catch(err => {
        if (isMounted) setError(err.message || 'Unable to load templates.');
      })
      .finally(() => {
        if (isMounted) setLoading(false);
      });

    return () => {
      isMounted = false;
    };
  }, [workspaceId]);

  useEffectViews2(() => {
    if (!selectedBotId) return undefined;
    let isMounted = true;
    loadKnowledgeData(selectedBotId, showAll, isMounted);
    return () => { isMounted = false; };
  }, [selectedBotId, showAll]);

  useEffectViews2(() => {
    if (!selectedBotId || !files.some(file => isKnowledgeProcessing(file.status))) return undefined;
    const timer = window.setInterval(() => {
      loadKnowledgeData(selectedBotId, showAll, true, { silent: true });
    }, 5000);
    return () => window.clearInterval(timer);
  }, [selectedBotId, showAll, files]);

  function loadKnowledgeData(botId, includeAll, isMounted, options = {}) {
    if (!options.silent) {
      setFilesLoading(true);
      setError('');
    }

    Promise.all([
      fetch(`/api/embedded-bots/${encodeURIComponent(botId)}/knowledge/vector-stores`, {
        credentials: 'include',
        cache: 'no-store',
      }).then(readKnowledgeResponse),
      fetch(`/api/embedded-bots/${encodeURIComponent(botId)}/knowledge/files?showAll=${includeAll ? '1' : '0'}`, {
        credentials: 'include',
        cache: 'no-store',
      }).then(readKnowledgeResponse),
    ])
      .then(([storesData, filesData]) => {
        if (!isMounted) return;
        setStores(Array.isArray(storesData.stores) ? storesData.stores : []);
        setFiles(Array.isArray(filesData.files) ? filesData.files : []);
        setSettings(filesData.settings || storesData.settings || settings);
        setSettingsDirty(false);
      })
      .catch(err => {
        if (!isMounted) return;
        setError(err.message || 'Unable to load Knowledge Base.');
      })
      .finally(() => {
        if (isMounted && !options.silent) setFilesLoading(false);
      });
  }

  const selectTemplate = botId => {
    setSelectedBotId(botId);
    setTrainingBotQuery(botId);
    setFiles([]);
    setNotice('');
    setError('');
    setSettingsDirty(false);
  };

  const uploadSelectedFile = async file => {
    if (!file || !selectedBotId) return;

    const validationError = validateTrainingFile(file);
    if (validationError) {
      setError(validationError);
      return;
    }

    const formData = new FormData();
    formData.append('knowledgeFile', file);
    setUploading(true);
    setError('');
    setNotice('');

    try {
      const data = await fetch(`/api/embedded-bots/${encodeURIComponent(selectedBotId)}/knowledge/files`, {
        method: 'POST',
        credentials: 'include',
        body: formData,
      }).then(readKnowledgeResponse);

      setNotice(`${data.file?.filename || file.name} uploaded to the Knowledge Base.`);
      setSettings(data.settings || settings);
      setSettingsDirty(false);
      loadKnowledgeData(selectedBotId, showAll, true);
    } catch (err) {
      setError(err.message || 'Unable to upload file.');
    } finally {
      setUploading(false);
    }
  };

  const uploadFile = event => {
    const file = event.target.files && event.target.files[0];
    event.target.value = '';
    uploadSelectedFile(file);
  };

  const trainFromWebsite = async () => {
    const url = websiteUrl.trim();
    if (!selectedBotId || !url) return;
    setTraining(true);
    setError('');
    setNotice('');

    try {
      const data = await fetch(`/api/embedded-bots/${encodeURIComponent(selectedBotId)}/knowledge/website`, {
        method: 'POST',
        credentials: 'include',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify({ url }),
      }).then(readKnowledgeResponse);

      setNotice(`Imported content from ${data.file?.sourceUrl || url} into the Knowledge Base.`);
      setSettings(data.settings || settings);
      setSettingsDirty(false);
      setWebsiteUrl('');
      loadKnowledgeData(selectedBotId, showAll, true);
    } catch (err) {
      setError(err.message || 'Unable to import content from that website.');
    } finally {
      setTraining(false);
    }
  };

  const createVectorStore = async () => {
    if (!selectedBotId) return;
    const name = newStoreName.trim() || `${selectedTemplate?.templateName || selectedTemplate?.botName || 'PluginChatBot'} Knowledge Base`;
    setError('');
    setNotice('');

    try {
      const data = await fetch(`/api/embedded-bots/${encodeURIComponent(selectedBotId)}/knowledge/vector-store`, {
        method: 'POST',
        credentials: 'include',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify({ actionType: 'create_new', name }),
      }).then(readKnowledgeResponse);

      setNewStoreName('');
      setSettings(data.settings || { ...settings, openaiVectorStoreId: data.vectorStoreId, enableFileSearch: true });
      setSettingsDirty(false);
      setNotice('Knowledge Base created and selected.');
      loadKnowledgeData(selectedBotId, showAll, true);
    } catch (err) {
      setError(err.message || 'Unable to create the Knowledge Base.');
    }
  };

  const selectVectorStore = vectorStoreId => {
    if (!selectedBotId) return;

    setSettings(previous => ({
      ...previous,
      openaiVectorStoreId: vectorStoreId,
      enableFileSearch: vectorStoreId ? true : previous.enableFileSearch,
    }));
    setSettingsDirty(true);
    setNotice('');
    setError('');
  };

  const updateToolSetting = (key, value) => {
    setSettings(previous => ({ ...previous, [key]: value }));
    setSettingsDirty(true);
    setNotice('');
    setError('');
  };

  const saveKnowledgeSettings = async () => {
    if (!selectedBotId || !settingsDirty) return;
    setSaving(true);
    setError('');
    setNotice('');

    try {
      const data = await fetch(`/api/embedded-bots/${encodeURIComponent(selectedBotId)}/knowledge/settings`, {
        method: 'PATCH',
        credentials: 'include',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify(settings),
      }).then(readKnowledgeResponse);

      setSettings(data.settings || settings);
      setSettingsDirty(false);
      setNotice('Knowledge Base settings saved.');
    } catch (err) {
      setError(err.message || 'Unable to save Knowledge Base settings.');
    } finally {
      setSaving(false);
    }
  };

  const runFileAction = async (file, action) => {
    if (!selectedBotId || !file.id) return;
    const labels = {
      attach: 'File added to the Knowledge Base.',
      detach: 'File removed from the Knowledge Base.',
      delete: 'File deleted from OpenAI.',
    };

    if (action === 'delete' && !window.confirm(`Delete "${file.filename}" from OpenAI? This cannot be undone.`)) return;

    setError('');
    setNotice('');

    try {
      const endpoint = action === 'delete'
        ? `/api/embedded-bots/${encodeURIComponent(selectedBotId)}/knowledge/files/${encodeURIComponent(file.id)}`
        : `/api/embedded-bots/${encodeURIComponent(selectedBotId)}/knowledge/files/${encodeURIComponent(file.id)}/${action}`;

      await fetch(endpoint, {
        method: action === 'delete' ? 'DELETE' : 'POST',
        credentials: 'include',
      }).then(readKnowledgeResponse);

      setNotice(labels[action]);
      loadKnowledgeData(selectedBotId, showAll, true);
    } catch (err) {
      setError(err.message || 'File action failed.');
    }
  };

  return (
    <div className={`view training-view ${embedded ? 'is-embedded' : ''}`}>
      <div className={`view-head ${embedded ? 'training-embedded-toolbar' : ''}`}>
        {!embedded && (
          <div>
            <h1>Knowledge Base</h1>
            <div className="sub">Upload approved business files and connect them to each chatbot template</div>
          </div>
        )}
        {templates.length > 0 && (
          <div className="view-head-actions">
            <button
              className="btn btn-secondary"
              type="button"
              disabled={!selectedBotId || uploading}
              title={!selectedBotId ? 'Select a chatbot template first, then you can upload files.' : 'Upload a knowledge file'}
              onClick={() => fileInputRef.current?.click()}
            >
              <Icon name="upload" size={14}/>{uploading ? 'Uploading' : 'Upload File'}
            </button>
            <button
              className="btn btn-primary"
              type="button"
              disabled={!selectedBotId || !settingsDirty || saving}
              onClick={saveKnowledgeSettings}
            >
              {saving ? 'Saving' : 'Save Changes'}
            </button>
            <input ref={fileInputRef} type="file" accept=".pdf,.txt,.doc,.docx,.md,.json,.csv" onChange={uploadFile} hidden/>
          </div>
        )}
      </div>

      {error && (
        <div className="embedded-api-feedback is-error app-page-feedback">
          <strong>Something Went Wrong</strong>
          <span>{error}</span>
        </div>
      )}

      {notice && <div className="embedded-api-feedback is-success app-page-feedback"><strong>Saved</strong><span>{notice}</span></div>}

      {loading ? (
        <div className="card app-empty-state">Loading templates…</div>
      ) : templates.length === 0 ? (
        <div className="card app-empty-state">
          <div className="app-empty-icon"><Icon name="brain" size={22}/></div>
          <h2>No Saved Chatbot Templates Yet</h2>
          <p>Create a template first, then return here to add business knowledge.</p>
          <div className="app-empty-actions"><a className="btn btn-primary" href="/ai-builder">Create Template</a></div>
        </div>
      ) : (
        <div className="training-layout">
          <div className="training-primary">
            <section className="card training-template-card training-card">
              <div className="training-card-heading">
                <span className="icon-chip"><Icon name="bot" size={20}/></span>
                <div><h2>Choose a Chatbot Template</h2><p>Each template keeps its own Knowledge Base and connected files.</p></div>
              </div>
              <BuilderFormRow label="Chatbot Template" help="Select the chatbot that should use the files and answering tools managed on this page.">
                <select className="embedded-input" value={selectedBotId} onChange={event => selectTemplate(event.target.value)}>
                  {templates.map(template => <option key={template.botId} value={template.botId}>{template.templateName || template.botName}</option>)}
                </select>
              </BuilderFormRow>
            </section>

            <section className="card training-upload-card training-card">
              <div className="training-card-heading">
                <span className="icon-chip"><Icon name="upload" size={20}/></span>
                <div><h2>Add Business Files</h2><p>Upload current, approved information the chatbot can use when answering visitors.</p></div>
              </div>
              <button
                className={`training-dropzone ${dragging ? 'is-dragging' : ''}`}
                type="button"
                disabled={!selectedBotId || uploading}
                onClick={() => fileInputRef.current?.click()}
                onDragEnter={event => { event.preventDefault(); setDragging(true); }}
                onDragOver={event => event.preventDefault()}
                onDragLeave={event => { event.preventDefault(); setDragging(false); }}
                onDrop={event => {
                  event.preventDefault();
                  setDragging(false);
                  uploadSelectedFile(event.dataTransfer.files?.[0]);
                }}
              >
                <span className="training-dropzone-icon"><Icon name="upload" size={25}/></span>
                <strong>{uploading ? 'Uploading File…' : 'Drop a File Here or Browse'}</strong>
                <span>PDF, TXT, DOC, DOCX, MD, JSON, or CSV.</span>
              </button>

              <div className="training-website-row" style={{marginTop:14, borderTop:'1px solid var(--app-ui-line, rgba(17,17,17,.08))', paddingTop:14}}>
                <label className="embedded-form-row" style={{display:'block'}}>
                  <span>Train from a website</span>
                  <div style={{display:'flex', gap:8, flexWrap:'wrap'}}>
                    <input
                      className="embedded-input"
                      type="url"
                      inputMode="url"
                      placeholder="https://your-website.com/about"
                      value={websiteUrl}
                      disabled={!selectedBotId || training}
                      onChange={event => { setError(''); setWebsiteUrl(event.target.value); }}
                      onKeyDown={event => { if (event.key === 'Enter') { event.preventDefault(); trainFromWebsite(); } }}
                      style={{flex:'1 1 240px', minWidth:0}}
                    />
                    <button
                      className="btn btn-secondary"
                      type="button"
                      disabled={!selectedBotId || training || !websiteUrl.trim()}
                      onClick={trainFromWebsite}
                    >
                      {training ? 'Importing…' : 'Import from Website'}
                    </button>
                  </div>
                </label>
                <p className="training-hint" style={{margin:'6px 0 0', fontSize:12, color:'var(--app-ui-muted, #6b7280)'}}>
                  We fetch the page, extract its text, and add it to this template's Knowledge Base.
                </p>
              </div>
            </section>

            <section className="card training-files-card training-card">
              <div className="training-files-head">
                <div>
                  <h2>Knowledge Files</h2>
                  <p>{filesLoading ? 'Loading files…' : `${files.length} file${files.length === 1 ? '' : 's'} available`}</p>
                </div>
                <div className="training-files-tools">
                  <label className="training-inline-check">
                    <input type="checkbox" checked={showAll} onChange={event => setShowAll(event.target.checked)}/>
                    <span>Show All OpenAI Files</span>
                    <AppInfoTip text="Leave this off to focus on files connected to this chatbot. Turn it on to find other files from the same OpenAI account." label="Show all OpenAI files"/>
                  </label>
                  <input className="embedded-input" value={filter} placeholder="Filter files…" onChange={event => setFilter(event.target.value)}/>
                  <button className="btn btn-secondary btn-sm" type="button" onClick={() => loadKnowledgeData(selectedBotId, showAll, true)}><Icon name="refresh" size={14}/>Refresh</button>
                </div>
              </div>

              <div className="training-file-list">
                {filesLoading ? (
                  <div className="training-file-empty">Loading files…</div>
                ) : visibleFiles.length ? visibleFiles.map((file, index) => (
                  <article className="training-file-row" key={file.id || index}>
                    <span className="training-file-icon"><Icon name="fileCode" size={18}/></span>
                    <div className="training-file-copy">
                      <strong title={file.filename}>{file.filename}</strong>
                      <div>
                        <span>{formatTrainingBytes(file.bytes)}</span>
                        <span>{formatTrainingDate(file.createdAt)}</span>
                        <span className={`pill ${isKnowledgeProcessing(file.status) ? 'warn' : 'dim'}`}>{file.status || 'completed'}</span>
                        <span className={`pill ${file.inKnowledgeBase ? 'success' : 'dim'}`}>{file.inKnowledgeBase ? 'Added' : 'Not Added'}</span>
                      </div>
                    </div>
                    <div className="training-file-actions">
                      {file.inKnowledgeBase
                        ? <button className="btn btn-secondary btn-sm" type="button" onClick={() => runFileAction(file, 'detach')}>Remove</button>
                        : <button className="btn btn-secondary btn-sm" type="button" onClick={() => runFileAction(file, 'attach')}>Add to Knowledge</button>}
                      <button className="btn btn-secondary btn-sm app-danger-button" type="button" onClick={() => runFileAction(file, 'delete')}>Delete</button>
                    </div>
                  </article>
                )) : <div className="training-file-empty">No files found. Upload a document to train this template.</div>}
              </div>
            </section>
          </div>

          <aside className="training-secondary">
            <section className="card training-capabilities-card training-card">
              <div className="training-card-heading">
                <span className="icon-chip"><Icon name="sparkles" size={20}/></span>
                <div><h2>AI Answering Tools</h2><p>Turn on only the support visitors are likely to need.</p></div>
                <AppInfoTip text="Uploaded files help the chatbot answer from your business information. Settings are saved only when you select Save Changes." label="AI Answering Tools"/>
              </div>
              <TrainingToggle
                label="Search Business Files"
                help="Allow this chatbot to search the connected Knowledge Base before preparing an answer."
                description="Answer visitor questions from uploaded business files."
                checked={settings.enableFileSearch}
                onChange={value => updateToolSetting('enableFileSearch', value)}
              />
              <TrainingToggle
                label="Analyse Files and Calculations"
                help="Allow the chatbot to work with supported documents, tables, structured information, and questions that need calculations."
                description="Use OpenAI analysis tools for supported files and calculations."
                checked={settings.enableCodeInterpreter}
                onChange={value => updateToolSetting('enableCodeInterpreter', value)}
              />
              {settingsDirty && <div className="training-unsaved"><Icon name="info" size={14}/>Unsaved changes</div>}
            </section>

            <section className="card training-library-card training-card">
              <div className="training-card-heading">
                <span className="icon-chip"><Icon name="bookOpen" size={20}/></span>
                <div><h2>Knowledge Library</h2><p>Select an existing library or create a separate one for this chatbot.</p></div>
              </div>
              <BuilderFormRow label="Selected Library" help="A Knowledge Library is the OpenAI vector store that holds the files available to this chatbot.">
                <select className="embedded-input" value={settings.openaiVectorStoreId || ''} onChange={event => selectVectorStore(event.target.value)}>
                  <option value="">Create Automatically on First Upload</option>
                  {stores.map(store => <option key={store.id} value={store.id}>{store.name || 'Knowledge Library'} ({store.fileCount || 0})</option>)}
                </select>
              </BuilderFormRow>
              <BuilderFormRow label="Create New Library" help="Use a clear name when this chatbot needs a separate collection of business files.">
                <div className="training-create-library">
                  <input className="embedded-input" value={newStoreName} placeholder="Knowledge Base name" onChange={event => setNewStoreName(event.target.value)}/>
                  <button className="btn btn-secondary" type="button" onClick={createVectorStore}>Create</button>
                </div>
              </BuilderFormRow>
              {settings.openaiVectorStoreId && (
                <details className="training-technical-details">
                  <summary>Technical Details</summary>
                  <code>{settings.openaiVectorStoreId}</code>
                </details>
              )}
            </section>

            <div className="training-guidance">
              <Icon name="info" size={18}/>
              <div><strong>Use Customer-Ready Content</strong><p>Clear FAQs, service guides, pricing notes, and policies usually produce the most useful answers.</p></div>
            </div>
          </aside>
        </div>
      )}
    </div>
  );
}

function TrainingToggle({ label, help, description, checked, onChange }) {
  return (
    <div className="training-toggle-row">
      <div>
        <strong><span>{label}</span>{help && <AppInfoTip text={help} label={label}/>}</strong>
        <p>{description}</p>
      </div>
      <button
        type="button"
        className={`embedded-toggle ${checked ? 'is-on' : ''}`}
        onClick={() => onChange(!checked)}
        aria-pressed={Boolean(checked)}
        aria-label={`${checked ? 'Disable' : 'Enable'} ${label}`}
      ><span/></button>
    </div>
  );
}

async function readKnowledgeResponse(response) {
  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);
    throw new Error('Authentication required.');
  }

  if (!response.ok || !data.ok) {
    const error = new Error(data.error || 'Request failed.');
    error.code = data.code || '';
    error.status = response.status;
    throw error;
  }

  return data;
}

function setTrainingBotQuery(botId) {
  const url = new URL(window.location.href);
  if (botId) {
    url.searchParams.set('botId', botId);
  } else {
    url.searchParams.delete('botId');
  }
  window.history.replaceState({}, '', url.toString());
}

function validateTrainingFile(file) {
  const allowed = ['pdf', 'txt', 'doc', 'docx', 'md', 'json', 'csv'];
  const extension = String(file.name || '').split('.').pop().toLowerCase();

  if (!allowed.includes(extension)) {
    return 'Unsupported file type. Upload pdf, txt, doc, docx, md, json, or csv.';
  }

  if (!file.size) {
    return 'Uploaded file is empty.';
  }

  if (file.size > 10 * 1024 * 1024) {
    return 'File is too large. Maximum allowed size is 10 MB.';
  }

  return '';
}

function formatTrainingBytes(bytes) {
  const size = Number(bytes || 0);
  if (!size) return '—';
  if (size < 1024) return `${size} B`;
  if (size < 1024 * 1024) return `${(size / 1024).toFixed(1)} KB`;
  return `${(size / 1024 / 1024).toFixed(1)} MB`;
}

function formatTrainingDate(value) {
  if (!value) return '—';
  const milliseconds = Number(value) > 100000000000 ? Number(value) : Number(value) * 1000;
  const date = new Date(milliseconds);
  return Number.isNaN(date.getTime()) ? '—' : date.toLocaleString();
}

function isKnowledgeProcessing(status) {
  return ['queued', 'processing', 'in_progress'].includes(String(status || '').toLowerCase());
}

function WidgetEditor() {
  const workspaceId = useSelectedWorkspaceId();
  const [settings, setSettings] = useStateViews2(() => getEmbeddedBuilderDraft());
  const [saved, setSaved] = useStateViews2(false);

  useEffectViews2(() => {
    setSettings(getEmbeddedBuilderDraft());
    setSaved(false);
  }, [workspaceId]);

  const starterList = useMemoViews2(() => getEmbeddedStarterList(settings.starters), [settings.starters]);
  const positionLabel = getEmbeddedPositionLabel(settings.position);

  const updateSetting = (key, value) => {
    setSaved(false);
    setSettings(prev => ({ ...prev, [key]: value }));
  };

  const saveSettings = () => {
    const normalizedSettings = {
      ...settings,
      colorMode: 'solid',
      gradientColor: settings.color,
      chatBackgroundColor: '#000000',
    };

    setSettings(normalizedSettings);
    saveEmbeddedBuilderDraft(normalizedSettings);
    setSaved(true);
  };

  return (
    <div className="view">
      <div className="view-head">
        <div>
          <h1>Widget</h1>
          <div className="sub">Customize appearance and conversation behavior</div>
        </div>
        <button className="btn btn-primary" type="button" onClick={saveSettings}>Save changes</button>
      </div>

      {saved && <SavedInstallNotice />}

      <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={isWidgetHexColor(settings.color) ? settings.color : '#D92F24'}
                  onChange={e => updateSetting('color', e.target.value)}
                  aria-label="Choose widget brand color"
                />
                <input
                  className="embedded-input"
                  value={settings.color}
                  maxLength="7"
                  onChange={e => updateSetting('color', e.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={e => updateSetting('position', e.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={e => updateSetting('botName', e.target.value)}
              />
            </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={e => updateSetting('openingMessage', e.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={e => updateSetting('starters', e.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={e => updateSetting('systemInstruction', e.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={e => updateSetting('guardrails', e.target.value)}
              />
            </BuilderFormRow>
          </WidgetSettingsPanel>
        </div>

        <WidgetPreview settings={settings} starters={starterList} positionLabel={positionLabel} />
      </div>
    </div>
  );
}

function WidgetSettingsPanel({ title, icon, description, children }) {
  const [isOpen, setIsOpen] = useStateViews2(true);
  const panelId = `widget-${String(title || '').toLowerCase().replace(/[^a-z0-9]+/g, '-')}`;

  return (
    <section className={`app-settings-panel ${isOpen ? 'is-open' : ''}`}>
      <button
        className="app-settings-panel-summary"
        type="button"
        onClick={() => setIsOpen(current => !current)}
        aria-expanded={isOpen}
        aria-controls={panelId}
      >
        <span className="icon-chip"><Icon name={icon} size={20}/></span>
        <span className="app-settings-panel-heading"><span className="app-settings-panel-title">{title}</span>{description && <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" hidden={!isOpen}>
        {children}
      </div>
    </section>
  );
}

function WidgetPreview({ settings, starters, positionLabel }) {
  return (
    <PlugStyleWidgetPreview
      settings={settings}
      starters={starters}
      positionLabel={positionLabel}
      className="app-builder-preview"
    />
  );
}

function PlugStyleWidgetPreview({ settings, starters = [], positionLabel, className = '', compact = false }) {
  const accent = isWidgetHexColor(settings.color) ? settings.color : '#D92F24';
  const positionClass = getWidgetPreviewPositionClass(settings.position);
  const widgetStyle = {
    '--pcw-accent': accent,
    '--pcw-accent-soft': widgetHexToRgba(accent, 0.15),
    '--pcw-accent-subtle': widgetHexToRgba(accent, 0.08),
    '--pcw-accent-glow': widgetHexToRgba(accent, 0.38),
  };
  const statusText = settings.enableTts ? 'Online · AI-generated voice' : 'Online';

  return (
    <aside className={`plug-widget-live-preview ${compact ? 'is-compact' : ''} ${className}`.trim()} aria-label="Live widget preview">
      <div className="plug-widget-preview-heading">
        <div>
          <span className="plug-widget-preview-kicker">Live preview</span>
          <strong>See changes before you save</strong>
        </div>
        <span className="plug-widget-live-dot"><i aria-hidden="true"/>Live</span>
      </div>

      <div className={`plug-widget-preview-stage ${positionClass}`} title={positionLabel}>
        <div className="plug-widget-window" style={widgetStyle}>
          <div className="plug-widget-header">
            {settings.avatarUrl
              ? <img className="plug-widget-avatar" src={settings.avatarUrl} alt=""/>
              : <div className="plug-widget-orb" aria-hidden="true"><span /></div>}
            <div className="plug-widget-identity">
              <strong>{settings.botName || 'AI Assistant'}</strong>
              <small><i aria-hidden="true" />{statusText}</small>
            </div>
            <div className="plug-widget-header-actions" aria-hidden="true">
              <span>
                <svg viewBox="0 0 24 24" focusable="false"><path d="M7 3H3v4M17 3h4v4M7 21H3v-4M17 21h4v-4" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/></svg>
              </span>
              <span>
                <svg viewBox="0 0 24 24" focusable="false"><path d="M18 6 6 18M6 6l12 12" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round"/></svg>
              </span>
            </div>
          </div>

          <div className="plug-widget-messages">
            <div className="plug-widget-message">
              {settings.openingMessage || 'Hello! How can I help you today?'}
            </div>
            {starters.length > 0 && (
              <div className="plug-widget-starters" aria-label="Conversation starters">
                {starters.map(starter => <span key={starter}>{starter}</span>)}
              </div>
            )}
          </div>

          <div className="plug-widget-composer">
            <span>Type your message…</span>
            <button type="button" tabIndex="-1" aria-hidden="true">
              <svg viewBox="0 0 24 24" focusable="false"><path d="M22 2 11 13M22 2l-7 20-4-9-9-4 20-7Z" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/></svg>
            </button>
          </div>

          <div className="plug-widget-tools" aria-hidden="true">
            <span><Icon name="palette" size={12}/>Theme</span>
            <span><Icon name="refresh" size={12}/>Reset</span>
            <span><Icon name="download" size={12}/>Export</span>
            <span className="is-danger"><Icon name="x" size={12}/>End chat</span>
          </div>
        </div>
      </div>

      <p className="plug-widget-preview-note">
        This preview shows appearance and content only. It does not send messages or use your AI account.
      </p>
    </aside>
  );
}

function isWidgetHexColor(value) {
  return /^#[0-9a-f]{6}$/i.test(String(value || '').trim());
}

function widgetHexToRgba(value, alpha) {
  const hex = isWidgetHexColor(value) ? value.slice(1) : 'D92F24';
  const red = parseInt(hex.slice(0, 2), 16);
  const green = parseInt(hex.slice(2, 4), 16);
  const blue = parseInt(hex.slice(4, 6), 16);
  return `rgba(${red}, ${green}, ${blue}, ${alpha})`;
}

function getWidgetPreviewPositionClass(position) {
  if (position === 'left') return 'is-bottom-left';
  if (position === 'top_right') return 'is-top-right';
  if (position === 'top_left') return 'is-top-left';
  return 'is-bottom-right';
}

function SavedInstallNotice() {
  return (
    <div className="app-save-notice">
      Settings saved. Please generate embedded code from <a href="/ai-builder">AI Builder</a>.
    </div>
  );
}

function BuilderFormRow({ label, help, hint, children }) {
  return (
    <div className="embedded-form-row">
      <AppFieldLabel label={label} help={help}/>
      {children}
      {hint && <small>{hint}</small>}
    </div>
  );
}

window.Leads = Leads;
window.Training = Training;
window.WidgetEditor = WidgetEditor;
