// Knowledge screen — sub-routes: context, files, pages
function ScreenKnowledge({ route, onAction }) {
  // CMS (site) é restrito a admin+ — editor/viewer não acessam.
  const canCms = Auth.userCan('admin');
  const rawSub = route.split(":")[1] || "context";
  // Se um editor forçar a URL knowledge:cms, cai de volta em "context".
  const sub = (rawSub === "cms" && !canCms) ? "context" : rawSub;
  const isFaqFile = (f) => f.docType === "faq" || f.sourceType === "faq";
  const faqCount = Data.files.filter(isFaqFile).length;
  const docsCount = Data.files.filter((f) => f.sourceType === "upload" && !isFaqFile(f)).length;
  const outrosCount = Data.files.length - faqCount - docsCount;
  const tabs = [
    { id: "context", label: "Contexto direto", icon: "fa-brain", count: Data.directContexts.length },
    { id: "docs", label: "Documentos", icon: "fa-file-lines", count: docsCount },
    { id: "faqs", label: "FAQs (IA)", icon: "fa-lightbulb", count: faqCount },
    { id: "files", label: "Arquivos (site/CMS)", icon: "fa-folder-open", count: outrosCount },
    { id: "pages", label: "Páginas do site", icon: "fa-globe", count: Data.sitePages.length },
    ...(canCms ? [{ id: "cms", label: "CMS (site)", icon: "fa-rss" }] : []),
  ];
  const currentTab = tabs.find((t) => t.id === sub) || tabs[0];

  return (
    <div className="page" data-screen-label={"Conhecimento · " + currentTab.label}>
      <div className="page-header">
        <div>
          <h1>
            <span className="h1-icon"><i className="fas fa-brain"></i></span>
            Base de conhecimento
          </h1>
          <p className="sub">Tudo que a IA pode usar para responder — contextos diretos, arquivos indexados e páginas do site.</p>
        </div>
        <div className="page-header-actions">
          {sub === "context" && (
            <button className="btn btn-primary" onClick={() => onAction("add-context")}>
              <i className="fas fa-plus"></i> Novo contexto
            </button>
          )}
          {(sub === "files" || sub === "docs") && (
            <button className="btn btn-ghost" onClick={() => onAction("reindex")}>
              <i className="fas fa-sync"></i> Reindexar tudo
            </button>
          )}
          {sub === "docs" && (
            <button className="btn btn-primary" onClick={() => onAction("upload")}>
              <i className="fas fa-plus"></i> Adicionar documento
            </button>
          )}
          {sub === "pages" && (
            <button className="btn btn-primary" onClick={() => onAction("add-page")}>
              <i className="fas fa-plus"></i> Adicionar página
            </button>
          )}
        </div>
      </div>

      <div className="subtabs">
        {tabs.map((t) => (
          <button key={t.id} className={"tab" + (sub === t.id ? " active" : "")} onClick={() => onAction("nav:knowledge:" + t.id)}>
            <i className={"fas " + t.icon}></i> {t.label}
            {t.count != null && <span className="count">{t.count}</span>}
          </button>
        ))}
      </div>

      {sub === "context" && <ContextPane />}
      {sub === "docs" && <FilesPane sourceFilter="only-docs" />}
      {sub === "faqs" && <FilesPane sourceFilter="only-faq" />}
      {sub === "files" && <FilesPane sourceFilter="outros" />}
      {sub === "pages" && <PagesPane />}
      {sub === "cms" && canCms && <CmsPane />}
    </div>
  );
}

// ---------- CMS (integração com a API de conteúdo do SINTRACON2) ----------
function CmsPane() {
  const toast = useToast();
  const [cfg, setCfg] = useState(null);
  const [token, setToken] = useState('');        // novo token (vazio = manter atual)
  const [status, setStatus] = useState(null);
  const [busy, setBusy] = useState(false);
  const [testing, setTesting] = useState(false);
  const [testResult, setTestResult] = useState(null);
  const [dismissed, setDismissed] = useState(false); // overlay dispensado ("2º plano")
  const wasRunning = useRef(false);
  const canEdit = Auth.userCan('edit_system');   // admin
  const canSync = Auth.userCan('upload_files');   // editor
  const boolVal = (v) => v === 'true' || v === true;

  const loadCfg = () => api('/api/admin/cms/config').then((r) => setCfg(r || {})).catch(() => {});
  const loadStatus = () => api('/api/admin/cms/sync/status').then(setStatus).catch(() => {});

  useEffect(() => { loadCfg(); loadStatus(); }, []);
  // Enquanto um sync roda, consulta o status a cada 2.5s pra atualizar o progresso.
  useEffect(() => {
    if (!status || !status.running) return;
    const t = setInterval(loadStatus, 2500);
    return () => clearInterval(t);
  }, [status && status.running]);
  // Detecta a transição rodando→parado pra avisar a conclusão (sem disparar no load).
  useEffect(() => {
    const isRunning = !!(status && status.running);
    if (wasRunning.current && !isRunning) {
      const l = status && status.last;
      if (l && l.totals) {
        toast({
          title: 'Sincronização concluída',
          msg: `+${l.totals.created} novos · ~${l.totals.updated} atualizados · erros ${l.totals.errors}`,
          kind: l.totals.errors ? 'info' : 'success',
        });
      }
      setDismissed(false);
    }
    wasRunning.current = isRunning;
  }, [status && status.running]);

  const setField = (k, v) => setCfg((c) => Object.assign({}, c, { [k]: v }));

  const save = () => {
    if (!cfg) return;
    setBusy(true);
    const payload = {
      cms_api_base_url: cfg.cms_api_base_url || '',
      cms_modules: cfg.cms_modules || '',
      cms_modules_auto: String(cfg.cms_modules_auto == null ? true : boolVal(cfg.cms_modules_auto)),
      cms_sync_enabled: String(boolVal(cfg.cms_sync_enabled)),
      cms_sync_interval_minutes: String(cfg.cms_sync_interval_minutes || '360'),
      cms_enrichment_enabled: String(boolVal(cfg.cms_enrichment_enabled)),
      cms_enrichment_max_questions: String(cfg.cms_enrichment_max_questions || '6'),
      cms_recency_weight: String(cfg.cms_recency_weight || '0.15'),
      cms_recency_halflife_days: String(cfg.cms_recency_halflife_days || '180'),
      rag_hard_floor_enabled: String(boolVal(cfg.rag_hard_floor_enabled)),
      response_quality_mode: cfg.response_quality_mode || 'equilibrado',
    };
    if (token) payload.cms_api_token = token;
    api('/api/admin/cms/config', { method: 'PATCH', body: payload })
      .then(() => { toast({ title: 'Configuração salva', kind: 'success' }); setToken(''); loadCfg(); })
      .catch((e) => toast({ title: 'Falha ao salvar', msg: e.message, kind: 'error' }))
      .finally(() => setBusy(false));
  };

  const test = () => {
    setTesting(true); setTestResult(null);
    api('/api/admin/cms/test', { method: 'POST' })
      .then((r) => { setTestResult(r); toast({ title: r.ok ? 'Conexão OK' : 'Falha na conexão', kind: r.ok ? 'success' : 'error' }); })
      .catch((e) => toast({ title: 'Falha no teste', msg: e.message, kind: 'error' }))
      .finally(() => setTesting(false));
  };

  const sync = () => {
    setDismissed(false);
    api('/api/admin/cms/sync', { method: 'POST' })
      .then((r) => { toast({ title: r.started ? 'Sincronização iniciada' : 'Sync já em andamento', kind: 'info' }); setTimeout(loadStatus, 1000); })
      .catch((e) => toast({ title: 'Falha ao sincronizar', msg: e.message, kind: 'error' }));
  };

  if (!cfg) return <div className="card"><p className="sub">Carregando configuração…</p></div>;

  const last = status && status.last;
  const autoOn = cfg.cms_modules_auto == null ? true : boolVal(cfg.cms_modules_auto);

  return (
    <div className="cms-pane" style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
      <div className="card">
        <h3>Conexão com o CMS</h3>
        <div className="field">
          <label>Base URL da API</label>
          <input className="input" value={cfg.cms_api_base_url || ''} onChange={(e) => setField('cms_api_base_url', e.target.value)} placeholder="https://…/api/v1" disabled={!canEdit} />
        </div>
        <div className="field">
          <label>Token (Bearer)</label>
          <input className="input" type="password" value={token} onChange={(e) => setToken(e.target.value)}
            placeholder={cfg.token_set ? '•••••••• (configurado — deixe vazio pra manter)' : 'cole o token sk_…'}
            disabled={!canEdit || cfg.token_from_env} />
          <span className="hint">{cfg.token_from_env
            ? 'Token definido via variável de ambiente (CMS_API_TOKEN) — edite no servidor.'
            : 'O token fica salvo no servidor; nunca é exibido de volta.'}</span>
        </div>
        <div className="field">
          <Switch on={autoOn} onChange={(v) => setField('cms_modules_auto', v)} label="Detectar módulos automaticamente (pelo escopo do token)" />
          <span className="hint">Ligado (recomendado): o chatbot sincroniza tudo que o token expõe no CMS — você não precisa listar nada aqui. Ao liberar um módulo novo no token, ele entra sozinho no próximo sync. Desligue para sincronizar apenas um subconjunto manual.</span>
        </div>
        <div className="field">
          <label>Módulos (keys separadas por vírgula){autoOn ? ' — usado só no modo manual' : ''}</label>
          <input className="input" value={cfg.cms_modules || ''} onChange={(e) => setField('cms_modules', e.target.value)} placeholder="news,alimentacao-ia" disabled={!canEdit || autoOn} />
          <span className="hint">Use as keys da API (news, booklets, services, salary…). Cada uma precisa estar no escopo do token no painel do CMS.</span>
        </div>
        <div className="row gap-md">
          <button className="btn btn-ghost" onClick={test} disabled={testing}>
            {testing ? <><i className="fas fa-circle-notch fa-spin"></i> Testando…</> : <><i className="fas fa-plug"></i> Testar conexão</>}
          </button>
        </div>
        {testResult && (
          <div className="hint" style={{ marginTop: 8 }}>
            {Object.entries(testResult.modules || {}).map(([m, v]) => (
              <div key={m}>
                <i className={'fas ' + (v.ok ? 'fa-check' : 'fa-times')} style={{ color: v.ok ? 'var(--gr)' : 'var(--rd)' }}></i>
                {' '}{m}: {v.ok ? (v.totalCount + ' itens') : ('erro ' + (v.status || '') + ' ' + (v.error || ''))}
              </div>
            ))}
            {testResult.error && <div style={{ color: 'var(--rd)' }}>{testResult.error}</div>}
          </div>
        )}
      </div>

      <div className="card">
        <h3>Sincronização</h3>
        <div className="row gap-md">
          <Switch on={boolVal(cfg.cms_sync_enabled)} onChange={(v) => setField('cms_sync_enabled', v)} label="Sync automático (cron)" />
        </div>
        <div className="field">
          <label>Intervalo (minutos)</label>
          <input className="input" type="number" min="5" value={cfg.cms_sync_interval_minutes || '360'} onChange={(e) => setField('cms_sync_interval_minutes', e.target.value)} style={{ maxWidth: 160 }} disabled={!canEdit} />
        </div>
        <div className="row gap-md">
          <button className="btn btn-primary" onClick={sync} disabled={!canSync || (status && status.running)}>
            {status && status.running
              ? <><i className="fas fa-circle-notch fa-spin"></i> Sincronizando…</>
              : <><i className="fas fa-sync"></i> Sincronizar agora</>}
          </button>
        </div>
        {last && (
          <div className="hint" style={{ marginTop: 8 }}>
            <div>Último sync: {last.finished_at ? new Date(last.finished_at).toLocaleString('pt-BR') : '—'} ({Math.round((last.elapsed_ms || 0) / 1000)}s)</div>
            <div>Totais: +{last.totals.created} novos · ~{last.totals.updated} atualizados · −{last.totals.deleted} removidos · ={last.totals.unchanged} iguais · erros {last.totals.errors}</div>
            {(last.modules || []).map((m) => (
              <div key={m.module}>• {m.module}: +{m.created} ~{m.updated} −{m.deleted} ={m.unchanged}{m.error ? (' [' + m.error + ']') : ''}</div>
            ))}
          </div>
        )}
      </div>

      <div className="card">
        <h3>Qualidade da recuperação</h3>
        <div className="field">
          <label>Modo de qualidade da resposta</label>
          <select className="input" value={cfg.response_quality_mode || 'equilibrado'} onChange={(e) => setField('response_quality_mode', e.target.value)} style={{ maxWidth: 320 }} disabled={!canEdit}>
            <option value="economia">Economia — mais rápido e barato (flash-lite, sem reranker)</option>
            <option value="equilibrado">Equilibrado — recomendado (flash, reranker)</option>
            <option value="alto">Alto desempenho — pro + thinking + verificação anti-alucinação</option>
          </select>
          <span className="hint">Pacote aplicado a cada resposta. "Alto" usa um modelo mais forte e confere a resposta contra o contexto (mais lento/caro). O override de modelo só vale com o provider Gemini ativo.</span>
        </div>
        <div className="row gap-md">
          <Switch on={boolVal(cfg.cms_enrichment_enabled)} onChange={(v) => setField('cms_enrichment_enabled', v)} label="Enriquecimento (perguntas-equivalentes)" />
        </div>
        <div className="field">
          <label>Máx. perguntas por item</label>
          <input className="input" type="number" min="0" max="12" value={cfg.cms_enrichment_max_questions || '6'} onChange={(e) => setField('cms_enrichment_max_questions', e.target.value)} style={{ maxWidth: 160 }} disabled={!canEdit} />
        </div>
        <div className="field">
          <label>Peso da recência</label>
          <input className="input" type="number" step="0.05" min="0" max="1" value={cfg.cms_recency_weight || '0.15'} onChange={(e) => setField('cms_recency_weight', e.target.value)} style={{ maxWidth: 160 }} disabled={!canEdit} />
          <span className="hint">Boost aditivo: conteúdo recente pesa mais. 0 = recência desligada.</span>
        </div>
        <div className="field">
          <label>Meia-vida da recência (dias)</label>
          <input className="input" type="number" min="1" value={cfg.cms_recency_halflife_days || '180'} onChange={(e) => setField('cms_recency_halflife_days', e.target.value)} style={{ maxWidth: 160 }} disabled={!canEdit} />
        </div>
        <div className="row gap-md">
          <Switch on={boolVal(cfg.rag_hard_floor_enabled)} onChange={(v) => setField('rag_hard_floor_enabled', v)} label="Piso anti-alucinação (não responde sem contexto)" />
        </div>
      </div>

      {canEdit && (
        <div className="row gap-md">
          <button className="btn btn-primary" onClick={save} disabled={busy}>
            {busy ? <><i className="fas fa-circle-notch fa-spin"></i> Salvando…</> : <><i className="fas fa-save"></i> Salvar configuração</>}
          </button>
        </div>
      )}

      {status && status.running && !dismissed && (
        <div style={{ position: 'fixed', inset: 0, zIndex: 9999, background: 'rgba(0,0,0,0.72)', backdropFilter: 'blur(3px)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
          <div className="card" style={{ maxWidth: 440, width: '90%', textAlign: 'center', padding: 32 }}>
            <i className="fas fa-circle-notch fa-spin" style={{ fontSize: 42, color: '#f97316' }}></i>
            <h3 style={{ marginTop: 16 }}>Sincronizando conteúdo do CMS</h3>
            {status.progress ? (
              <>
                <p className="sub" style={{ marginTop: 8 }}>
                  Módulo <strong>{status.progress.module || '—'}</strong> ({status.progress.moduleIndex}/{status.progress.moduleCount})
                </p>
                <p className="sub">{status.progress.done}/{status.progress.total || '?'} itens processados</p>
                {status.progress.total > 0 && (
                  <div style={{ height: 8, borderRadius: 4, background: 'rgba(255,255,255,0.12)', overflow: 'hidden', margin: '12px 0' }}>
                    <div style={{ height: '100%', width: Math.round((status.progress.done / Math.max(1, status.progress.total)) * 100) + '%', background: '#f97316', transition: 'width .4s' }}></div>
                  </div>
                )}
              </>
            ) : (
              <p className="sub" style={{ marginTop: 8 }}>Preparando…</p>
            )}
            <p className="hint" style={{ marginTop: 8 }}>Pode levar alguns minutos (enriquecimento + embedding por item). Você pode acompanhar aqui ou deixar rodando em segundo plano.</p>
            <button className="btn btn-ghost" style={{ marginTop: 12 }} onClick={() => setDismissed(true)}>
              <i className="fas fa-clock"></i> Continuar em segundo plano
            </button>
          </div>
        </div>
      )}
    </div>
  );
}

// ---------- Contexts ----------
function ContextPane() {
  const toast = useToast();
  const confirm = useConfirm();
  const [contexts, setContexts] = useState(Data.directContexts);
  const [filter, setFilter] = useState("all");
  const [editing, setEditing] = useState(null);
  const reload = () => DataLoader.directContexts().then(() => setContexts([...Data.directContexts]));
  useEffect(() => {
    let alive = true;
    DataLoader.directContexts().then(() => { if (alive) setContexts([...Data.directContexts]); });
    return () => { alive = false; };
  }, []);
  // Deep-link vindo do "Buscar na base": abre o editor do contexto/diretriz apontado.
  useEffect(() => {
    const k = window.__kbOpen;
    if (!k || (k.kind !== 'contexto' && k.kind !== 'diretriz') || !contexts.length) return;
    const c = contexts.find((x) => x.id === k.id);
    if (c) { setEditing(c); window.__kbOpen = null; }
  }, [contexts]);

  const visible = useMemo(() => {
    if (filter === "always") return contexts.filter((c) => c.always);
    if (filter === "tagged") return contexts.filter((c) => c.tags.length > 0 && !c.always);
    if (filter === "off") return contexts.filter((c) => !c.enabled);
    return contexts;
  }, [contexts, filter]);

  const toggle = (id, key) => {
    const cur = contexts.find((c) => c.id === id);
    if (!cur) return;
    const next = !cur[key];
    // Optimistic UI
    setContexts((cs) => cs.map((c) => c.id === id ? { ...c, [key]: next } : c));
    const payload = { [key === 'enabled' ? 'enabled' : 'always_inject']: next };
    api('/api/admin/direct-contexts/' + id, { method: 'PATCH', body: payload })
      .catch((e) => {
        toast({ title: "Falha ao atualizar", msg: e.message, kind: "error" });
        setContexts((cs) => cs.map((c) => c.id === id ? { ...c, [key]: !next } : c));
      });
  };

  const askDelete = async (c) => {
    const ok = await confirm({
      title: 'Apagar contexto direto',
      message: <>Apagar o contexto <strong>"{c.title}"</strong>?</>,
      details: 'O card será removido da base e não entrará mais em nenhuma resposta da IA. ' +
               'Você precisará recadastrá-lo manualmente para voltar a usar.',
      confirmLabel: 'Apagar contexto',
      danger: true,
      icon: 'fa-brain',
    });
    if (!ok) return;
    api('/api/admin/direct-contexts/' + c.id, { method: 'DELETE' })
      .then(() => { toast({ title: "Contexto apagado", kind: "success" }); reload(); })
      .catch((e) => toast({ title: "Falha ao apagar", msg: e.message, kind: "error" }));
  };

  return (
    <>
      <div className="card" style={{ background: "var(--bg2)", border: "1px solid var(--b1)" }}>
        <div style={{ display: "flex", gap: 14, alignItems: "flex-start" }}>
          <div style={{ width: 32, height: 32, borderRadius: 8, background: "var(--og)", color: "var(--or)", display: "grid", placeItems: "center", flexShrink: 0 }}>
            <i className="fas fa-lightbulb"></i>
          </div>
          <div style={{ fontSize: 12.5, color: "var(--t2)", lineHeight: 1.55, flex: 1 }}>
            Cada card é um trecho de informação. Use <strong style={{ color: "var(--or)" }}>"Sempre injetar"</strong> para contextos críticos (endereço, política de fallback). Para o resto, defina <strong>tags</strong> — o card só entra no prompt quando a tag aparecer na pergunta.
            <div style={{ marginTop: 6, color: "var(--t3)", fontSize: 11.5 }}>
              Ex.: card "Assembleia 28/05" com tag <code style={{ background: "var(--bg3)", padding: "1px 5px", borderRadius: 4 }}>assembleia, evento</code> só entra se a pergunta tiver alguma dessas palavras. Economiza tokens.
            </div>
          </div>
        </div>
      </div>

      <div className="row gap-sm" style={{ justifyContent: "space-between" }}>
        <div className="subtabs" style={{ background: "var(--bg2)" }}>
          <button className={"tab" + (filter === "all" ? " active" : "")} onClick={() => setFilter("all")}>Todos<span className="count">{contexts.length}</span></button>
          <button className={"tab" + (filter === "always" ? " active" : "")} onClick={() => setFilter("always")}>Sempre injetam<span className="count">{contexts.filter((c) => c.always).length}</span></button>
          <button className={"tab" + (filter === "tagged" ? " active" : "")} onClick={() => setFilter("tagged")}>Por tag<span className="count">{contexts.filter((c) => c.tags.length > 0 && !c.always).length}</span></button>
          <button className={"tab" + (filter === "off" ? " active" : "")} onClick={() => setFilter("off")}>Desligados<span className="count">{contexts.filter((c) => !c.enabled).length}</span></button>
        </div>
        <div className="row gap-sm">
          <input className="input" placeholder="Buscar contextos…" style={{ width: 240 }} />
        </div>
      </div>

      <div>
        {visible.map((c) => {
          // Calcula status de expiração: null=sem expiração; expired=passou;
          // soon=faltam < 7d; future=>=7d. Usado pra cor do badge e label.
          let expiry = null;
          if (c.expires_at) {
            const exp = new Date(c.expires_at);
            const diffMs = exp.getTime() - Date.now();
            const diffDays = Math.ceil(diffMs / (24 * 60 * 60 * 1000));
            if (diffMs <= 0) {
              expiry = { state: 'expired', label: 'expirado', date: exp };
            } else if (diffDays <= 7) {
              expiry = { state: 'soon', label: `expira em ${diffDays}d`, date: exp };
            } else {
              expiry = { state: 'future', label: `expira em ${diffDays}d`, date: exp };
            }
          }
          return (
          <div key={c.id} className="kc-card" style={!c.enabled ? { opacity: 0.55 } : {}}>
            <span className="kc-handle"><i className="fas fa-grip-vertical"></i></span>
            <div className="kc-body">
              <div className="kc-head">
                <span className="kc-title">{c.title}</span>
                {c.kind === 'diretriz' && <span className="badge b-bl"><i className="fas fa-route"></i> diretriz</span>}
                {c.always && <span className="badge b-or"><i className="fas fa-thumbtack"></i> sempre</span>}
                {!c.always && c.tags.length > 0 && <span className="badge b-bl"><i className="fas fa-tag"></i> por tag</span>}
                {!c.enabled && <span className="badge b-mut"><i className="fas fa-power-off"></i> off</span>}
                {expiry && (
                  <span
                    className={'badge ' + (expiry.state === 'expired' ? 'b-rd' : expiry.state === 'soon' ? 'b-am' : 'b-mut')}
                    title={expiry.date.toLocaleString('pt-BR')}
                  >
                    <i className="fas fa-hourglass-half"></i> {expiry.label}
                  </span>
                )}
              </div>
              <div className="kc-text">{c.body}</div>
              {c.tags.length > 0 && (
                <div className="kc-tags">
                  {c.tags.map((t) => <span key={t} className="tag">{t}</span>)}
                </div>
              )}
            </div>
            <div className="kc-actions">
              <Switch on={c.enabled} onChange={() => toggle(c.id, "enabled")} />
              <button className="icon-btn" onClick={() => setEditing(c)} title="Editar"><i className="fas fa-edit"></i></button>
              <button className="icon-btn" style={{ color: "var(--rd)" }} onClick={() => askDelete(c)} title="Apagar"><i className="fas fa-trash"></i></button>
            </div>
          </div>
          );
        })}
      </div>

      <p className="muted" style={{ fontSize: 11.5, marginTop: 4 }}>
        <i className="fas fa-arrows-alt-v" style={{ marginRight: 4 }}></i>
        Arraste os cards pelo handle para reordenar. A ordem afeta como aparecem no prompt.
      </p>

      {editing && <ContextEditModal context={editing} onClose={() => setEditing(null)} onSaved={() => { setEditing(null); reload(); }} />}
    </>
  );
}

// ---- Modal para criar/editar contexto direto ----
function ContextEditModal({ context, onClose, onSaved }) {
  const toast = useToast();
  // expires_at vem como ISO timestamptz (ex: '2026-06-30T23:59:59.000Z').
  // <input type="datetime-local"> precisa de 'YYYY-MM-DDTHH:mm' no fuso local.
  const isoToLocalInput = (iso) => {
    if (!iso) return '';
    const d = new Date(iso);
    if (isNaN(d.getTime())) return '';
    const pad = (n) => String(n).padStart(2, '0');
    return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
  };
  const [vals, setVals] = useState({
    title: context.title || '',
    body: context.body || '',
    tag: Array.isArray(context.tags) ? context.tags.join(', ') : (context.tag || ''),
    always_inject: !!context.always,
    enabled: context.enabled !== false,
    kind: context.kind === 'diretriz' ? 'diretriz' : 'informacao',
    expires_at: isoToLocalInput(context.expires_at),
  });
  const [busy, setBusy] = useState(false);
  const [showAIImprove, setShowAIImprove] = useState(false);
  const setV = (k, v) => setVals((s) => ({ ...s, [k]: v }));

  const submit = (e) => {
    e.preventDefault();
    if (!vals.title.trim() || !vals.body.trim()) {
      toast({ title: "Campos obrigatórios", msg: "Título e conteúdo são necessários.", kind: "info" });
      return;
    }
    setBusy(true);
    // datetime-local devolve 'YYYY-MM-DDTHH:mm'. new Date() interpreta como
    // local time → o backend recebe ISO UTC. '' vira null pra remover expiração.
    const expIso = vals.expires_at ? new Date(vals.expires_at).toISOString() : null;
    api('/api/admin/direct-contexts/' + context.id, {
      method: 'PATCH',
      body: {
        title: vals.title,
        body: vals.body,
        tag: vals.tag,
        always_inject: vals.always_inject,
        enabled: vals.enabled,
        kind: vals.kind,
        expires_at: expIso,
      },
    })
      .then(() => { toast({ title: "Contexto atualizado", kind: "success" }); onSaved(); })
      .catch((e) => { toast({ title: "Falha ao salvar", msg: e.message, kind: "error" }); setBusy(false); });
  };

  return (
    <div className="modal-overlay" onClick={onClose}>
      <form className="modal modal-wide" onClick={(e) => e.stopPropagation()} onSubmit={submit}>
        <div className="modal-head">
          <h3><i className="fas fa-edit"></i> Editar contexto direto</h3>
          <button type="button" className="icon-btn" onClick={onClose}><i className="fas fa-times"></i></button>
        </div>
        <div className="modal-body">
          <div className="field">
            <label>Tipo</label>
            <select className="input" value={vals.kind} onChange={(e) => setV('kind', e.target.value)}>
              <option value="informacao">Informação (um fato pra IA responder)</option>
              <option value="diretriz">Diretriz de atendimento (como conduzir a conversa)</option>
            </select>
            <span className="hint">
              {vals.kind === 'diretriz'
                ? 'Diretriz: um passo a passo de COMO a IA deve conduzir certos pedidos. É seguida como fluxo e nunca aparece na resposta.'
                : 'Informação: um dado autoritativo que a IA usa pra responder (ex.: valor da mensalidade, endereço).'}
            </span>
          </div>
          <div className="field">
            <label>Título</label>
            <input className="input" value={vals.title} onChange={(e) => setV('title', e.target.value)} required maxLength={200} />
          </div>
          <div className="field">
            <label style={{ display: 'flex', alignItems: 'center' }}>
              <span>Conteúdo</span>
              <button
                type="button"
                className="btn btn-soft btn-sm"
                onClick={() => setShowAIImprove(true)}
                disabled={vals.body.trim().length < 50}
                title={vals.body.trim().length < 50 ? 'Mínimo 50 caracteres' : 'Pré-processar com IA pra melhorar retrieval'}
                style={{ marginLeft: 'auto' }}
              >
                <i className="fas fa-wand-magic-sparkles" style={{ color: 'var(--or)' }}></i> Melhorar com IA
              </button>
            </label>
            <textarea className="input" rows={8} value={vals.body} onChange={(e) => setV('body', e.target.value)} required></textarea>
          </div>
          <div className="field">
            <label>Tags (separadas por vírgula)</label>
            <input className="input" value={vals.tag} onChange={(e) => setV('tag', e.target.value)} placeholder="horario, atendimento, expediente" />
            <span className="hint">Quando a pergunta do usuário contém uma tag, este contexto entra no prompt. Ignorado se "Sempre injetar" estiver ligado.</span>
          </div>
          <div className="field">
            <label>Data de expiração (opcional)</label>
            <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
              <input
                type="datetime-local"
                className="input"
                value={vals.expires_at}
                onChange={(e) => setV('expires_at', e.target.value)}
                style={{ flex: 1, maxWidth: 280 }}
              />
              {vals.expires_at && (
                <button
                  type="button"
                  className="btn btn-ghost btn-sm"
                  onClick={() => setV('expires_at', '')}
                  title="Remover expiração"
                >
                  <i className="fas fa-times"></i> Limpar
                </button>
              )}
            </div>
            <span className="hint">
              Quando essa data passa, o contexto é desativado automaticamente — útil pra
              avisos sazonais (assembleia, eleição, evento). Vazio = nunca expira.
            </span>
          </div>
          <div className="row gap-md">
            <Switch on={vals.always_inject} onChange={(v) => setV('always_inject', v)} label="Sempre injetar (ignora tags)" />
            <Switch on={vals.enabled} onChange={(v) => setV('enabled', v)} label="Ativo" />
          </div>
        </div>
        <div className="modal-foot">
          <button type="button" className="btn btn-ghost" onClick={onClose}>Cancelar</button>
          <button type="submit" className="btn btn-primary" disabled={busy}>
            {busy ? <><i className="fas fa-circle-notch fa-spin"></i> Salvando…</> : <><i className="fas fa-save"></i> Salvar alterações</>}
          </button>
        </div>
      </form>
      {showAIImprove && window.AIImproveModal && (
        <window.AIImproveModal
          originalText={vals.body}
          sourceHint="direct_context"
          filename={vals.title}
          onAccept={(improved) => { setV('body', improved); toast({ title: 'Conteúdo atualizado', msg: 'Revise antes de salvar.', kind: 'success' }); }}
          onClose={() => setShowAIImprove(false)}
        />
      )}
    </div>
  );
}

// ---------- Files ----------
function FilesPane({ sourceFilter }) {
  // sourceFilter: "only-faq" (só respostas da IA) | "exclude-faq" (tudo menos IA) | undefined
  const toast = useToast();
  const confirm = useConfirm();
  const [selected, setSelected] = useState([]);
  const [files, setFiles] = useState(Data.files);
  const [search, setSearch] = useState('');
  const [typeFilter, setTypeFilter] = useState('all');
  const [statusFilter, setStatusFilter] = useState('all');
  const [viewing, setViewing] = useState(null);
  const [editing, setEditing] = useState(null);
  const [aiImproving, setAiImproving] = useState(null); // file object → abre AIImproveModal pra reprocessar
  const [aiBatchOpen, setAiBatchOpen] = useState(false); // abre modal de batch
  const [dupesOpen, setDupesOpen] = useState(false); // abre modal de duplicatas
  const reload = () => DataLoader.files().then(() => setFiles([...Data.files]));
  useEffect(() => {
    let alive = true;
    DataLoader.files().then(() => { if (alive) setFiles([...Data.files]); });
    return () => { alive = false; };
  }, []);
  // Deep-link vindo do "Buscar na base": abre os detalhes do documento apontado.
  useEffect(() => {
    const k = window.__kbOpen;
    if (!k || k.kind !== 'documento' || !files.length) return;
    const f = files.find((x) => x.id === k.id);
    if (f) {
      window.__kbOpen = null;
      api('/api/admin/files/' + f.id)
        .then((data) => setViewing({ ...f, detail: data }))
        .catch(() => {});
    }
  }, [files]);
  // Auto-refresh enquanto houver arquivo "Processando": a lista atualiza sozinha
  // (o badge com spinner vira "Indexado"/"Falhou") sem o usuário dar F5.
  const anyProcessing = files.some((f) => f.status === 'processing');
  useEffect(() => {
    if (!anyProcessing) return;
    const t = setInterval(() => {
      DataLoader.files().then(() => setFiles([...Data.files]));
    }, 3000);
    return () => clearInterval(t);
  }, [anyProcessing]);
  const filtered = useMemo(() => {
    return files.filter((f) => {
      const isFaq = f.docType === 'faq' || f.sourceType === 'faq';
      const isManualDoc = f.sourceType === 'upload' && !isFaq; // PDF/DOCX/texto adicionados à mão
      if (sourceFilter === 'only-faq' && !isFaq) return false;
      if (sourceFilter === 'only-docs' && !isManualDoc) return false;
      if (sourceFilter === 'outros' && (isFaq || isManualDoc)) return false;
      if (search && !(f.name || '').toLowerCase().includes(search.toLowerCase())) return false;
      if (typeFilter !== 'all' && f.type !== typeFilter) return false;
      if (statusFilter !== 'all' && f.status !== statusFilter) return false;
      return true;
    });
  }, [files, search, typeFilter, statusFilter, sourceFilter]);
  const indexed = files.filter((f) => f.status === "indexed").length;
  const processing = files.filter((f) => f.status === "processing").length;
  const errors = files.filter((f) => f.status === "error").length;
  const totalChunks = files.reduce((s, f) => s + f.chunks, 0);

  const toggle = (id) => {
    setSelected((s) => s.includes(id) ? s.filter((x) => x !== id) : [...s, id]);
  };

  const askDelete = async (f) => {
    const ok = await confirm({
      title: 'Apagar arquivo da base',
      message: <>Apagar <strong>"{f.name}"</strong> e todos os seus embeddings?</>,
      details: `O arquivo (${f.chunks || 0} chunks) será removido do banco vetorial. ` +
               'A IA deixa de ter acesso a esse conteúdo imediatamente. Para voltar a usá-lo, será necessário reenviar.',
      confirmLabel: 'Apagar arquivo',
      danger: true,
      icon: 'fa-file-circle-xmark',
    });
    if (!ok) return;
    api('/api/admin/files/' + f.id, { method: 'DELETE' })
      .then(() => { toast({ title: "Arquivo removido", kind: "success" }); reload(); })
      .catch((e) => toast({ title: "Falha ao remover", msg: e.message, kind: "error" }));
  };

  const reindex = (f) => {
    api('/api/admin/process', { method: 'POST', body: { ids: [f.id] } })
      .then(() => { toast({ title: "Reprocessamento iniciado", msg: f.name, kind: "info" }); setTimeout(reload, 1500); })
      .catch((e) => toast({ title: "Falha ao reprocessar", msg: e.message, kind: "error" }));
  };

  // Reindexa APENAS os arquivos ainda não indexados nesta aba (respeita o filtro
  // atual). Mais seguro que "reindexar tudo", que reprocessa a base inteira.
  const reindexPending = () => {
    const pend = filtered.filter((f) => f.status !== 'indexed').map((f) => f.id);
    if (!pend.length) {
      toast({ title: "Tudo indexado", msg: "Nenhum arquivo pendente nesta aba.", kind: "info" });
      return;
    }
    api('/api/admin/process', { method: 'POST', body: { ids: pend } })
      .then(() => { toast({ title: "Reindexando pendentes", msg: `${pend.length} arquivo(s)`, kind: "info" }); setTimeout(reload, 1200); })
      .catch((e) => toast({ title: "Falha ao reprocessar", msg: e.message, kind: "error" }));
  };

  const view = (f) => {
    api('/api/admin/files/' + f.id).then((data) => setViewing({ ...f, detail: data })).catch((e) => toast({ title: "Falha", msg: e.message, kind: "error" }));
  };

  const bulkDelete = async () => {
    if (!selected.length) return;
    const ok = await confirm({
      title: 'Apagar arquivos em lote',
      message: <>Apagar <strong>{selected.length} arquivo{selected.length === 1 ? '' : 's'}</strong> da base?</>,
      details: 'Todos os chunks (embeddings) associados serão removidos. A IA perderá acesso a esse conteúdo imediatamente.',
      confirmLabel: 'Apagar ' + selected.length,
      danger: true,
      icon: 'fa-trash',
    });
    if (!ok) return;
    Promise.all(selected.map((id) => api('/api/admin/files/' + id, { method: 'DELETE' }).catch(() => null)))
      .then(() => { toast({ title: "Arquivos removidos", kind: "success" }); setSelected([]); reload(); });
  };
  const bulkProcess = () => {
    if (!selected.length) return;
    api('/api/admin/process', { method: 'POST', body: { ids: selected } })
      .then(() => { toast({ title: "Processamento em lote iniciado", kind: "info" }); setTimeout(reload, 1500); })
      .catch((e) => toast({ title: "Falha", msg: e.message, kind: "error" }));
  };

  const typeIcon = { PDF: "fa-file-pdf", TEXT: "fa-file-alt", TXT: "fa-file-alt", DOCX: "fa-file-word", MD: "fa-file-code", IMG: "fa-file-image" };
  const typeColor = { PDF: "var(--rd)", DOCX: "var(--bl)", TEXT: "var(--t2)", TXT: "var(--t2)", MD: "var(--bl)", IMG: "var(--am)" };

  return (
    <>
      <div className="dash-grid-3">
        <div className="card tight" style={{ display: "flex", alignItems: "center", gap: 14 }}>
          <div className="stat-icon gr" style={{ width: 36, height: 36, fontSize: 14 }}><i className="fas fa-check-circle"></i></div>
          <div>
            <div style={{ fontSize: 22, fontWeight: 600 }}>{indexed}</div>
            <div style={{ fontSize: 11.5, color: "var(--t3)" }}>arquivos indexados</div>
          </div>
        </div>
        <div className="card tight" style={{ display: "flex", alignItems: "center", gap: 14 }}>
          <div className="stat-icon or" style={{ width: 36, height: 36, fontSize: 14 }}><i className="fas fa-cubes"></i></div>
          <div>
            <div style={{ fontSize: 22, fontWeight: 600 }}>{totalChunks.toLocaleString("pt-BR")}</div>
            <div style={{ fontSize: 11.5, color: "var(--t3)" }}>chunks no banco vetorial</div>
          </div>
        </div>
        <div className="card tight" style={{ display: "flex", alignItems: "center", gap: 14 }}>
          <div className="stat-icon rd" style={{ width: 36, height: 36, fontSize: 14 }}><i className="fas fa-exclamation-triangle"></i></div>
          <div>
            <div style={{ fontSize: 22, fontWeight: 600 }}>{processing + errors}</div>
            <div style={{ fontSize: 11.5, color: "var(--t3)" }}>{processing} processando · {errors} erros</div>
          </div>
        </div>
      </div>

      <div className="row gap-sm" style={{ justifyContent: "space-between" }}>
        <div className="row gap-sm">
          {selected.length > 0 ? (
            <>
              <span className="badge b-or">{selected.length} selecionados</span>
              <button className="btn btn-soft btn-sm" onClick={bulkProcess}><i className="fas fa-cogs"></i> Processar</button>
              <button className="btn btn-danger btn-sm" onClick={bulkDelete}><i className="fas fa-trash"></i> Remover</button>
            </>
          ) : (
            <>
              <input className="input" placeholder="Buscar arquivos por nome…" style={{ width: 320 }} value={search} onChange={(e) => setSearch(e.target.value)} />
              <button className="btn btn-soft btn-sm" onClick={() => setAiBatchOpen(true)} title="Processar lote de arquivos não-otimizados com IA">
                <i className="fas fa-wand-magic-sparkles" style={{ color: 'var(--or)' }}></i> Otimizar lote com IA
              </button>
              <button className="btn btn-soft btn-sm" onClick={() => setDupesOpen(true)} title="Detectar arquivos duplicados ou versões antigas">
                <i className="fas fa-clone"></i> Duplicatas
              </button>
              {(() => {
                const pendingCount = filtered.filter((f) => f.status !== 'indexed').length;
                return (
                  <button className="btn btn-soft btn-sm" onClick={reindexPending} disabled={pendingCount === 0}
                          title="Reprocessar apenas os arquivos ainda não indexados desta aba">
                    <i className="fas fa-rotate"></i> Reindexar pendentes{pendingCount > 0 ? ` (${pendingCount})` : ''}
                  </button>
                );
              })()}
            </>
          )}
        </div>
        <div className="row gap-sm">
          <select className="input" style={{ width: 140 }} value={typeFilter} onChange={(e) => setTypeFilter(e.target.value)}>
            <option value="all">Todos os tipos</option>
            <option value="PDF">PDF</option>
            <option value="DOCX">DOCX</option>
            <option value="TXT">TXT</option>
            <option value="TEXT">TEXT</option>
            <option value="MD">MD</option>
          </select>
          <select className="input" style={{ width: 140 }} value={statusFilter} onChange={(e) => setStatusFilter(e.target.value)}>
            <option value="all">Todos os status</option>
            <option value="indexed">Indexados</option>
            <option value="processing">Processando</option>
            <option value="failed">Falhou</option>
            <option value="error">Erros</option>
          </select>
        </div>
      </div>

      <div className="card flush">
        <div className="table-wrap">
          <table className="dt">
            <thead>
              <tr>
                <th style={{ width: 36 }}><input type="checkbox" checked={selected.length > 0 && selected.length === filtered.length} onChange={(e) => setSelected(e.target.checked ? filtered.map((f) => f.id) : [])} /></th>
                <th>Arquivo</th>
                <th style={{ width: 90 }}>Tipo</th>
                <th style={{ width: 90 }}>Tamanho</th>
                <th style={{ width: 80, textAlign: "right" }}>Chunks</th>
                <th style={{ width: 130 }}>Status</th>
                <th style={{ width: 160 }}>Indexado em</th>
                <th style={{ width: 140, textAlign: "right" }}>Ações</th>
              </tr>
            </thead>
            <tbody>
              {filtered.map((f) => (
                <tr key={f.id}>
                  <td><input type="checkbox" checked={selected.includes(f.id)} onChange={() => toggle(f.id)} /></td>
                  <td>
                    <div className="row gap-sm">
                      <i className={"fas " + (typeIcon[f.type] || "fa-file")} style={{ color: typeColor[f.type] || "var(--t2)", fontSize: 14 }}></i>
                      <span>{f.name}</span>
                    </div>
                  </td>
                  <td><span className="tag">{f.type}</span></td>
                  <td><code style={{ fontSize: 11, color: "var(--t3)" }}>{f.size}</code></td>
                  <td style={{ textAlign: "right", fontFamily: "var(--ff-mono)", color: f.chunks > 0 ? "var(--t1)" : "var(--t3)" }}>{f.chunks || "—"}</td>
                  <td><StatusBadge status={f.status} /></td>
                  <td style={{ fontFamily: "var(--ff-mono)", fontSize: 11, color: "var(--t3)" }}>{f.date}</td>
                  <td>
                    <div className="row-actions">
                      <button className="icon-btn" onClick={() => view(f)} title="Ver detalhes"><i className="fas fa-eye"></i></button>
                      <button className="icon-btn" onClick={() => setEditing(f)} title="Editar título"><i className="fas fa-edit"></i></button>
                      <button
                        className="icon-btn"
                        onClick={() => setAiImproving(f)}
                        title={f.status === 'indexed' ? 'Melhorar com IA + re-indexar' : 'Aguarde indexar antes de melhorar'}
                        disabled={f.status !== 'indexed'}
                        style={{ color: f.status === 'indexed' ? 'var(--or)' : 'var(--t3)' }}
                      >
                        <i className="fas fa-wand-magic-sparkles"></i>
                      </button>
                      <button className="icon-btn" onClick={() => reindex(f)} title="Reindexar só este documento"><i className="fas fa-redo"></i></button>
                      <button className="icon-btn" style={{ color: "var(--rd)" }} onClick={() => askDelete(f)} title="Apagar"><i className="fas fa-trash"></i></button>
                    </div>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>

      {viewing && <FileViewModal file={viewing} onClose={() => setViewing(null)} />}
      {editing && <FileEditModal file={editing} onClose={() => setEditing(null)} onSaved={() => { setEditing(null); reload(); }} />}
      {dupesOpen && <DuplicatesModal onClose={() => setDupesOpen(false)} onChanged={reload} onEdit={(f) => { setDupesOpen(false); setEditing({ id: f.id, name: f.original_name }); }} />}
      {aiImproving && window.AIImproveModal && (
        <window.AIImproveModal
          fileId={aiImproving.id}
          sourceHint={aiImproving.type ? aiImproving.type.toLowerCase() : 'pdf'}
          filename={aiImproving.name}
          onAccept={() => { setAiImproving(null); setTimeout(reload, 1500); }}
          onClose={() => setAiImproving(null)}
        />
      )}
      {aiBatchOpen && window.AIBatchModal && (
        <window.AIBatchModal
          onClose={() => setAiBatchOpen(false)}
          onDone={() => { setTimeout(reload, 1500); }}
        />
      )}
    </>
  );
}

// ---- Modal: ver detalhes do arquivo (preview do texto extraído) ----
function FileViewModal({ file, onClose }) {
  const d = file.detail || {};
  return (
    <div className="modal-overlay" onClick={onClose}>
      <div className="modal modal-wide modal-tall" onClick={(e) => e.stopPropagation()}>
        <div className="modal-head">
          <h3><i className="fas fa-file-alt"></i> {file.name}</h3>
          <button type="button" className="icon-btn" onClick={onClose}><i className="fas fa-times"></i></button>
        </div>
        <div className="modal-body">
          <div className="row gap-sm" style={{ flexWrap: 'wrap' }}>
            <span className="badge b-mut">{file.type}</span>
            <span className="badge b-bl">{file.chunks} chunks</span>
            <StatusBadge status={file.status} />
            <span className="badge b-mut" style={{ fontFamily: 'var(--ff-mono)' }}>{file.size}</span>
            <span className="badge b-mut" style={{ fontFamily: 'var(--ff-mono)' }}>{file.date}</span>
          </div>
          {(d.status === 'failed' || d.status === 'error') && d.error_log && (
            <div className="field">
              <label style={{ color: 'var(--rd)' }}><i className="fas fa-exclamation-triangle"></i> Motivo da falha</label>
              <div style={{ padding: '10px 12px', background: 'rgba(220,50,50,.08)', border: '1px solid var(--rd)', borderRadius: 'var(--r-input)', fontSize: 12.5, color: 'var(--rd)', whiteSpace: 'pre-wrap', fontFamily: 'var(--ff-mono)' }}>
                {d.error_log}
              </div>
            </div>
          )}
          {d.stored_name && (
            <div style={{ fontSize: 11, color: 'var(--t3)', fontFamily: 'var(--ff-mono)', marginTop: 4 }}>
              <i className="fas fa-hdd"></i> arquivo em disco: {d.stored_name}
            </div>
          )}
          {d.user_context && (
            <div className="field">
              <label><i className="fas fa-info-circle"></i> Contexto adicional</label>
              <div style={{ padding: '10px 12px', background: 'var(--bg1)', border: '1px solid var(--b1)', borderRadius: 'var(--r-input)', fontSize: 12.5, color: 'var(--t2)', whiteSpace: 'pre-wrap' }}>
                {d.user_context}
              </div>
            </div>
          )}
          {d.extracted_text && (
            <div className="field">
              <label><i className="fas fa-align-left"></i> Texto extraído (preview)</label>
              <pre style={{ background: 'var(--bg1)', border: '1px solid var(--b1)', borderRadius: 'var(--r-input)', padding: '10px 14px', fontFamily: 'var(--ff-mono)', fontSize: 11.5, color: 'var(--t2)', lineHeight: 1.55, maxHeight: 320, overflowY: 'auto', whiteSpace: 'pre-wrap', margin: 0 }}>
                {d.extracted_text.slice(0, 5000)}{d.extracted_text.length > 5000 ? '\n\n[…texto truncado, mostrando primeiros 5000 chars]' : ''}
              </pre>
            </div>
          )}
          {!d.extracted_text && (
            <div className="empty" style={{ padding: 20 }}>
              <i className="fas fa-hourglass-half"></i>
              <p>Texto extraído ainda não disponível.</p>
              <p style={{ fontSize: 11, color: 'var(--t3)' }}>Aguarde o processamento concluir e clique no olho novamente.</p>
            </div>
          )}
        </div>
        <div className="modal-foot">
          <button type="button" className="btn btn-ghost" onClick={onClose}>Fechar</button>
        </div>
      </div>
    </div>
  );
}

// ---- Modal: editar título do arquivo ----
function FileEditModal({ file, onClose, onSaved }) {
  const toast = useToast();
  const [title, setTitle] = useState(file.name || '');
  const [userContext, setUserContext] = useState('');
  const [text, setText] = useState('');
  const [originalText, setOriginalText] = useState('');
  const [loaded, setLoaded] = useState(false);
  const [busy, setBusy] = useState(false);
  const [meta, setMeta] = useState(null);
  const [docType, setDocType] = useState('');
  const [sourceWeight, setSourceWeight] = useState('');
  const [validUntil, setValidUntil] = useState('');

  // Carrega o conteúdo extraído ao abrir
  useEffect(() => {
    let alive = true;
    api('/api/admin/files/' + file.id)
      .then((d) => {
        if (!alive) return;
        setUserContext(d.user_context || '');
        const t = d.extracted_text || '';
        setText(t);
        setOriginalText(t);
        setDocType(d.doc_type || '');
        setSourceWeight(d.source_weight != null ? String(d.source_weight) : '');
        setValidUntil(d.valid_until ? String(d.valid_until).slice(0, 10) : '');
        setMeta({
          file_type: d.file_type,
          source_type: d.source_type,
          source_url: d.source_url,
          chunk_count: d.chunk_count,
          file_size_kb: d.file_size_kb,
        });
        setLoaded(true);
      })
      .catch((e) => { toast({ title: "Falha ao carregar arquivo", msg: e.message, kind: "error" }); setLoaded(true); });
    return () => { alive = false; };
  }, [file.id]);

  const isBinary = meta && /^(pdf|docx|image|jpeg|png)$/i.test(meta.file_type || '');
  const textChanged = text !== originalText;
  const charCount = text.length;
  const tooShort = text.trim().length > 0 && text.trim().length < 20;

  const submit = (e) => {
    e.preventDefault();
    if (tooShort) {
      toast({ title: "Texto muito curto", msg: "Mínimo 20 caracteres pra indexar.", kind: "info" });
      return;
    }
    setBusy(true);
    const body = { title, user_context: userContext };
    if (textChanged) body.extracted_text = text;
    body.doc_type = docType || null;
    body.source_weight = sourceWeight === '' ? null : parseInt(sourceWeight, 10);
    body.valid_until = validUntil || null;
    api('/api/admin/files/' + file.id, { method: 'PATCH', body })
      .then((r) => {
        const msg = r && r.reindexing
          ? 'Texto editado — reindexação iniciada em background.'
          : 'Metadados atualizados.';
        toast({ title: "Arquivo salvo", msg, kind: "success" });
        onSaved();
      })
      .catch((e) => { toast({ title: "Falha ao salvar", msg: e.message, kind: "error" }); setBusy(false); });
  };

  return (
    <div className="modal-overlay" onClick={onClose}>
      <form className="modal modal-wide modal-tall" onClick={(e) => e.stopPropagation()} onSubmit={submit}>
        <div className="modal-head">
          <h3><i className="fas fa-edit"></i> Editar arquivo
            {textChanged && <span className="badge b-am" style={{ marginLeft: 10, fontSize: 10 }}><i className="fas fa-circle"></i> alterado</span>}
          </h3>
          <button type="button" className="icon-btn" onClick={onClose}><i className="fas fa-times"></i></button>
        </div>
        <div className="modal-body">
          {!loaded ? (
            <div style={{ textAlign: 'center', padding: 30, color: 'var(--t3)' }}>
              <i className="fas fa-circle-notch fa-spin"></i> Carregando conteúdo…
            </div>
          ) : (
            <>
              {isBinary && (
                <div style={{
                  padding: '10px 14px',
                  background: 'rgba(245,158,11,.08)',
                  border: '1px solid rgba(245,158,11,.25)',
                  borderRadius: 'var(--r-input)',
                  fontSize: 12,
                  color: 'var(--t2)',
                  display: 'flex', gap: 10, alignItems: 'flex-start',
                }}>
                  <i className="fas fa-info-circle" style={{ color: 'var(--am)', paddingTop: 2 }}></i>
                  <div>
                    Este arquivo veio de um <strong>{(meta.file_type || '').toUpperCase()}</strong>. Editar o texto aqui
                    altera <strong>apenas o conteúdo indexado pela IA</strong> — o arquivo original em <code>uploads/</code>
                    fica intacto. Útil pra corrigir extração ruim sem precisar reenviar.
                  </div>
                </div>
              )}

              <div className="field">
                <label>Título / nome de exibição</label>
                <input className="input" value={title} onChange={(e) => setTitle(e.target.value)} required maxLength={500} />
                <span className="hint">Usado no painel e nas citações de fontes do bot.</span>
              </div>

              <div className="field">
                <label>Contexto adicional (opcional)</label>
                <textarea
                  className="input"
                  rows={3}
                  value={userContext}
                  onChange={(e) => setUserContext(e.target.value)}
                  placeholder="Ex.: Este documento contém o estatuto social do SINTRACON. Use quando perguntarem sobre estatuto, regras, deveres dos sócios…"
                />
                <span className="hint">Anexado ao texto extraído antes do chunking. Ajuda a IA a entender o intuito do arquivo.</span>
              </div>

              <div className="field">
                <label>Autoridade da fonte <span style={{ fontSize: 10.5, color: 'var(--t3)' }}>(prioriza no ranqueamento)</span></label>
                <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap' }}>
                  <select className="input" style={{ flex: '1 1 170px' }} value={docType} onChange={(e) => setDocType(e.target.value)}>
                    <option value="">— tipo —</option>
                    <option value="convencao">Convenção Coletiva</option>
                    <option value="estatuto">Estatuto</option>
                    <option value="oficial">Documento oficial</option>
                    <option value="site">Página do site</option>
                    <option value="faq">FAQ</option>
                    <option value="noticia">Notícia</option>
                    <option value="manual">Texto manual</option>
                  </select>
                  <input className="input" style={{ flex: '0 1 120px' }} type="number" min={0} max={100}
                    value={sourceWeight} onChange={(e) => setSourceWeight(e.target.value)} placeholder="peso 0–100" />
                  <input className="input" style={{ flex: '0 1 170px' }} type="date"
                    value={validUntil} onChange={(e) => setValidUntil(e.target.value)} title="Válido até (vazio = sem validade)" />
                </div>
                <span className="hint">Peso maior = mais priorizado (Convenção 100 › Notícia 55). "Válido até" no passado remove o documento da busca.</span>
              </div>

              <div className="field">
                <label>
                  Texto indexado
                  <span style={{ marginLeft: 'auto', fontSize: 10.5, color: 'var(--t3)', fontFamily: 'var(--ff-mono)' }}>
                    {charCount.toLocaleString('pt-BR')} caracteres
                    {meta && meta.chunk_count > 0 && <> · {meta.chunk_count} chunks atuais</>}
                  </span>
                </label>
                <textarea
                  className="input"
                  value={text}
                  onChange={(e) => setText(e.target.value)}
                  style={{ minHeight: 360, fontFamily: 'var(--ff-mono)', fontSize: 12.5, lineHeight: 1.6 }}
                  placeholder="Conteúdo que a IA consulta. Edite à vontade — ao salvar, os chunks antigos são apagados e o texto é re-indexado automaticamente."
                />
                <span className="hint">
                  {textChanged
                    ? <><i className="fas fa-bolt" style={{ color: 'var(--am)' }}></i> Ao salvar, os embeddings antigos serão apagados e o texto será re-chunkado e re-indexado (pode levar alguns segundos por chunk).</>
                    : <>Cada edição re-chunka e re-indexa o conteúdo. Markdown é preservado. Mínimo 20 chars, máximo 500.000.</>
                  }
                </span>
                {tooShort && (
                  <span style={{ fontSize: 11, color: 'var(--rd)' }}>
                    <i className="fas fa-exclamation-circle"></i> Texto muito curto — mínimo 20 caracteres.
                  </span>
                )}
              </div>
            </>
          )}
        </div>
        <div className="modal-foot">
          {textChanged && !busy && (
            <button type="button" className="btn btn-ghost btn-sm" onClick={() => setText(originalText)}>
              <i className="fas fa-undo"></i> Descartar mudanças
            </button>
          )}
          <button type="button" className="btn btn-ghost" onClick={onClose}>Cancelar</button>
          <button type="submit" className="btn btn-primary" disabled={busy || !loaded || tooShort}>
            {busy
              ? <><i className="fas fa-circle-notch fa-spin"></i> Salvando…</>
              : textChanged
                ? <><i className="fas fa-save"></i> Salvar e reindexar</>
                : <><i className="fas fa-save"></i> Salvar</>}
          </button>
        </div>
      </form>
    </div>
  );
}

// Relatório de duplicatas/versões — read-only + ações (excluir / abrir edição).
function DuplicatesModal({ onClose, onChanged, onEdit }) {
  const toast = useToast();
  const confirm = useConfirm();
  const [data, setData] = useState(null);
  const [busy, setBusy] = useState(null);

  const load = () => api('/api/admin/files/duplicates')
    .then(setData)
    .catch((e) => toast({ title: 'Falha ao detectar', msg: e.message, kind: 'error' }));
  useEffect(() => { load(); }, []);

  const fmtDate = (d) => d ? String(d).slice(0, 10) : '—';

  const del = async (f) => {
    const ok = await confirm({
      title: 'Apagar arquivo',
      message: <>Apagar <strong>"{f.original_name}"</strong> (#{f.id})?</>,
      details: 'Remove o arquivo e seus embeddings do banco vetorial.',
      confirmLabel: 'Apagar', danger: true, icon: 'fa-clone',
    });
    if (!ok) return;
    setBusy(f.id);
    try {
      await api('/api/admin/files/' + f.id, { method: 'DELETE' });
      toast({ title: 'Removido', kind: 'success' });
      await load();
      if (onChanged) onChanged();
    } catch (e) { toast({ title: 'Falha', msg: e.message, kind: 'error' }); }
    setBusy(null);
  };

  const FileRow = ({ f, tag }) => (
    <div className="row gap-sm" style={{ justifyContent: 'space-between', padding: '6px 0', borderBottom: '1px solid var(--b1)' }}>
      <div style={{ minWidth: 0 }}>
        <div style={{ fontSize: 12.5, fontWeight: 600, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
          {tag === 'keep' && <span className="badge b-gr" style={{ marginRight: 6 }}>manter</span>}
          {tag === 'old' && <span className="badge b-am" style={{ marginRight: 6 }}>antiga</span>}
          {f.original_name}
        </div>
        <div style={{ fontSize: 11, color: 'var(--t3)' }}>
          #{f.id} · {f.chunk_count || 0} chunks · {f.status} · {fmtDate(f.uploaded_at)}
          {f.valid_until ? <> · válido até {fmtDate(f.valid_until)}</> : null}
        </div>
      </div>
      <div className="row gap-sm" style={{ flexShrink: 0 }}>
        <button className="icon-btn" title="Editar (tipo/peso/validade)" onClick={() => onEdit(f)}><i className="fas fa-edit"></i></button>
        <button className="icon-btn" title="Excluir" disabled={busy === f.id} onClick={() => del(f)}>
          {busy === f.id ? <i className="fas fa-circle-notch fa-spin"></i> : <i className="fas fa-trash" style={{ color: 'var(--rd)' }}></i>}
        </button>
      </div>
    </div>
  );

  return (
    <div className="modal-overlay" onClick={onClose}>
      <div className="modal modal-wide modal-tall" onClick={(e) => e.stopPropagation()}>
        <div className="modal-head">
          <h3><i className="fas fa-clone"></i> Duplicatas e versões</h3>
          <button type="button" className="icon-btn" onClick={onClose}><i className="fas fa-times"></i></button>
        </div>
        <div className="modal-body">
          {!data ? (
            <div style={{ textAlign: 'center', padding: 30, color: 'var(--t3)' }}>
              <i className="fas fa-circle-notch fa-spin"></i> Varrendo arquivos…
            </div>
          ) : (
            <>
              <p style={{ fontSize: 12, color: 'var(--t3)' }}>
                {data.total_files} arquivos · {data.exact_groups} grupo(s) de cópia exata · {data.version_groups} grupo(s) de versão.
              </p>

              <h4 style={{ marginTop: 14 }}><i className="fas fa-copy" style={{ color: 'var(--rd)' }}></i> Cópias exatas — apague as repetidas (mantenha 1)</h4>
              {data.exact.length === 0
                ? <p style={{ fontSize: 12, color: 'var(--t3)' }}>Nenhuma cópia exata encontrada. 🎉</p>
                : data.exact.map((g, i) => (
                    <div key={'e' + i} className="card" style={{ padding: 10, marginBottom: 10 }}>
                      {g.files.map((f, j) => <FileRow key={f.id} f={f} tag={j === 0 ? 'keep' : null} />)}
                    </div>
                  ))}

              <h4 style={{ marginTop: 18 }}><i className="fas fa-layer-group" style={{ color: 'var(--am)' }}></i> Versões — marque "válido até" na antiga (ou apague)</h4>
              {data.versions.length === 0
                ? <p style={{ fontSize: 12, color: 'var(--t3)' }}>Nenhum grupo de versão detectado.</p>
                : data.versions.map((g, i) => (
                    <div key={'v' + i} className="card" style={{ padding: 10, marginBottom: 10 }}>
                      {g.files.map((f, j) => <FileRow key={f.id} f={f} tag={j === 0 ? 'keep' : 'old'} />)}
                    </div>
                  ))}

              <p style={{ fontSize: 11, color: 'var(--t3)', marginTop: 10 }}>
                "manter" = mais recente. Periódicos (A Tribuna, comunicados) podem aparecer aqui — são edições diferentes, não precisa apagar.
              </p>
            </>
          )}
        </div>
        <div className="modal-foot">
          <button type="button" className="btn btn-ghost" onClick={onClose}>Fechar</button>
        </div>
      </div>
    </div>
  );
}

// ---------- Pages ----------
function PagesPane() {
  const toast = useToast();
  const confirm = useConfirm();
  const [pages, setPages] = useState(Data.sitePages);
  const [editing, setEditing] = useState(null);
  const [crawlUrl, setCrawlUrl] = useState('');
  const [crawl, setCrawl] = useState(null);
  const [crawlBusy, setCrawlBusy] = useState(false);
  const reload = () => DataLoader.sitePages().then(() => setPages([...Data.sitePages]));
  useEffect(() => {
    let alive = true;
    DataLoader.sitePages().then(() => { if (alive) setPages([...Data.sitePages]); });
    return () => { alive = false; };
  }, []);
  const askDelete = async (p) => {
    const ok = await confirm({
      title: 'Apagar página do site',
      message: <>Apagar <strong>"{p.title || p.url}"</strong> da base?</>,
      details: 'A página deixa de ser recomendada pelo bot e seus embeddings são removidos. ' +
               'Para voltar a usá-la, será necessário recadastrar e reindexar.',
      confirmLabel: 'Apagar página',
      danger: true,
      icon: 'fa-globe',
    });
    if (!ok) return;
    api('/api/admin/site-pages/' + p.id, { method: 'DELETE' })
      .then(() => { toast({ title: "Página removida", kind: "success" }); reload(); })
      .catch((e) => toast({ title: "Falha ao remover", msg: e.message, kind: "error" }));
  };
  const reprocess = (p) => {
    api('/api/admin/site-pages/' + p.id + '/reprocess', { method: 'POST' })
      .then(() => { toast({ title: "Reprocessamento iniciado", msg: p.title || p.url, kind: "info" }); setTimeout(reload, 1500); })
      .catch((e) => toast({ title: "Falha ao reprocessar", msg: e.message, kind: "error" }));
  };

  // Spider do site inteiro: dispara e faz polling do progresso.
  const startCrawl = () => {
    const url = (crawlUrl || '').trim();
    if (!/^https?:\/\//i.test(url)) { toast({ title: "Informe a URL do site (https://…)", kind: "info" }); return; }
    setCrawlBusy(true);
    api('/api/admin/site-pages/crawl-site', { method: 'POST', body: { url } })
      .then(() => { toast({ title: "Varredura iniciada", msg: "Vasculhando o site inteiro…", kind: "success" });
                    setCrawl({ running: true, discovered: 1, visited: 0, indexed: 0, failed: 0, pages: [] }); })
      .catch((e) => toast({ title: "Falha ao iniciar", msg: e.message, kind: "error" }))
      .finally(() => setCrawlBusy(false));
  };
  useEffect(() => {
    if (!crawl || !crawl.running) return;
    const t = setInterval(() => {
      api('/api/admin/site-pages/crawl-status').then((s) => {
        setCrawl(s);
        if (!s.running) { clearInterval(t); reload(); }
      }).catch(() => {});
    }, 2500);
    return () => clearInterval(t);
  }, [crawl && crawl.running]);

  return (
    <>
      <div className="card" style={{ background: "var(--bg2)", border: "1px solid var(--b1)" }}>
        <div style={{ display: "flex", gap: 14, alignItems: "flex-start" }}>
          <div style={{ width: 32, height: 32, borderRadius: 8, background: "var(--og)", color: "var(--or)", display: "grid", placeItems: "center", flexShrink: 0 }}>
            <i className="fas fa-globe"></i>
          </div>
          <div style={{ fontSize: 12.5, color: "var(--t2)", lineHeight: 1.55, flex: 1 }}>
            Aqui você cadastra URLs do site SINTRACON e descreve <strong style={{ color: "var(--t1)" }}>o que cada página contém</strong>. Quando a pergunta do usuário combinar com a descrição, o bot recomenda a página citando o link.
            <div style={{ marginTop: 4, color: "var(--t3)", fontSize: 11.5 }}>
              Não é scraping — você descreve, a IA aprende quando recomendar. Mude a página? Atualize a descrição.
            </div>
          </div>
        </div>

        {/* Spider do site inteiro */}
        <div style={{ marginTop: 14, paddingTop: 14, borderTop: "1px solid var(--b1)" }}>
          <div style={{ display: "flex", gap: 10, alignItems: "center", flexWrap: "wrap" }}>
            <i className="fas fa-spider" style={{ color: "var(--or)" }}></i>
            <strong style={{ fontSize: 13, color: "var(--t1)" }}>Vasculhar o site inteiro</strong>
            <span style={{ fontSize: 11.5, color: "var(--t3)" }}>A IA segue os links do site e indexa TODO o conteúdo automaticamente.</span>
          </div>
          <div style={{ display: "flex", gap: 10, marginTop: 10, flexWrap: "wrap" }}>
            <input className="input" style={{ flex: "1 1 320px" }} placeholder="https://www.sintraconsp.org.br/"
              value={crawlUrl} onChange={(e) => setCrawlUrl(e.target.value)} />
            <button className="btn btn-primary" onClick={startCrawl} disabled={crawlBusy || (crawl && crawl.running)}>
              {(crawl && crawl.running)
                ? <><i className="fas fa-circle-notch fa-spin"></i> Vasculhando…</>
                : <><i className="fas fa-spider"></i> Vasculhar site</>}
            </button>
          </div>
          {crawl && (crawl.running || crawl.visited > 0) && (
            <div style={{ marginTop: 10, fontSize: 12, color: "var(--t2)" }}>
              <div>
                Descobertas: <strong>{crawl.discovered}</strong> · Visitadas: <strong>{crawl.visited}</strong> ·
                Indexadas: <strong style={{ color: "var(--gr)" }}>{crawl.indexed}</strong>
                {crawl.failed > 0 && <> · Falhas: {crawl.failed}</>}
              </div>
              {crawl.running && crawl.current && (
                <div style={{ fontSize: 11, color: "var(--t3)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
                  Agora: {crawl.current}
                </div>
              )}
              {!crawl.running && crawl.finishedAt && (
                <div style={{ color: "var(--gr)", fontSize: 11.5, marginTop: 2 }}>
                  <i className="fas fa-check"></i> Varredura concluída — {crawl.indexed} páginas indexadas. Já estão na lista abaixo.
                </div>
              )}
            </div>
          )}
        </div>
      </div>

      <div className="card flush">
        <div className="table-wrap">
          <table className="dt" style={{ tableLayout: 'fixed' }}>
            <colgroup>
              <col style={{ width: 260 }} />
              <col style={{ width: 320 }} />
              <col />
              <col style={{ width: 110 }} />
              <col style={{ width: 110 }} />
              <col style={{ width: 110 }} />
            </colgroup>
            <thead>
              <tr>
                <th>Título</th>
                <th>URL</th>
                <th>Descrição (intuito)</th>
                <th>Status</th>
                <th>Adicionada</th>
                <th style={{ textAlign: "right" }}>Ações</th>
              </tr>
            </thead>
            <tbody>
              {pages.map((p) => {
                const urlShort = (p.url || '').replace(/^https?:\/\//, '');
                const isFailed = p.status === 'failed' || p.status === 'error' || p.status === 'pending' || p.status === 'processing' || !p.status;
                return (
                <tr key={p.id}>
                  <td style={{ fontWeight: 600, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }} title={p.title || ''}>
                    {p.title || '—'}
                  </td>
                  <td style={{ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
                    {p.url ? (
                      <a href={p.url} target="_blank" rel="noopener" title={p.url} style={{ color: "var(--or)", fontSize: 12, fontFamily: "var(--ff-mono)" }}>
                        <i className="fas fa-external-link-alt" style={{ fontSize: 9, marginRight: 4 }}></i>
                        {urlShort}
                      </a>
                    ) : (
                      <span style={{ color: 'var(--t3)' }}>—</span>
                    )}
                  </td>
                  <td style={{ color: "var(--t2)", fontSize: 12.5, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }} title={p.desc || ''}>
                    {p.desc || '—'}
                  </td>
                  <td><StatusBadge status={p.status || 'pending'} /></td>
                  <td style={{ fontFamily: "var(--ff-mono)", fontSize: 11, color: "var(--t3)", whiteSpace: 'nowrap' }}>{p.date}</td>
                  <td>
                    <div className="row-actions">
                      <button className="icon-btn" onClick={() => reprocess(p)} title={isFailed ? "Reprocessar (recomendado)" : "Reprocessar"}>
                        <i className="fas fa-sync" style={isFailed ? { color: 'var(--am)' } : undefined}></i>
                      </button>
                      <button className="icon-btn" onClick={() => setEditing(p)} title="Editar"><i className="fas fa-edit"></i></button>
                      <button className="icon-btn" style={{ color: "var(--rd)" }} onClick={() => askDelete(p)} title="Apagar"><i className="fas fa-trash"></i></button>
                    </div>
                  </td>
                </tr>
                );
              })}
            </tbody>
          </table>
        </div>
      </div>

      {editing && <PageEditModal page={editing} onClose={() => setEditing(null)} onSaved={() => { setEditing(null); reload(); }} />}
    </>
  );
}

// ---- Modal: editar página do site ----
function PageEditModal({ page, onClose, onSaved }) {
  const toast = useToast();
  const [vals, setVals] = useState({
    title: page.title || '',
    url: page.url || '',
    description: page.desc || '',
  });
  const [busy, setBusy] = useState(false);
  const [showAIImprove, setShowAIImprove] = useState(false);
  const setV = (k, v) => setVals((s) => ({ ...s, [k]: v }));
  const charCount = vals.description.length;

  const submit = (e) => {
    e.preventDefault();
    if (!vals.url || !/^https?:\/\//i.test(vals.url)) {
      toast({ title: "URL inválida", msg: "Use http:// ou https://", kind: "info" });
      return;
    }
    setBusy(true);
    api('/api/admin/site-pages/' + page.id, {
      method: 'PATCH',
      body: {
        title: vals.title,
        url: vals.url,
        description: vals.description,
      },
    })
      .then(() => { toast({ title: "Página atualizada", msg: "Reindexação automática", kind: "success" }); onSaved(); })
      .catch((e) => { toast({ title: "Falha ao salvar", msg: e.message, kind: "error" }); setBusy(false); });
  };

  return (
    <div className="modal-overlay" onClick={onClose}>
      <form className="modal modal-wide" onClick={(e) => e.stopPropagation()} onSubmit={submit}>
        <div className="modal-head">
          <h3><i className="fas fa-edit"></i> Editar página do site</h3>
          <button type="button" className="icon-btn" onClick={onClose}><i className="fas fa-times"></i></button>
        </div>
        <div className="modal-body">
          <div className="field">
            <label>URL completa</label>
            <input className="input" type="url" value={vals.url} onChange={(e) => setV('url', e.target.value)} required />
          </div>
          <div className="field">
            <label>Título</label>
            <input className="input" value={vals.title} onChange={(e) => setV('title', e.target.value)} />
          </div>
          <div className="field">
            <label style={{ display: 'flex', alignItems: 'center' }}>
              <span>Descrição / intuito da página</span>
              <span style={{ marginLeft: 'auto', fontSize: 10.5, color: 'var(--t3)', fontFamily: 'var(--ff-mono)', marginRight: 8 }}>{charCount} caracteres</span>
              <button
                type="button"
                className="btn btn-soft btn-sm"
                onClick={() => setShowAIImprove(true)}
                disabled={vals.description.trim().length < 50}
                title={vals.description.trim().length < 50 ? 'Mínimo 50 caracteres' : 'Pré-processar com IA pra melhorar retrieval'}
              >
                <i className="fas fa-wand-magic-sparkles" style={{ color: 'var(--or)' }}></i> Melhorar com IA
              </button>
            </label>
            <textarea
              className="input"
              rows={10}
              value={vals.description}
              onChange={(e) => setV('description', e.target.value)}
              placeholder="Descreva detalhadamente o que tem na página, quais perguntas ela responde, em que contexto a IA deve recomendá-la, palavras-chave importantes, exemplos…"
              required
            ></textarea>
            <span className="hint">
              Escreva um texto rico — quanto mais contexto, melhor a IA recomenda a página certa.
              Sem limite prático (até 20.000 caracteres). Mínimo: 10 caracteres.
            </span>
          </div>
        </div>
        <div className="modal-foot">
          <button type="button" className="btn btn-ghost" onClick={onClose}>Cancelar</button>
          <button type="submit" className="btn btn-primary" disabled={busy}>
            {busy ? <><i className="fas fa-circle-notch fa-spin"></i> Salvando…</> : <><i className="fas fa-save"></i> Salvar alterações</>}
          </button>
        </div>
      </form>
      {showAIImprove && window.AIImproveModal && (
        <window.AIImproveModal
          originalText={vals.description}
          sourceHint="url"
          filename={vals.title || vals.url}
          onAccept={(improved) => { setV('description', improved); toast({ title: 'Descrição atualizada', msg: 'Revise antes de salvar.', kind: 'success' }); }}
          onClose={() => setShowAIImprove(false)}
        />
      )}
    </div>
  );
}

window.ScreenKnowledge = ScreenKnowledge;
