// System screen — sub: config / diag / api
function ScreenSystem({ route, onAction, version }) {
  const sub = route.split(":")[1] || "config";
  const toast = useToast();
  const [status, setStatus] = useState({ uptime: '', version: version || '' });

  useEffect(() => {
    api('/api/admin/status').then((s) => {
      if (!s) return;
      setStatus({
        uptime: s.uptime_human || s.uptime || '',
        version: s.version || version || '',
      });
    }).catch(() => {});
  }, [version]);

  const tabs = [
    { id: "config",  label: "Geral",             icon: "fa-sliders-h" },
    { id: "acessos", label: "Acessos",           icon: "fa-user-shield" },
    { id: "smtp",    label: "SMTP",              icon: "fa-envelope" },
    { id: "terms",   label: "Termos de IA",      icon: "fa-file-contract" },
    { id: "diag",    label: "Diagnóstico",       icon: "fa-stethoscope" },
    { id: "api",     label: "API & Integração",  icon: "fa-code" },
  ];
  const cur = tabs.find((t) => t.id === sub) || tabs[0];

  return (
    <div className="page" data-screen-label={"Sistema · " + cur.label}>
      <div className="page-header">
        <div>
          <h1>
            <span className="h1-icon"><i className="fas fa-sliders-h"></i></span>
            Sistema
          </h1>
          <p className="sub">Infraestrutura, manutenção e integração — coisas que mudam pouco no dia a dia.</p>
        </div>
        <div className="page-header-actions">
          <span className="badge b-gr"><span className="dot"></span> {status.version || 'v3'} {status.uptime && ('· online há ' + status.uptime)}</span>
        </div>
      </div>

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

      {sub === "config"  && <ConfigPane />}
      {sub === "acessos" && <AcessosPane />}
      {sub === "smtp"    && <SmtpPane />}
      {sub === "terms"   && <TermsPane />}
      {sub === "diag"    && <DiagPane />}
      {sub === "api"     && <ApiPane />}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────
// AcessosPane — gerenciamento de 2FA + (futuro) usuários + perfil próprio
// Reorganização da v3.23: SecurityCard era em ConfigPane, agora vive aqui.
// Cards de "Usuários" e "Meu perfil" entram no Deploy 2.
// ─────────────────────────────────────────────────────────────────────────
function AcessosPane() {
  // MyProfile + 2FA são sempre visíveis (cada user mexe no próprio).
  // UsersCard só pra owner (CRUD de outros usuários).
  return (
    <>
      <MyProfileCard />
      {Auth.userCan('manage_users') && <UsersCard />}
      <SecurityCard />
      {!Auth.userCan('manage_users') && (
        <div className="card" style={{ background: 'var(--bg1)' }}>
          <div style={{ padding: 14, textAlign: 'center', color: 'var(--t3)', fontSize: 12 }}>
            <i className="fas fa-lock" style={{ marginRight: 6 }}></i>
            Gerenciamento de usuários disponível só pra <strong>owner</strong>.
            Sua role atual: <code>{Auth.userRole || 'admin'}</code>.
          </div>
        </div>
      )}
    </>
  );
}

// ─────────────────────────────────────────────────────────────────────────
// MyProfileCard — usuário edita o próprio perfil (nome, e-mail, senha).
// Pra trocar senha precisa digitar a atual (defesa contra token comprometido).
// ─────────────────────────────────────────────────────────────────────────
function MyProfileCard() {
  const toast = useToast();
  const [me, setMe]       = useState(null);
  const [vals, setVals]   = useState({ full_name: '', email: '' });
  const [pw, setPw]       = useState({ current: '', next: '', confirm: '' });
  const [savingProfile, setSavingProfile]   = useState(false);
  const [savingPw, setSavingPw]   = useState(false);
  const [showPwSection, setShowPwSection] = useState(false);

  const reload = () => api('/api/admin/me').then((r) => {
    if (r && r.user) {
      setMe(r.user);
      setVals({ full_name: r.user.full_name || '', email: r.user.email || '' });
    }
  }).catch(() => {});

  useEffect(() => { reload(); }, []);

  const saveProfile = () => {
    setSavingProfile(true);
    api('/api/admin/me', {
      method: 'PATCH',
      body: { full_name: vals.full_name, email: vals.email },
    })
      .then(() => { toast({ title: 'Perfil salvo', kind: 'success' }); reload(); })
      .catch((e) => toast({ title: 'Falha', msg: e.message, kind: 'error' }))
      .finally(() => setSavingProfile(false));
  };

  const savePw = () => {
    if (pw.next !== pw.confirm) {
      toast({ title: 'Senhas não conferem', kind: 'error' });
      return;
    }
    if (pw.next.length < 8) {
      toast({ title: 'Senha muito curta', msg: 'Mínimo 8 caracteres.', kind: 'error' });
      return;
    }
    setSavingPw(true);
    api('/api/admin/me', {
      method: 'PATCH',
      body: { current_password: pw.current, new_password: pw.next },
    })
      .then(() => {
        toast({ title: 'Senha alterada', msg: 'Dispositivos confiáveis foram revogados.', kind: 'success' });
        setPw({ current: '', next: '', confirm: '' });
        setShowPwSection(false);
      })
      .catch((e) => toast({ title: 'Falha', msg: e.message, kind: 'error' }))
      .finally(() => setSavingPw(false));
  };

  return (
    <div className="card">
      <div className="card-header">
        <h3><i className="fas fa-user-pen" style={{ color: 'var(--or)' }}></i> Meu perfil</h3>
        {me && <span className="meta">@{me.username} · {me.role}</span>}
      </div>

      <div className="form-row">
        <div className="field">
          <label>Nome completo</label>
          <input
            className="input"
            value={vals.full_name}
            onChange={(e) => setVals((s) => ({ ...s, full_name: e.target.value }))}
            placeholder="Seu nome"
            maxLength={100}
          />
        </div>
        <div className="field">
          <label>E-mail</label>
          <input
            type="email"
            className="input"
            value={vals.email}
            onChange={(e) => setVals((s) => ({ ...s, email: e.target.value }))}
            placeholder="voce@empresa.com"
            maxLength={150}
          />
          <span className="hint">Usado pra recuperação de senha e notificações (quando SMTP estiver configurado).</span>
        </div>
      </div>

      <div className="row" style={{ justifyContent: 'space-between', alignItems: 'center', marginTop: 8 }}>
        <button
          type="button"
          className="btn btn-ghost btn-sm"
          onClick={() => setShowPwSection((v) => !v)}
        >
          <i className={'fas ' + (showPwSection ? 'fa-chevron-up' : 'fa-key')}></i>
          {showPwSection ? ' Esconder troca de senha' : ' Trocar senha'}
        </button>
        <button className="btn btn-primary btn-sm" onClick={saveProfile} disabled={savingProfile}>
          {savingProfile ? <><i className="fas fa-circle-notch fa-spin"></i> Salvando…</> : <><i className="fas fa-save"></i> Salvar perfil</>}
        </button>
      </div>

      {showPwSection && (
        <div style={{ marginTop: 14, padding: 14, background: 'var(--bg1)', border: '1px solid var(--b1)', borderRadius: 'var(--r-input)' }}>
          <div style={{ fontSize: 12, color: 'var(--t3)', marginBottom: 8 }}>
            <i className="fas fa-info-circle"></i> Após mudar a senha, todos os dispositivos confiáveis (lembrar 30 dias) são revogados.
          </div>
          <div className="form-row">
            <div className="field">
              <label>Senha atual</label>
              <input type="password" className="input" value={pw.current} onChange={(e) => setPw((s) => ({ ...s, current: e.target.value }))} autoComplete="current-password" />
            </div>
            <div className="field">
              <label>Nova senha</label>
              <input type="password" className="input" value={pw.next} onChange={(e) => setPw((s) => ({ ...s, next: e.target.value }))} autoComplete="new-password" minLength={8} />
              <span className="hint">Mínimo 8 caracteres.</span>
            </div>
            <div className="field">
              <label>Confirmar nova senha</label>
              <input type="password" className="input" value={pw.confirm} onChange={(e) => setPw((s) => ({ ...s, confirm: e.target.value }))} autoComplete="new-password" minLength={8} />
            </div>
          </div>
          <div className="row" style={{ justifyContent: 'flex-end' }}>
            <button className="btn btn-primary btn-sm" onClick={savePw} disabled={savingPw || !pw.current || !pw.next || !pw.confirm}>
              {savingPw ? <><i className="fas fa-circle-notch fa-spin"></i> Trocando…</> : <><i className="fas fa-key"></i> Trocar senha</>}
            </button>
          </div>
        </div>
      )}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────
// UsersCard — lista, criar, editar, desativar outros admins.
// RBAC enforcement (filtrar por role) vem no Deploy 5 (ROADMAP). Por
// enquanto, qualquer admin autenticado pode mexer em outros.
// ─────────────────────────────────────────────────────────────────────────
function UsersCard() {
  const toast = useToast();
  const confirm = useConfirm();
  const [users, setUsers]   = useState([]);
  const [loading, setLoading] = useState(true);
  const [showCreate, setShowCreate] = useState(false);
  const [showInvite, setShowInvite] = useState(false);
  const [editing, setEditing] = useState(null);

  const reload = () => api('/api/admin/users')
    .then((r) => { setUsers((r && r.users) || []); setLoading(false); })
    .catch(() => setLoading(false));

  useEffect(() => { reload(); }, []);

  const askDelete = async (u) => {
    const ok = await confirm({
      title: 'Desativar usuário',
      message: <>Desativar <strong>@{u.username}</strong>?</>,
      details: 'Usuário não conseguirá fazer login. Sessões e dispositivos confiáveis são revogados imediatamente. Pode reativar depois.',
      confirmLabel: 'Desativar',
      danger: true,
      icon: 'fa-user-slash',
    });
    if (!ok) return;
    api('/api/admin/users/' + u.id, { method: 'DELETE' })
      .then(() => { toast({ title: 'Desativado', kind: 'success' }); reload(); })
      .catch((e) => toast({ title: 'Falha', msg: e.message, kind: 'error' }));
  };

  const reactivate = async (u) => {
    api('/api/admin/users/' + u.id, { method: 'PATCH', body: { is_active: true } })
      .then(() => { toast({ title: 'Reativado', kind: 'success' }); reload(); })
      .catch((e) => toast({ title: 'Falha', msg: e.message, kind: 'error' }));
  };

  const resendInvite = async (u) => {
    api('/api/admin/users/' + u.id + '/resend-invite', { method: 'POST' })
      .then((r) => {
        if (r.email_sent) {
          toast({ title: 'Convite reenviado', msg: `Pra ${u.email}`, kind: 'success' });
        } else {
          // Backend não conseguiu enviar — devolveu manual_link
          toast({ title: 'E-mail falhou', msg: 'Use o link manual: ' + (r.manual_link || ''), kind: 'info' });
        }
      })
      .catch((e) => toast({ title: 'Falha', msg: e.message, kind: 'error' }));
  };

  return (
    <div className="card">
      <div className="card-header">
        <h3><i className="fas fa-users-cog" style={{ color: 'var(--or)' }}></i> Usuários ({users.length})</h3>
        <div style={{ display: 'flex', gap: 8 }}>
          <button className="btn btn-soft btn-sm" onClick={() => setShowInvite(true)} title="Enviar convite por e-mail (requer SMTP configurado)">
            <i className="fas fa-envelope"></i> Convidar
          </button>
          <button className="btn btn-primary btn-sm" onClick={() => setShowCreate(true)}>
            <i className="fas fa-plus"></i> Novo usuário
          </button>
        </div>
      </div>

      {loading ? (
        <div style={{ padding: 30, textAlign: 'center', color: 'var(--t3)' }}>
          <i className="fas fa-circle-notch fa-spin"></i> Carregando…
        </div>
      ) : users.length === 0 ? (
        <div className="empty" style={{ padding: 20 }}>
          <i className="fas fa-users"></i>
          <p>Nenhum usuário cadastrado ainda.</p>
        </div>
      ) : (
        <div className="table-wrap" style={{ marginTop: 4 }}>
          <table className="dt">
            <thead>
              <tr>
                <th>Usuário</th>
                <th style={{ width: 200 }}>Nome / E-mail</th>
                <th style={{ width: 100 }}>Role</th>
                <th style={{ width: 80 }}>2FA</th>
                <th style={{ width: 100 }}>Status</th>
                <th style={{ width: 130 }}>Último login</th>
                <th style={{ width: 100, textAlign: 'right' }}>Ações</th>
              </tr>
            </thead>
            <tbody>
              {users.map((u) => (
                <tr key={u.id}>
                  <td><code style={{ fontSize: 12 }}>@{u.username}</code></td>
                  <td>
                    <div style={{ fontSize: 12 }}>{u.full_name || '—'}</div>
                    {u.email && <div style={{ fontSize: 11, color: 'var(--t3)' }}>{u.email}</div>}
                  </td>
                  <td>
                    <span className={'badge ' + (u.role === 'owner' ? 'b-or' : u.role === 'admin' ? 'b-bl' : u.role === 'editor' ? 'b-am' : 'b-mut')}>{u.role}</span>
                  </td>
                  <td>
                    {u.totp_enabled
                      ? <span style={{ color: 'var(--gr)' }}><i className="fas fa-shield-halved"></i> ativo</span>
                      : <span style={{ color: 'var(--t3)', fontSize: 11 }}>—</span>}
                  </td>
                  <td>
                    {u.is_active
                      ? <span className="badge b-gr"><span className="dot"></span> ativo</span>
                      : <span className="badge b-mut">desativado</span>}
                  </td>
                  <td style={{ fontSize: 11, color: 'var(--t3)', fontFamily: 'var(--ff-mono)' }}>
                    {u.last_login_at ? new Date(u.last_login_at).toLocaleString('pt-BR', { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' }) : '—'}
                  </td>
                  <td style={{ textAlign: 'right' }}>
                    {!u.is_active && u.email && u.role !== 'owner' && (
                      <button className="icon-btn" style={{ color: 'var(--or)' }} onClick={() => resendInvite(u)} title="Reenviar convite por e-mail"><i className="fas fa-envelope"></i></button>
                    )}
                    <button className="icon-btn" onClick={() => setEditing(u)} title="Editar"><i className="fas fa-edit"></i></button>
                    {u.role !== 'owner' && (u.is_active
                      ? <button className="icon-btn" style={{ color: 'var(--rd)' }} onClick={() => askDelete(u)} title="Desativar"><i className="fas fa-user-slash"></i></button>
                      : <button className="icon-btn" style={{ color: 'var(--gr)' }} onClick={() => reactivate(u)} title="Reativar"><i className="fas fa-user-check"></i></button>
                    )}
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      )}

      {showCreate && (
        <UserCreateModal
          onClose={() => setShowCreate(false)}
          onCreated={() => { setShowCreate(false); reload(); }}
        />
      )}
      {showInvite && (
        <UserInviteModal
          onClose={() => setShowInvite(false)}
          onCreated={() => { setShowInvite(false); reload(); }}
        />
      )}
      {editing && (
        <UserEditModal
          user={editing}
          onClose={() => setEditing(null)}
          onSaved={() => { setEditing(null); reload(); }}
        />
      )}
    </div>
  );
}

function UserInviteModal({ onClose, onCreated }) {
  const toast = useToast();
  const [vals, setVals] = useState({ username: '', full_name: '', email: '', role: 'admin' });
  const [busy, setBusy] = useState(false);
  const [manualLink, setManualLink] = useState(null); // se SMTP falhar
  const setV = (k, v) => setVals((s) => ({ ...s, [k]: v }));

  const submit = (e) => {
    e.preventDefault();
    setBusy(true);
    api('/api/admin/users/invite', { method: 'POST', body: vals })
      .then((r) => {
        if (r.email_sent === false && r.manual_link) {
          // SMTP falhou — admin precisa enviar o link manualmente
          setManualLink(r.manual_link);
          setBusy(false);
        } else {
          toast({ title: 'Convite enviado', msg: `Pra ${vals.email}`, kind: 'success' });
          onCreated();
        }
      })
      .catch((e) => { toast({ title: 'Falha', msg: e.message, kind: 'error' }); setBusy(false); });
  };

  const copyLink = () => {
    navigator.clipboard.writeText(manualLink).then(() => toast({ title: 'Link copiado', kind: 'success' }));
  };

  return (
    <div className="modal-overlay" onClick={onClose}>
      <form className="modal" onClick={(e) => e.stopPropagation()} onSubmit={submit}>
        <div className="modal-head">
          <h3><i className="fas fa-envelope-open-text"></i> Convidar novo usuário</h3>
          <button type="button" className="icon-btn" onClick={onClose}><i className="fas fa-times"></i></button>
        </div>
        <div className="modal-body">
          {manualLink ? (
            <>
              <div style={{ padding: '12px 14px', background: 'rgba(245,158,11,0.08)', border: '1px solid var(--am)', borderRadius: 'var(--r-input)', fontSize: 12, color: 'var(--t1)', marginBottom: 12 }}>
                <strong style={{ color: 'var(--am)' }}><i className="fas fa-exclamation-triangle"></i> SMTP não enviou o e-mail.</strong>
                {' '}Usuário foi criado, mas você precisa mandar o link manualmente:
              </div>
              <div className="field">
                <label>Link do convite</label>
                <textarea readOnly className="input" value={manualLink} rows={3} style={{ fontFamily: 'var(--ff-mono)', fontSize: 11, wordBreak: 'break-all' }} onClick={(e) => e.target.select()} />
              </div>
              <button type="button" className="btn btn-soft btn-sm" onClick={copyLink}>
                <i className="fas fa-copy"></i> Copiar link
              </button>
            </>
          ) : (
            <>
              <div style={{ padding: '10px 12px', background: 'var(--bg1)', border: '1px solid var(--b1)', borderRadius: 'var(--r-input)', fontSize: 12, color: 'var(--t2)', marginBottom: 12 }}>
                <i className="fas fa-info-circle" style={{ color: 'var(--bl)', marginRight: 4 }}></i>
                O usuário recebe um e-mail com link pra <strong>definir a própria senha</strong> e entrar no painel. Convite expira em 7 dias.
              </div>
              <div className="form-row">
                <div className="field">
                  <label>Username</label>
                  <input className="input" value={vals.username} onChange={(e) => setV('username', e.target.value.toLowerCase())} required pattern="[a-z0-9_.-]{3,32}" placeholder="ex: maria.silva" autoFocus />
                </div>
                <div className="field">
                  <label>Role</label>
                  <select className="input" value={vals.role} onChange={(e) => setV('role', e.target.value)}>
                    <option value="admin">admin</option>
                    <option value="editor">editor</option>
                    <option value="viewer">viewer</option>
                  </select>
                </div>
              </div>
              <div className="field">
                <label>Nome completo (recomendado)</label>
                <input className="input" value={vals.full_name} onChange={(e) => setV('full_name', e.target.value)} maxLength={100} />
              </div>
              <div className="field">
                <label>E-mail *</label>
                <input type="email" className="input" value={vals.email} onChange={(e) => setV('email', e.target.value)} required placeholder="usuario@empresa.com" maxLength={150} />
                <span className="hint">Obrigatório — convite vai pra este endereço.</span>
              </div>
            </>
          )}
        </div>
        <div className="modal-foot">
          <button type="button" className="btn btn-ghost" onClick={onClose}>{manualLink ? 'Fechar' : 'Cancelar'}</button>
          {!manualLink && (
            <button type="submit" className="btn btn-primary" disabled={busy}>
              {busy ? <><i className="fas fa-circle-notch fa-spin"></i> Enviando…</> : <><i className="fas fa-paper-plane"></i> Enviar convite</>}
            </button>
          )}
          {manualLink && (
            <button type="button" className="btn btn-primary" onClick={onCreated}>
              <i className="fas fa-check"></i> Concluído
            </button>
          )}
        </div>
      </form>
    </div>
  );
}

function UserCreateModal({ onClose, onCreated }) {
  const toast = useToast();
  const [vals, setVals] = useState({ username: '', full_name: '', email: '', password: '', role: 'admin' });
  const [busy, setBusy] = useState(false);
  const setV = (k, v) => setVals((s) => ({ ...s, [k]: v }));

  const submit = (e) => {
    e.preventDefault();
    setBusy(true);
    api('/api/admin/users', { method: 'POST', body: vals })
      .then(() => { toast({ title: 'Usuário criado', msg: `@${vals.username}`, kind: 'success' }); onCreated(); })
      .catch((e) => { toast({ title: 'Falha', msg: e.message, kind: 'error' }); setBusy(false); });
  };

  return (
    <div className="modal-overlay" onClick={onClose}>
      <form className="modal" onClick={(e) => e.stopPropagation()} onSubmit={submit}>
        <div className="modal-head">
          <h3><i className="fas fa-user-plus"></i> Novo usuário</h3>
          <button type="button" className="icon-btn" onClick={onClose}><i className="fas fa-times"></i></button>
        </div>
        <div className="modal-body">
          <div className="form-row">
            <div className="field">
              <label>Username</label>
              <input className="input" value={vals.username} onChange={(e) => setV('username', e.target.value.toLowerCase())} required pattern="[a-z0-9_.-]{3,32}" placeholder="ex: maria.silva" autoFocus />
              <span className="hint">3-32 chars, minúsculas, números e ._-</span>
            </div>
            <div className="field">
              <label>Role</label>
              <select className="input" value={vals.role} onChange={(e) => setV('role', e.target.value)}>
                <option value="admin">admin — acesso total</option>
                <option value="editor">editor — edita conteúdo</option>
                <option value="viewer">viewer — só leitura</option>
              </select>
              <span className="hint">Enforcement de role no middleware vem em fase futura. Por enquanto, todos têm acesso total.</span>
            </div>
          </div>
          <div className="field">
            <label>Nome completo (opcional)</label>
            <input className="input" value={vals.full_name} onChange={(e) => setV('full_name', e.target.value)} maxLength={100} />
          </div>
          <div className="field">
            <label>E-mail (opcional, recomendado)</label>
            <input type="email" className="input" value={vals.email} onChange={(e) => setV('email', e.target.value)} placeholder="usuario@empresa.com" maxLength={150} />
            <span className="hint">Necessário pra recuperação de senha e convites quando SMTP estiver configurado.</span>
          </div>
          <div className="field">
            <label>Senha inicial</label>
            <input type="password" className="input" value={vals.password} onChange={(e) => setV('password', e.target.value)} required minLength={8} />
            <span className="hint">Mínimo 8 chars. O usuário pode trocar depois em Meu Perfil.</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> Criando…</> : <><i className="fas fa-user-plus"></i> Criar usuário</>}
          </button>
        </div>
      </form>
    </div>
  );
}

function UserEditModal({ user, onClose, onSaved }) {
  const toast = useToast();
  const [vals, setVals] = useState({
    full_name: user.full_name || '',
    email: user.email || '',
    role: user.role,
    is_active: user.is_active,
  });
  const [resetPw, setResetPw] = useState('');
  const [busy, setBusy] = useState(false);
  const setV = (k, v) => setVals((s) => ({ ...s, [k]: v }));
  const isOwner = user.role === 'owner';

  const submit = (e) => {
    e.preventDefault();
    setBusy(true);
    const body = {
      full_name: vals.full_name,
      email: vals.email,
    };
    if (!isOwner) {
      body.role = vals.role;
      body.is_active = vals.is_active;
    }
    if (resetPw.trim().length >= 8) body.password = resetPw;
    api('/api/admin/users/' + user.id, { method: 'PATCH', body })
      .then(() => { toast({ title: 'Salvo', kind: 'success' }); onSaved(); })
      .catch((e) => { toast({ title: 'Falha', msg: e.message, kind: 'error' }); setBusy(false); });
  };

  return (
    <div className="modal-overlay" onClick={onClose}>
      <form className="modal" onClick={(e) => e.stopPropagation()} onSubmit={submit}>
        <div className="modal-head">
          <h3><i className="fas fa-user-pen"></i> Editar @{user.username}</h3>
          <button type="button" className="icon-btn" onClick={onClose}><i className="fas fa-times"></i></button>
        </div>
        <div className="modal-body">
          {isOwner && (
            <div style={{ padding: '8px 12px', background: 'rgba(245,158,11,0.08)', border: '1px solid var(--am)', borderRadius: 'var(--r-input)', fontSize: 11.5, marginBottom: 12 }}>
              <i className="fas fa-shield" style={{ color: 'var(--am)', marginRight: 4 }}></i>
              <strong>Owner</strong> — role e status não podem ser alterados (proteção do admin original).
            </div>
          )}
          <div className="form-row">
            <div className="field">
              <label>Nome completo</label>
              <input className="input" value={vals.full_name} onChange={(e) => setV('full_name', e.target.value)} maxLength={100} />
            </div>
            <div className="field">
              <label>E-mail</label>
              <input type="email" className="input" value={vals.email} onChange={(e) => setV('email', e.target.value)} maxLength={150} />
            </div>
          </div>
          <div className="form-row">
            <div className="field">
              <label>Role</label>
              <select className="input" value={vals.role} onChange={(e) => setV('role', e.target.value)} disabled={isOwner}>
                {isOwner && <option value="owner">owner</option>}
                <option value="admin">admin</option>
                <option value="editor">editor</option>
                <option value="viewer">viewer</option>
              </select>
            </div>
            <div className="field" style={{ alignItems: 'center' }}>
              <label style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                <Switch on={vals.is_active} onChange={(v) => setV('is_active', v)} disabled={isOwner} />
                <span>{vals.is_active ? 'Ativo' : 'Desativado'}</span>
              </label>
            </div>
          </div>
          <div className="field">
            <label>Resetar senha (opcional)</label>
            <input type="password" className="input" value={resetPw} onChange={(e) => setResetPw(e.target.value)} placeholder="Deixe em branco pra não alterar" minLength={8} />
            <span className="hint">Se preenchido, sobrescreve a senha do usuário e revoga sessões dele. Mínimo 8 chars.</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</>}
          </button>
        </div>
      </form>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────
// SmtpPane — config SMTP + teste de conexão + envio de e-mail de teste.
// Senha do SMTP é sensível: GET retorna '***' quando preenchida; PATCH
// ignora '***' pra não sobrescrever.
// ─────────────────────────────────────────────────────────────────────────
function SmtpPane() {
  const toast = useToast();
  const [cfg, setCfg] = useState({
    smtp_host: '', smtp_port: '587', smtp_secure: 'false',
    smtp_user: '', smtp_password: '',
    smtp_from_name: '', smtp_from_email: '',
  });
  const [saving, setSaving]   = useState(false);
  const [verifying, setVerifying] = useState(false);
  const [sending, setSending] = useState(false);
  const [testTo, setTestTo]   = useState('');
  const upd = (k, v) => setCfg((s) => ({ ...s, [k]: v }));
  const canEdit = Auth.userCan('edit_smtp');

  const reload = () => api('/api/admin/system-config')
    .then((r) => {
      if (!r) return;
      setCfg({
        smtp_host:       r.smtp_host       || '',
        smtp_port:       r.smtp_port       || '587',
        smtp_secure:     r.smtp_secure     || 'false',
        smtp_user:       r.smtp_user       || '',
        smtp_password:   r.smtp_password   || '', // backend retorna '***' se setada
        smtp_from_name:  r.smtp_from_name  || '',
        smtp_from_email: r.smtp_from_email || '',
      });
    })
    .catch(() => {});

  useEffect(() => { reload(); }, []);

  const save = () => {
    setSaving(true);
    api('/api/admin/system-config', {
      method: 'PATCH',
      body: {
        smtp_host: cfg.smtp_host,
        smtp_port: cfg.smtp_port,
        smtp_secure: cfg.smtp_secure,
        smtp_user: cfg.smtp_user,
        smtp_password: cfg.smtp_password, // se valor é '***', backend mantém o atual
        smtp_from_name: cfg.smtp_from_name,
        smtp_from_email: cfg.smtp_from_email,
      },
    })
      .then(() => { toast({ title: 'SMTP salvo', kind: 'success' }); reload(); })
      .catch((e) => toast({ title: 'Falha', msg: e.message, kind: 'error' }))
      .finally(() => setSaving(false));
  };

  const verifyConn = () => {
    setVerifying(true);
    api('/api/admin/smtp/verify', { method: 'POST' })
      .then((r) => toast({ title: 'Conexão OK', msg: `${r.host}:${r.port} (${r.secure ? 'SSL' : 'STARTTLS'})`, kind: 'success' }))
      .catch((e) => toast({ title: 'Falha na conexão', msg: e.message, kind: 'error' }))
      .finally(() => setVerifying(false));
  };

  const sendTest = () => {
    setSending(true);
    api('/api/admin/smtp/send-test', { method: 'POST', body: testTo ? { to: testTo } : {} })
      .then((r) => toast({ title: 'E-mail de teste enviado', msg: `Pra ${r.to}. Verifique a caixa.`, kind: 'success' }))
      .catch((e) => toast({ title: 'Falha ao enviar', msg: e.message, kind: 'error' }))
      .finally(() => setSending(false));
  };

  const configured = cfg.smtp_host && cfg.smtp_from_email;

  if (!canEdit) {
    return (
      <div className="card" style={{ background: 'var(--bg1)' }}>
        <div style={{ padding: 30, textAlign: 'center', color: 'var(--t3)' }}>
          <i className="fas fa-lock" style={{ fontSize: 28, marginBottom: 10, color: 'var(--t3)' }}></i>
          <h4 style={{ margin: '0 0 6px', fontSize: 14, color: 'var(--t2)' }}>Sem permissão</h4>
          <p style={{ fontSize: 12, margin: 0, lineHeight: 1.55 }}>
            Configuração SMTP disponível só pra <strong>admin</strong> ou <strong>owner</strong>.
            Sua role atual: <code>{Auth.userRole || 'admin'}</code>.
          </p>
        </div>
      </div>
    );
  }

  return (
    <>
      <div className="card">
        <div className="card-header">
          <h3><i className="fas fa-envelope" style={{ color: 'var(--or)' }}></i> Servidor SMTP</h3>
          <span className="meta">
            {configured
              ? <span style={{ color: 'var(--gr)' }}><i className="fas fa-check-circle"></i> Configurado</span>
              : <span style={{ color: 'var(--am)' }}><i className="fas fa-exclamation-circle"></i> Não configurado</span>}
          </span>
        </div>

        <div style={{ padding: '10px 14px', background: 'var(--bg1)', border: '1px solid var(--b1)', borderRadius: 'var(--r-input)', fontSize: 12, color: 'var(--t2)', marginBottom: 12, lineHeight: 1.55 }}>
          <i className="fas fa-info-circle" style={{ color: 'var(--bl)', marginRight: 4 }}></i>
          Usado pra enviar e-mails transacionais: <strong>recuperação de senha</strong>, <strong>convite de novo usuário</strong> e notificações de segurança.
          Exemplos de servidores comuns: Gmail (smtp.gmail.com:587 + senha de app), SendGrid, Mailgun, AWS SES, ou seu próprio servidor.
        </div>

        <div className="form-row-3">
          <div className="field" style={{ gridColumn: 'span 2' }}>
            <label>Host SMTP</label>
            <input className="input" value={cfg.smtp_host} onChange={(e) => upd('smtp_host', e.target.value)} placeholder="smtp.gmail.com" />
          </div>
          <div className="field">
            <label>Porta</label>
            <input className="input" value={cfg.smtp_port} onChange={(e) => upd('smtp_port', e.target.value)} placeholder="587" />
            <span className="hint">587 = STARTTLS, 465 = SSL.</span>
          </div>
        </div>

        <div className="form-row">
          <div className="field">
            <label>Conexão segura</label>
            <select className="input" value={cfg.smtp_secure} onChange={(e) => upd('smtp_secure', e.target.value)}>
              <option value="false">STARTTLS (recomendado pra porta 587)</option>
              <option value="true">SSL/TLS direto (pra porta 465)</option>
            </select>
          </div>
        </div>

        <div className="form-row">
          <div className="field">
            <label>Usuário SMTP</label>
            <input className="input" value={cfg.smtp_user} onChange={(e) => upd('smtp_user', e.target.value)} placeholder="seu-usuario@empresa.com" autoComplete="username" />
          </div>
          <div className="field">
            <label>Senha SMTP</label>
            <input
              type="password"
              className="input"
              value={cfg.smtp_password}
              onChange={(e) => upd('smtp_password', e.target.value)}
              placeholder={cfg.smtp_password === '***' ? '(configurada — deixe assim pra manter)' : 'sua senha SMTP'}
              autoComplete="new-password"
            />
            <span className="hint">Pra Gmail, gere uma <a href="https://myaccount.google.com/apppasswords" target="_blank" rel="noopener" style={{ color: 'var(--or)' }}>senha de app</a>.</span>
          </div>
        </div>

        <div className="form-row">
          <div className="field">
            <label>Nome do remetente</label>
            <input className="input" value={cfg.smtp_from_name} onChange={(e) => upd('smtp_from_name', e.target.value)} placeholder="SINTRACON-SP" />
          </div>
          <div className="field">
            <label>E-mail do remetente</label>
            <input type="email" className="input" value={cfg.smtp_from_email} onChange={(e) => upd('smtp_from_email', e.target.value)} placeholder="no-reply@empresa.com" />
            <span className="hint">Aparece como "De:" nos e-mails. Pode ser igual ao usuário SMTP.</span>
          </div>
        </div>

        <div className="row" style={{ justifyContent: 'flex-end', gap: 8, marginTop: 8 }}>
          <button className="btn btn-soft btn-sm" onClick={verifyConn} disabled={verifying || !configured}>
            {verifying ? <><i className="fas fa-circle-notch fa-spin"></i> Verificando…</> : <><i className="fas fa-plug"></i> Testar conexão</>}
          </button>
          <button className="btn btn-primary btn-sm" onClick={save} disabled={saving}>
            {saving ? <><i className="fas fa-circle-notch fa-spin"></i> Salvando…</> : <><i className="fas fa-save"></i> Salvar SMTP</>}
          </button>
        </div>
      </div>

      {configured && (
        <div className="card">
          <div className="card-header">
            <h3><i className="fas fa-paper-plane" style={{ color: 'var(--or)' }}></i> Enviar e-mail de teste</h3>
            <span className="meta">Manda um e-mail real pra confirmar fim-a-fim</span>
          </div>
          <div className="form-row" style={{ alignItems: 'flex-end' }}>
            <div className="field" style={{ flex: 1 }}>
              <label>Destinatário</label>
              <input type="email" className="input" value={testTo} onChange={(e) => setTestTo(e.target.value)} placeholder="Deixe em branco pra enviar pra você (e-mail do perfil)" />
            </div>
            <div className="field">
              <button className="btn btn-primary btn-sm" onClick={sendTest} disabled={sending}>
                {sending ? <><i className="fas fa-circle-notch fa-spin"></i> Enviando…</> : <><i className="fas fa-paper-plane"></i> Enviar teste</>}
              </button>
            </div>
          </div>
        </div>
      )}
    </>
  );
}

function ConfigPane() {
  const toast = useToast();
  const [cfg, setCfg] = useState({
    lgpd: 90,
    upload: 25,
    jwt: "24h",
    llm: "llama3.2:3b-instruct-q4_K_M",
    embed: "nomic-embed-text:latest",
    aiAutoAccept: false,
  });
  const upd = (k, v) => setCfg((c) => ({ ...c, [k]: v }));

  useEffect(() => {
    api('/api/admin/system-config').then((r) => {
      if (!r) return;
      setCfg((c) => ({
        ...c,
        lgpd: r.lgpd_retention_days || c.lgpd,
        upload: r.upload_max_mb || c.upload,
        jwt: r.jwt_ttl || c.jwt,
        llm: r.ollama_llm_model || c.llm,
        embed: r.ollama_embed_model || c.embed,
        aiAutoAccept: r.ai_auto_accept_when_clean === 'true',
      }));
    }).catch(() => {});
  }, []);

  const save = () => {
    api('/api/admin/system-config', {
      method: 'PATCH',
      body: {
        lgpd_retention_days: cfg.lgpd,
        upload_max_mb: cfg.upload,
        jwt_ttl: cfg.jwt,
        ollama_llm_model: cfg.llm,
        ollama_embed_model: cfg.embed,
        ai_auto_accept_when_clean: cfg.aiAutoAccept ? 'true' : 'false',
      },
    })
      .then(() => toast({ title: "Configurações salvas", kind: "success" }))
      .catch((e) => toast({ title: "Falha ao salvar", msg: e.message, kind: "error" }));
  };

  const clearCache = () => api('/api/admin/cache', { method: 'DELETE' })
    .then(() => toast({ title: "Cache limpo", kind: "success" }))
    .catch((e) => toast({ title: "Falha", msg: e.message, kind: "error" }));

  const reindex = () => api('/api/admin/reindex', { method: 'POST' })
    .then(() => toast({ title: "Reindexação iniciada", msg: "Refazendo embeddings…", kind: "info" }))
    .catch((e) => toast({ title: "Falha", msg: e.message, kind: "error" }));

  const canEdit = Auth.userCan('edit_system');
  if (!canEdit) {
    return (
      <div className="card" style={{ background: 'var(--bg1)' }}>
        <div style={{ padding: 30, textAlign: 'center', color: 'var(--t3)' }}>
          <i className="fas fa-lock" style={{ fontSize: 28, marginBottom: 10, color: 'var(--t3)' }}></i>
          <h4 style={{ margin: '0 0 6px', fontSize: 14, color: 'var(--t2)' }}>Sem permissão</h4>
          <p style={{ fontSize: 12, margin: 0, lineHeight: 1.55 }}>
            Configurações de sistema disponíveis só pra <strong>admin</strong> ou <strong>owner</strong>.
            Sua role atual: <code>{Auth.userRole || 'admin'}</code>.
          </p>
        </div>
      </div>
    );
  }

  return (
    <>
      <BrandingCard />

      <LlmProviderCard />

      <TtsProviderCard />

      <div className="dash-grid">
        <div className="card">
          <div className="card-header">
            <h3><i className="fas fa-vector-square"></i> Embeddings (RAG)</h3>
            <span className="badge b-gr"><span className="dot"></span> local</span>
          </div>
          <div className="field">
            <label>Modelo</label>
            <select className="input" value={cfg.embed} onChange={(e) => upd("embed", e.target.value)}>
              <option value="nomic-embed-text:latest">nomic-embed-text:latest</option>
            </select>
            <span className="hint">Modelo do RAG (sempre local via Ollama — grátis). <strong style={{ color: "var(--am)" }}>Trocar exige reindexar arquivos.</strong></span>
          </div>
        </div>

        <div className="card">
          <div className="card-header">
            <h3><i className="fas fa-shield-alt"></i> LGPD & Segurança</h3>
          </div>
          <div className="form-row" style={{ gridTemplateColumns: "1fr" }}>
            <div className="field">
              <label><i className="fas fa-eraser"></i> Retenção de logs <code style={{ color: "var(--or)" }}>{cfg.lgpd} dias</code></label>
              <Range min={7} max={365} step={1} value={cfg.lgpd} onChange={(v) => upd("lgpd", v)} format={(v) => v + "d"} />
              <span className="hint">Logs mais antigos são apagados automaticamente. Aplica imediato.</span>
            </div>
            <div className="field">
              <label><i className="fas fa-clock"></i> JWT TTL</label>
              <input className="input" value={cfg.jwt} onChange={(e) => upd("jwt", e.target.value)} placeholder="24h" />
              <span className="hint">Duração do token de login admin (ex.: 12h, 24h, 7d). <strong>Requer reinício.</strong></span>
            </div>
          </div>
        </div>
      </div>

      <div className="card">
        <div className="card-header">
          <h3><i className="fas fa-upload"></i> Upload & manutenção</h3>
        </div>
        <div className="form-row-3">
          <div className="field">
            <label>Upload máximo</label>
            <div className="row gap-sm">
              <input type="number" className="input" value={cfg.upload} onChange={(e) => upd("upload", parseInt(e.target.value))} style={{ flex: 1 }} />
              <span className="tag">MB</span>
            </div>
            <span className="hint">Tamanho máximo por arquivo. <strong>Requer reinício.</strong></span>
          </div>
          <div className="field">
            <label>Cache de respostas</label>
            <button className="btn btn-soft" style={{ width: "100%", justifyContent: "center" }} onClick={clearCache}>
              <i className="fas fa-trash-alt"></i> Limpar cache agora
            </button>
            <span className="hint">Força regerar todas as próximas respostas.</span>
          </div>
          <div className="field">
            <label>Índice vetorial</label>
            <button className="btn btn-soft" style={{ width: "100%", justifyContent: "center" }} onClick={reindex}>
              <i className="fas fa-sync"></i> Reindexar tudo
            </button>
            <span className="hint">Refaz embeddings de todos os arquivos.</span>
          </div>
        </div>
      </div>

      <div className="card">
        <div className="card-header">
          <h3><i className="fas fa-wand-magic-sparkles" style={{ color: 'var(--or)' }}></i> Melhoria de conteúdo por IA</h3>
          <span className="meta">Configura o comportamento do botão "✨ Melhorar com IA" no ingest.</span>
        </div>
        <div className="form-row" style={{ alignItems: 'center' }}>
          <div className="field" style={{ flex: 1 }}>
            <label style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
              <Switch on={cfg.aiAutoAccept} onChange={(v) => upd('aiAutoAccept', v)} />
              <span>Aceitar melhoria automaticamente quando a IA não detectar alucinações</span>
            </label>
            <span className="hint">
              Quando <strong>ON</strong>: se o passo 3 do pipeline (validação) retornar 0 alucinações
              <strong> e</strong> 0 fatos removidos, a melhoria é aplicada sem mostrar o preview.
              Reduz fricção mas remove revisão humana — só ative quando você já confiar no sistema.
              Default: <strong>OFF</strong> (preview obrigatório).
            </span>
          </div>
        </div>
      </div>

      <InfraCard />

      <div className="row" style={{ justifyContent: "flex-end" }}>
        <button className="btn btn-primary" onClick={save}>
          <i className="fas fa-save"></i> Salvar configurações
        </button>
      </div>
    </>
  );
}

// ─────────────────────────────────────────────────────────────────────────
// SecurityCard — gerenciamento de 2FA + dispositivos confiáveis do user.
// Tudo opera no user do token atual. Multi-user fica preparado mas sem
// admin-de-admin nesta fase (cada admin gerencia o próprio 2FA).
// ─────────────────────────────────────────────────────────────────────────
// ─────────────────────────────────────────────────────────────────────────
// BrandingCard — identidade da org na sidebar + login screen.
// Salva em system_config: brand_name (nome), brand_initial (letra do mark),
// brand_logo_path (URL/caminho — quando definido, substitui a letra).
// Após salvar, chama window.appReloadBranding() pra refresh instantâneo.
// ─────────────────────────────────────────────────────────────────────────
function BrandingCard() {
  const toast = useToast();
  const fileInputRef = useRef(null);
  const devLogoInputRef = useRef(null);
  const [vals, setVals] = useState({ brand_name: '', brand_initial: '', brand_logo_path: '', developer_logo_path: '', developer_url: '' });
  const [busy, setBusy] = useState(false);
  const [uploading, setUploading] = useState(false);
  const [uploadingDev, setUploadingDev] = useState(false);
  const setV = (k, v) => setVals((s) => ({ ...s, [k]: v }));

  useEffect(() => {
    api('/api/admin/system-config')
      .then((r) => {
        if (!r) return;
        setVals({
          brand_name:          r.brand_name          || '',
          brand_initial:       r.brand_initial       || '',
          brand_logo_path:     r.brand_logo_path     || '',
          developer_logo_path: r.developer_logo_path || '',
          developer_url:       r.developer_url       || '',
        });
      })
      .catch(() => {});
  }, []);

  const save = () => {
    setBusy(true);
    api('/api/admin/system-config', {
      method: 'PATCH',
      body: {
        brand_name:          vals.brand_name.trim()    || 'SINTRACON-SP',
        brand_initial:       vals.brand_initial.trim() || 'S',
        brand_logo_path:     vals.brand_logo_path     || '',
        developer_logo_path: vals.developer_logo_path || '',
        developer_url:       vals.developer_url.trim() || '',
      },
    })
      .then(() => {
        toast({ title: 'Identidade salva', msg: 'Aplicada na sidebar, login e widget.', kind: 'success' });
        if (typeof window.appReloadBranding === 'function') window.appReloadBranding();
      })
      .catch((e) => toast({ title: 'Falha ao salvar', msg: e.message, kind: 'error' }))
      .finally(() => setBusy(false));
  };

  // Upload reusável pros 2 campos de logo. `targetKey` decide onde gravar.
  const uploadImage = async (file, targetKey, setBusyFn) => {
    if (!file.type.startsWith('image/')) {
      toast({ title: 'Arquivo inválido', msg: 'Selecione uma imagem.', kind: 'error' });
      return;
    }
    const fd = new FormData();
    fd.append('file', file);
    setBusyFn(true);
    try {
      const r = await fetch('/api/admin/upload-avatar-video', {
        method: 'POST',
        body: fd,
        headers: { Authorization: 'Bearer ' + (Auth && Auth.token ? Auth.token : '') },
      });
      const data = await r.json();
      if (!r.ok || !data.url) throw new Error(data.error || 'Falha no upload');
      setV(targetKey, data.url);
      toast({ title: 'Imagem carregada', msg: 'Lembre de clicar em Salvar.', kind: 'success' });
    } catch (err) {
      toast({ title: 'Falha no upload', msg: err.message, kind: 'error' });
    } finally {
      setBusyFn(false);
    }
  };

  const onPickFile = () => fileInputRef.current && fileInputRef.current.click();
  const onFile = async (e) => {
    const file = e.target.files && e.target.files[0];
    if (!file) return;
    await uploadImage(file, 'brand_logo_path', setUploading);
    if (fileInputRef.current) fileInputRef.current.value = '';
  };

  const onPickDevLogo = () => devLogoInputRef.current && devLogoInputRef.current.click();
  const onDevLogoFile = async (e) => {
    const file = e.target.files && e.target.files[0];
    if (!file) return;
    await uploadImage(file, 'developer_logo_path', setUploadingDev);
    if (devLogoInputRef.current) devLogoInputRef.current.value = '';
  };

  const absPath = (p) => p ? (p.startsWith('http') || p.startsWith('/') ? p : '/' + p) : null;
  const previewLogoSrc    = absPath(vals.brand_logo_path);
  const previewDevLogoSrc = absPath(vals.developer_logo_path);

  const canEdit = Auth.userCan('edit_branding');
  if (!canEdit) {
    return (
      <div className="card" style={{ background: 'var(--bg1)' }}>
        <div style={{ padding: 30, textAlign: 'center', color: 'var(--t3)' }}>
          <i className="fas fa-lock" style={{ fontSize: 28, marginBottom: 10, color: 'var(--t3)' }}></i>
          <h4 style={{ margin: '0 0 6px', fontSize: 14, color: 'var(--t2)' }}>Sem permissão</h4>
          <p style={{ fontSize: 12, margin: 0, lineHeight: 1.55 }}>
            Configuração de identidade disponível só pra <strong>admin</strong> ou <strong>owner</strong>.
            Sua role atual: <code>{Auth.userRole || 'admin'}</code>.
          </p>
        </div>
      </div>
    );
  }

  return (
    <div className="card">
      <div className="card-header">
        <h3><i className="fas fa-tag" style={{ color: 'var(--or)' }}></i> Identidade do painel</h3>
        <span className="meta">Nome, sigla e logo que aparecem na sidebar e tela de login.</span>
      </div>

      {/* Preview ao vivo */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 14, padding: 12, background: 'var(--bg1)', border: '1px solid var(--b1)', borderRadius: 'var(--r-input)', marginBottom: 12 }}>
        <div style={{
          width: 40, height: 40, borderRadius: 10,
          background: previewLogoSrc ? 'transparent' : 'var(--or)',
          color: '#fff',
          display: 'flex', alignItems: 'center', justifyContent: 'center', fontWeight: 700, fontSize: 16,
          overflow: 'hidden', flexShrink: 0,
        }}>
          {previewLogoSrc
            ? <img src={previewLogoSrc} alt="" style={{ width: '100%', height: '100%', objectFit: 'contain' }} />
            : (vals.brand_initial || 'S').charAt(0).toUpperCase()}
        </div>
        <div>
          <div style={{ fontSize: 14, fontWeight: 600 }}>{vals.brand_name || 'SINTRACON-SP'}</div>
          <div style={{ fontSize: 11, color: 'var(--t3)' }}>admin</div>
        </div>
        <span className="hint" style={{ marginLeft: 'auto', fontSize: 11 }}>← preview da sidebar</span>
      </div>

      <div className="form-row">
        <div className="field">
          <label>Nome (aparece embaixo do logo)</label>
          <input
            className="input"
            value={vals.brand_name}
            onChange={(e) => setV('brand_name', e.target.value)}
            placeholder="SINTRACON-SP"
            maxLength={50}
          />
        </div>
        <div className="field">
          <label>Sigla (letra do logo padrão)</label>
          <input
            className="input"
            value={vals.brand_initial}
            onChange={(e) => setV('brand_initial', e.target.value.toUpperCase().slice(0, 2))}
            placeholder="S"
            style={{ maxWidth: 80, textAlign: 'center', fontSize: 16, fontWeight: 700 }}
            maxLength={2}
          />
          <span className="hint">Usado quando não há logo enviado. Máximo 2 caracteres.</span>
        </div>
      </div>

      <div className="form-row">
        <div className="field" style={{ flex: 1 }}>
          <label>Logo (substitui o ícone laranja)</label>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
            <button
              type="button"
              className="btn btn-soft btn-sm"
              onClick={onPickFile}
              disabled={uploading}
            >
              <i className={'fas ' + (uploading ? 'fa-spinner fa-spin' : 'fa-upload')}></i>
              {uploading ? ' Enviando…' : ' Carregar imagem'}
            </button>
            {vals.brand_logo_path && (
              <>
                <code style={{ fontSize: 11, color: 'var(--t3)', flex: 1, overflow: 'hidden', textOverflow: 'ellipsis' }}>{vals.brand_logo_path}</code>
                <button
                  type="button"
                  className="btn btn-ghost btn-sm"
                  onClick={() => setV('brand_logo_path', '')}
                  title="Remover logo (volta a usar a sigla)"
                >
                  <i className="fas fa-trash"></i>
                </button>
              </>
            )}
            <input
              ref={fileInputRef}
              type="file"
              accept="image/*"
              style={{ display: 'none' }}
              onChange={onFile}
            />
          </div>
          <span className="hint">PNG/JPG quadrado (ideal 128×128). Fundo transparente preserva o quadrado laranja.</span>
        </div>
      </div>

      {/* Logo "desenvolvido por" + URL clicável no footer do widget. Em 2
          colunas pra ficar compacto e claro que os campos andam juntos. */}
      <div className="form-row" style={{ marginTop: 14, paddingTop: 14, borderTop: '1px solid var(--b1)' }}>
        <div className="field" style={{ flex: 1 }}>
          <label>Logo "desenvolvido por" (footer do widget)</label>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap' }}>
            <div style={{
              width: 80, height: 28, borderRadius: 4,
              background: 'var(--bg2)', border: '1px solid var(--b1)',
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              overflow: 'hidden', flexShrink: 0,
            }}>
              {previewDevLogoSrc
                ? <img src={previewDevLogoSrc} alt="" style={{ maxWidth: '100%', maxHeight: '100%', objectFit: 'contain' }} />
                : <span style={{ fontSize: 9, color: 'var(--t3)' }}>sem logo</span>}
            </div>
            <button
              type="button"
              className="btn btn-soft btn-sm"
              onClick={onPickDevLogo}
              disabled={uploadingDev}
            >
              <i className={'fas ' + (uploadingDev ? 'fa-spinner fa-spin' : 'fa-upload')}></i>
              {uploadingDev ? ' Enviando…' : ' Carregar logo'}
            </button>
            {vals.developer_logo_path && (
              <button
                type="button"
                className="btn btn-ghost btn-sm"
                onClick={() => setV('developer_logo_path', '')}
                title="Remover logo (linha 'desenvolvido por' some do widget)"
              >
                <i className="fas fa-trash"></i>
              </button>
            )}
            <input
              ref={devLogoInputRef}
              type="file"
              accept="image/*"
              style={{ display: 'none' }}
              onChange={onDevLogoFile}
            />
          </div>
          <span className="hint">
            Imagem horizontal pequena (ideal 80×16). Vazio = linha não aparece no widget.
          </span>
        </div>

        <div className="field" style={{ flex: 1 }}>
          <label>URL do desenvolvedor (link da logo)</label>
          <input
            type="url"
            className="input"
            value={vals.developer_url}
            onChange={(e) => setV('developer_url', e.target.value)}
            placeholder="https://vazto.com.br"
          />
          <span className="hint">
            URL aberta em nova aba ao clicar na logo. Vazio = logo não é clicável.
          </span>
        </div>
      </div>

      <div className="row" style={{ justifyContent: 'flex-end', marginTop: 8 }}>
        <button className="btn btn-primary btn-sm" onClick={save} disabled={busy}>
          {busy ? <><i className="fas fa-circle-notch fa-spin"></i> Salvando…</> : <><i className="fas fa-save"></i> Salvar identidade</>}
        </button>
      </div>
    </div>
  );
}

// TermsPane — editor do texto "Termos de IA" exibido no footer do widget.
// Texto inicial vem da migration 029 (modelo inspirado em jotform.com/ai-terms
// adaptado pra realidade do SINTRACON). Marcadores ***DADO*** devem ser
// substituídos pelo admin pelos dados reais (DPO, e-mail, telefone, endereço).
// Só admin/owner conseguem editar (PATCH /system-config exige requireRole('admin')).
function TermsPane() {
  const toast = useToast();
  const [text, setText] = useState('');
  const [original, setOriginal] = useState('');
  const [loading, setLoading] = useState(true);
  const [saving, setSaving] = useState(false);

  useEffect(() => {
    api('/api/admin/system-config')
      .then((r) => {
        const t = (r && r.ai_terms_text) || '';
        setText(t);
        setOriginal(t);
      })
      .catch((e) => toast({ title: 'Falha ao carregar termos', msg: e.message, kind: 'error' }))
      .finally(() => setLoading(false));
  }, []);

  const canEdit = Auth.userCan('edit_system');
  if (!canEdit) {
    return (
      <div className="card" style={{ background: 'var(--bg1)' }}>
        <div style={{ padding: 30, textAlign: 'center', color: 'var(--t3)' }}>
          <i className="fas fa-lock" style={{ fontSize: 28, marginBottom: 10, color: 'var(--t3)' }}></i>
          <h4 style={{ margin: '0 0 6px', fontSize: 14, color: 'var(--t2)' }}>Sem permissão</h4>
          <p style={{ fontSize: 12, margin: 0, lineHeight: 1.55 }}>
            Edição dos Termos de IA disponível só pra <strong>admin</strong> ou <strong>owner</strong>.
            Sua role atual: <code>{Auth.userRole || 'admin'}</code>.
          </p>
        </div>
      </div>
    );
  }

  const dirty = text !== original;
  const markersFound = (text.match(/\*\*\*[A-Z_-]+\*\*\*/g) || []).length;

  const save = () => {
    setSaving(true);
    api('/api/admin/system-config', {
      method: 'PATCH',
      body: { ai_terms_text: text },
    })
      .then(() => {
        setOriginal(text);
        toast({ title: 'Termos salvos', msg: 'Já estão disponíveis no widget.', kind: 'success' });
      })
      .catch((e) => toast({ title: 'Falha ao salvar', msg: e.message, kind: 'error' }))
      .finally(() => setSaving(false));
  };

  const resetToModel = async () => {
    if (!window.confirm('Recarregar o texto-modelo padrão? Suas alterações locais serão perdidas (clique em Salvar pra aplicar de fato).')) return;
    try {
      // Endpoint público devolve o que está no DB. Pra resetar, melhor o admin
      // copiar manualmente do arquivo de migration — aqui só recarregamos o
      // valor atual do servidor (descarta edição não salva).
      const r = await api('/api/admin/system-config');
      const t = (r && r.ai_terms_text) || '';
      setText(t);
      toast({ title: 'Texto recarregado do servidor', kind: 'info' });
    } catch (e) {
      toast({ title: 'Falha', msg: e.message, kind: 'error' });
    }
  };

  return (
    <div className="card">
      <div className="card-header">
        <h3><i className="fas fa-file-contract" style={{ color: 'var(--or)' }}></i> Termos de uso de IA</h3>
        <span className="meta">
          Exibido no widget quando o usuário clica em "Termos de IA" no footer da conversa.
          Pode usar quebras de linha. Substitua os marcadores <code>***DADO***</code> pelos dados reais.
        </span>
      </div>

      {loading ? (
        <div style={{ padding: 24, textAlign: 'center', color: 'var(--t3)' }}>
          <i className="fas fa-circle-notch fa-spin"></i> Carregando…
        </div>
      ) : (
        <>
          {markersFound > 0 && (
            <div style={{
              padding: '10px 14px', marginBottom: 12,
              background: 'rgba(245, 158, 11, 0.1)',
              border: '1px solid rgba(245, 158, 11, 0.3)',
              borderRadius: 'var(--r-input)',
              fontSize: 12, color: 'var(--t2)',
            }}>
              <i className="fas fa-exclamation-triangle" style={{ color: '#f59e0b', marginRight: 6 }}></i>
              <strong>{markersFound} marcador{markersFound > 1 ? 'es' : ''}</strong> <code>***DADO***</code> ainda não preenchido{markersFound > 1 ? 's' : ''}.
              Localize com Ctrl+F e substitua pelos dados reais antes de salvar.
            </div>
          )}

          <div className="field">
            <label style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
              <span>Texto dos termos (exibido literalmente, com quebras de linha)</span>
              <span style={{ fontSize: 10.5, color: 'var(--t3)', fontFamily: 'var(--ff-mono)' }}>
                {text.length.toLocaleString('pt-BR')} caracteres
              </span>
            </label>
            <textarea
              className="input"
              value={text}
              onChange={(e) => setText(e.target.value)}
              spellCheck="false"
              style={{ minHeight: 480, fontFamily: 'var(--ff-mono)', fontSize: 12.5, lineHeight: 1.5 }}
            ></textarea>
            <span className="hint">
              Marcadores <code>***DADO***</code>: <code>***DATA_ATUALIZACAO***</code>, <code>***DIAS_RETENCAO***</code>,
              <code>***E-MAIL_DPO***</code>, <code>***E-MAIL_CONTATO***</code>, <code>***TELEFONE***</code>, <code>***ENDERECO_SINTRACON***</code>.
            </span>
          </div>

          <div className="row" style={{ justifyContent: 'space-between', marginTop: 12 }}>
            <button type="button" className="btn btn-ghost btn-sm" onClick={resetToModel} disabled={saving}>
              <i className="fas fa-rotate-left"></i> Recarregar do servidor
            </button>
            <button
              type="button"
              className="btn btn-primary btn-sm"
              onClick={save}
              disabled={saving || !dirty}
              title={!dirty ? 'Nenhuma alteração' : 'Salvar termos'}
            >
              {saving
                ? <><i className="fas fa-circle-notch fa-spin"></i> Salvando…</>
                : <><i className="fas fa-save"></i> Salvar termos</>}
            </button>
          </div>
        </>
      )}
    </div>
  );
}

function SecurityCard() {
  const toast = useToast();
  const confirm = useConfirm();
  const [status, setStatus]       = useState(null);  // { totp_enabled, ... }
  const [setupData, setSetupData] = useState(null);  // { qr_data_url, secret_text, backup_codes, setup_token }
  const [showSetup, setShowSetup] = useState(false);
  const [showCodes, setShowCodes] = useState(null);  // array de plaintext codes (mostrar UMA vez)
  const [devices, setDevices]     = useState([]);
  const [loading, setLoading]     = useState(true);

  const reload = () => Promise.all([
    api('/api/admin/security/2fa/status'),
    api('/api/admin/security/trusted-devices'),
  ]).then(([s, d]) => {
    setStatus(s);
    setDevices((d && d.devices) || []);
    setLoading(false);
  }).catch(() => setLoading(false));

  useEffect(() => { reload(); }, []);

  const startSetup = () => {
    api('/api/admin/security/2fa/setup', { method: 'POST' })
      .then((r) => { setSetupData(r); setShowSetup(true); })
      .catch((e) => toast({ title: 'Falha', msg: e.message, kind: 'error' }));
  };

  const confirmSetup = (code) => {
    return api('/api/admin/security/2fa/confirm', {
      method: 'POST',
      body: { setup_token: setupData.setup_token, code },
    }).then(() => {
      // 2FA ativado — mostra backup codes uma vez, fecha modal de setup.
      setShowCodes(setupData.backup_codes);
      setShowSetup(false);
      setSetupData(null);
      reload();
      toast({ title: '2FA ativado', msg: 'Guarde os códigos de backup com segurança.', kind: 'success' });
    });
  };

  const disable2fa = async () => {
    const pass = window.prompt('Confirme sua senha pra desabilitar 2FA:');
    if (!pass) return;
    api('/api/admin/security/2fa/disable', { method: 'POST', body: { password: pass } })
      .then(() => { toast({ title: '2FA desabilitado', kind: 'success' }); reload(); })
      .catch((e) => toast({ title: 'Falha', msg: e.message, kind: 'error' }));
  };

  const regenerateBackupCodes = async () => {
    const pass = window.prompt('Confirme sua senha pra gerar novos códigos de backup (os antigos serão invalidados):');
    if (!pass) return;
    api('/api/admin/security/2fa/regenerate-backup-codes', { method: 'POST', body: { password: pass } })
      .then((r) => { setShowCodes(r.backup_codes); reload(); })
      .catch((e) => toast({ title: 'Falha', msg: e.message, kind: 'error' }));
  };

  const revokeDevice = async (id) => {
    api('/api/admin/security/trusted-devices/' + id, { method: 'DELETE' })
      .then(() => { toast({ title: 'Dispositivo revogado', kind: 'success' }); reload(); })
      .catch((e) => toast({ title: 'Falha', msg: e.message, kind: 'error' }));
  };

  const revokeAllDevices = async () => {
    const ok = await confirm({
      title: 'Esquecer todos os dispositivos?',
      message: 'Você será forçado a digitar o código 2FA no próximo login em qualquer navegador.',
      confirmLabel: 'Esquecer todos',
      danger: true,
      icon: 'fa-shield-halved',
    });
    if (!ok) return;
    api('/api/admin/security/trusted-devices', { method: 'DELETE' })
      .then((r) => { toast({ title: 'Dispositivos esquecidos', msg: `${r.revoked} revogados`, kind: 'success' }); reload(); })
      .catch((e) => toast({ title: 'Falha', msg: e.message, kind: 'error' }));
  };

  return (
    <div className="card">
      <div className="card-header">
        <h3><i className="fas fa-shield-halved" style={{ color: 'var(--or)' }}></i> Segurança da conta</h3>
        <span className="meta">2FA (TOTP) + dispositivos confiáveis (lembrar 30 dias)</span>
      </div>

      {loading && (
        <div style={{ padding: 20, textAlign: 'center', color: 'var(--t3)' }}>
          <i className="fas fa-circle-notch fa-spin"></i> Carregando…
        </div>
      )}

      {!loading && status && (
        <>
          {/* Status do 2FA */}
          <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '10px 12px', background: 'var(--bg1)', border: '1px solid var(--b1)', borderRadius: 'var(--r-input)', marginBottom: 12 }}>
            <i className={'fas ' + (status.totp_enabled ? 'fa-shield-halved' : 'fa-shield-cat')} style={{ fontSize: 22, color: status.totp_enabled ? 'var(--gr)' : 'var(--t3)' }}></i>
            <div style={{ flex: 1 }}>
              <div style={{ fontWeight: 600, fontSize: 13 }}>
                Autenticação em 2 fatores: {status.totp_enabled
                  ? <span style={{ color: 'var(--gr)' }}>Ativa</span>
                  : <span style={{ color: 'var(--t3)' }}>Desativada</span>}
              </div>
              <div style={{ fontSize: 11, color: 'var(--t3)' }}>
                Usuário: <code>{status.username}</code> · role: <code>{status.role}</code>
                {status.totp_enabled && status.totp_enabled_at && (
                  <> · ativada em {new Date(status.totp_enabled_at).toLocaleString('pt-BR', { day: '2-digit', month: '2-digit', year: 'numeric' })}</>
                )}
              </div>
            </div>
            {!status.totp_enabled ? (
              <button className="btn btn-primary btn-sm" onClick={startSetup}>
                <i className="fas fa-plus"></i> Ativar 2FA
              </button>
            ) : (
              <button className="btn btn-danger btn-sm" onClick={disable2fa}>
                <i className="fas fa-power-off"></i> Desativar
              </button>
            )}
          </div>

          {/* Backup codes status */}
          {status.totp_enabled && (
            <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '10px 12px', background: 'var(--bg1)', border: '1px solid var(--b1)', borderRadius: 'var(--r-input)', marginBottom: 12, fontSize: 12 }}>
              <i className="fas fa-key" style={{ color: status.backup_codes_unused < 3 ? 'var(--rd)' : 'var(--am)' }}></i>
              <span style={{ flex: 1, color: 'var(--t2)' }}>
                <strong style={{ color: status.backup_codes_unused < 3 ? 'var(--rd)' : 'var(--t1)' }}>
                  {status.backup_codes_unused}
                </strong> de {status.backup_codes_total} códigos de backup disponíveis
                {status.backup_codes_unused < 3 && <span style={{ color: 'var(--rd)', marginLeft: 8 }}>· considere regenerar!</span>}
              </span>
              <button className="btn btn-soft btn-sm" onClick={regenerateBackupCodes}>
                <i className="fas fa-arrows-rotate"></i> Gerar novos
              </button>
            </div>
          )}

          {/* Trusted devices */}
          {status.totp_enabled && (
            <div style={{ marginTop: 16 }}>
              <div style={{ display: 'flex', alignItems: 'center', marginBottom: 8 }}>
                <strong style={{ fontSize: 13 }}>Dispositivos confiáveis ({devices.length})</strong>
                {devices.length > 0 && (
                  <button className="btn btn-ghost btn-sm" style={{ marginLeft: 'auto' }} onClick={revokeAllDevices}>
                    <i className="fas fa-trash"></i> Esquecer todos
                  </button>
                )}
              </div>
              {devices.length === 0 ? (
                <div style={{ padding: 12, background: 'var(--bg1)', border: '1px solid var(--b1)', borderRadius: 'var(--r-input)', fontSize: 12, color: 'var(--t3)', textAlign: 'center' }}>
                  Nenhum dispositivo confiável ativo. Marque "Lembrar deste dispositivo" no próximo login pra adicionar.
                </div>
              ) : (
                <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
                  {devices.map((d) => (
                    <div key={d.id} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '8px 12px', background: 'var(--bg1)', border: '1px solid var(--b1)', borderRadius: 'var(--r-input)', fontSize: 11.5 }}>
                      <i className="fas fa-laptop" style={{ color: 'var(--t3)' }}></i>
                      <div style={{ flex: 1, minWidth: 0 }}>
                        <div style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', color: 'var(--t1)' }}>
                          {d.user_agent || '(user-agent desconhecido)'}
                        </div>
                        <div style={{ fontSize: 10.5, color: 'var(--t3)' }}>
                          IP: <code>{d.last_ip || '—'}</code> · último uso: {new Date(d.last_used_at).toLocaleString('pt-BR', { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' })} · expira em {new Date(d.expires_at).toLocaleDateString('pt-BR')}
                        </div>
                      </div>
                      <button className="icon-btn" onClick={() => revokeDevice(d.id)} title="Revogar este dispositivo" style={{ color: 'var(--rd)' }}>
                        <i className="fas fa-times"></i>
                      </button>
                    </div>
                  ))}
                </div>
              )}
            </div>
          )}
        </>
      )}

      {/* Modal: setup 2FA — mostra QR + secret + input pra confirmar */}
      {showSetup && setupData && (
        <Setup2FAModal
          data={setupData}
          onConfirm={confirmSetup}
          onClose={() => { setShowSetup(false); setSetupData(null); }}
        />
      )}

      {/* Modal: mostra backup codes (UMA VEZ) */}
      {showCodes && (
        <BackupCodesModal codes={showCodes} onClose={() => setShowCodes(null)} />
      )}
    </div>
  );
}

function Setup2FAModal({ data, onConfirm, onClose }) {
  const [code, setCode] = useState('');
  const [busy, setBusy] = useState(false);
  const [err, setErr]   = useState('');

  const submit = (e) => {
    e.preventDefault();
    if (!code) return;
    setBusy(true); setErr('');
    onConfirm(code.trim())
      .catch((e) => { setErr(e.message); setBusy(false); });
  };

  return (
    <div className="modal-overlay" onClick={onClose}>
      <div className="modal" onClick={(e) => e.stopPropagation()} style={{ maxWidth: 560 }}>
        <div className="modal-head">
          <h3><i className="fas fa-shield-halved" style={{ color: 'var(--or)' }}></i> Ativar 2FA</h3>
          <button type="button" className="icon-btn" onClick={onClose}><i className="fas fa-times"></i></button>
        </div>
        <form onSubmit={submit}>
          <div className="modal-body">
            <ol style={{ margin: 0, paddingLeft: 18, fontSize: 13, lineHeight: 1.7, color: 'var(--t2)' }}>
              <li>Abra um app authenticator (Google Authenticator, Authy, 1Password, Microsoft Authenticator…)</li>
              <li>Escaneie o QR abaixo (ou digite o secret manualmente)</li>
              <li>Digite o código de 6 dígitos que aparecer no app pra confirmar</li>
            </ol>
            <div style={{ textAlign: 'center', margin: '14px 0' }}>
              <img src={data.qr_data_url} alt="QR code 2FA" style={{ width: 220, height: 220, border: '1px solid var(--b1)', borderRadius: 8, background: '#fff', padding: 8 }} />
              <div style={{ marginTop: 8, fontSize: 11, color: 'var(--t3)' }}>
                Não conseguiu escanear? Digite o secret manual:{' '}
                <code style={{ background: 'var(--bg2)', padding: '2px 6px', borderRadius: 4, userSelect: 'all', wordBreak: 'break-all' }}>{data.secret_text}</code>
              </div>
            </div>
            <div className="field">
              <label><i className="fas fa-mobile-screen"></i> Código de confirmação (6 dígitos)</label>
              <input
                className="input"
                value={code}
                onChange={(e) => setCode(e.target.value)}
                autoFocus
                required
                inputMode="numeric"
                maxLength={6}
                placeholder="123456"
                style={{ fontFamily: 'var(--ff-mono)', fontSize: 18, letterSpacing: 4, textAlign: 'center' }}
              />
            </div>
            {err && <div className="login-error" style={{ marginTop: 8 }}>{err}</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> Confirmando…</> : <><i className="fas fa-check"></i> Confirmar e ativar</>}
            </button>
          </div>
        </form>
      </div>
    </div>
  );
}

function BackupCodesModal({ codes, onClose }) {
  const [copied, setCopied] = useState(false);
  const copy = () => {
    const text = codes.join('\n');
    navigator.clipboard.writeText(text).then(() => { setCopied(true); setTimeout(() => setCopied(false), 2000); });
  };
  return (
    <div className="modal-overlay" onClick={onClose}>
      <div className="modal" onClick={(e) => e.stopPropagation()} style={{ maxWidth: 500 }}>
        <div className="modal-head">
          <h3><i className="fas fa-key" style={{ color: 'var(--am)' }}></i> Códigos de backup</h3>
          <button type="button" className="icon-btn" onClick={onClose}><i className="fas fa-times"></i></button>
        </div>
        <div className="modal-body">
          <div style={{ padding: '10px 12px', background: 'rgba(245,158,11,0.08)', border: '1px solid var(--am)', borderRadius: 'var(--r-input)', fontSize: 12.5, color: 'var(--t1)', marginBottom: 12 }}>
            <strong style={{ color: 'var(--am)' }}><i className="fas fa-exclamation-triangle"></i> Anote, imprima ou copie agora.</strong> Estes códigos só aparecem UMA vez. Cada um pode ser usado UMA vez no lugar do código TOTP — usar se perder o celular.
          </div>
          <div style={{
            display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 6,
            padding: 14, background: 'var(--bg1)', border: '1px solid var(--b1)', borderRadius: 'var(--r-input)',
            fontFamily: 'var(--ff-mono)', fontSize: 14, letterSpacing: 1,
          }}>
            {codes.map((c) => (
              <div key={c} style={{ padding: '4px 8px', background: 'var(--bg2)', borderRadius: 4, textAlign: 'center', userSelect: 'all' }}>{c}</div>
            ))}
          </div>
        </div>
        <div className="modal-foot">
          <button type="button" className="btn btn-soft" onClick={copy}>
            <i className={'fas ' + (copied ? 'fa-check' : 'fa-copy')}></i> {copied ? 'Copiado!' : 'Copiar todos'}
          </button>
          <button type="button" className="btn btn-primary" onClick={onClose}>Fechei tudo, fechar</button>
        </div>
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────
// InfraCard — memória/disco/Ollama version em tempo real (poll 10s)
// ─────────────────────────────────────────────────────────────────────────
function InfraCard() {
  const [stats, setStats] = useState(null);
  const [err, setErr] = useState(null);

  useEffect(() => {
    let alive = true;
    const load = () => api('/api/admin/system-stats')
      .then((s) => { if (alive) { setStats(s); setErr(null); } })
      .catch((e) => { if (alive) setErr(e.message); });
    load();
    const t = setInterval(load, 10000);
    return () => { alive = false; clearInterval(t); };
  }, []);

  if (err) {
    return (
      <div className="card">
        <div className="card-header"><h3><i className="fas fa-microchip"></i> Infraestrutura</h3></div>
        <div className="empty" style={{ padding: 14 }}>
          <i className="fas fa-exclamation-triangle"></i>
          <p>Falha ao ler stats: {err}</p>
        </div>
      </div>
    );
  }
  if (!stats || !stats.current) {
    return (
      <div className="card">
        <div className="card-header"><h3><i className="fas fa-microchip"></i> Infraestrutura</h3></div>
        <div style={{ textAlign: 'center', padding: 20, color: 'var(--t3)' }}>
          <i className="fas fa-circle-notch fa-spin"></i> Coletando primeira amostra…
        </div>
      </div>
    );
  }

  const c = stats.current;
  const memPctColor = c.memPct >= 90 ? 'var(--rd)' : c.memPct >= 75 ? 'var(--am)' : 'var(--gr)';
  const diskPctColor = c.diskPct == null ? 'var(--t3)' : c.diskPct >= 90 ? 'var(--rd)' : c.diskPct >= 80 ? 'var(--am)' : 'var(--gr)';
  const memSpark = stats.history.map((h) => h.memPct);
  const rssSpark = stats.history.map((h) => h.procRss / (1024 * 1024)); // em MB

  return (
    <div className="card">
      <div className="card-header">
        <h3><i className="fas fa-microchip"></i> Infraestrutura</h3>
        <span className="meta">
          amostragem a cada {Math.round(stats.sample_interval_ms / 1000)}s · histórico {stats.history_window_minutes}min · {stats.samples_total} pontos
        </span>
      </div>

      {/* Linha 1: Ollama + Node + uptime */}
      <div className="dash-grid-3">
        <InfoTile
          icon="fa-brain" iconCls="or"
          label="Ollama"
          main={stats.ollama_version ? 'v' + stats.ollama_version : 'indisponível'}
          sub={stats.ollama_version ? 'API conectada' : 'Falha ao chamar /api/version do Ollama'}
        />
        <InfoTile
          icon="fa-server" iconCls="bl"
          label="Node.js"
          main={stats.node_version || '—'}
          sub={`${stats.platform} · ${stats.cpu_count} CPU${stats.cpu_count === 1 ? '' : 's'}`}
        />
        <InfoTile
          icon="fa-clock" iconCls="gr"
          label="Uptime do processo"
          main={humanDuration(stats.uptime_seconds)}
          sub={`host: ${stats.hostname}`}
        />
      </div>

      {/* Linha 2: Memória + Disco */}
      <div className="dash-grid" style={{ marginTop: 14 }}>
        <div style={{
          padding: '14px 16px',
          background: 'var(--bg1)',
          border: '1px solid var(--b1)',
          borderRadius: 'var(--r-input)',
          position: 'relative',
          overflow: 'hidden',
        }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 6 }}>
            <div>
              <div style={{ fontSize: 11, color: 'var(--t3)', textTransform: 'uppercase', letterSpacing: '.04em', fontWeight: 600 }}>
                Memória RAM
              </div>
              <div style={{ fontSize: 22, fontWeight: 600, marginTop: 2 }}>
                {c.memPct.toFixed(1)}<span style={{ fontSize: 14, color: 'var(--t3)', marginLeft: 2 }}>%</span>
              </div>
              <div style={{ fontSize: 11, color: 'var(--t3)', fontFamily: 'var(--ff-mono)' }}>
                {humanSize(c.memUsed)} / {humanSize(c.memTotal)}
              </div>
            </div>
            <div style={{ textAlign: 'right', fontSize: 10.5, color: 'var(--t3)', fontFamily: 'var(--ff-mono)' }}>
              <div>5 min: <strong style={{ color: 'var(--t2)' }}>{stats.averages.mem_pct_5min}%</strong></div>
              <div>15 min: <strong style={{ color: 'var(--t2)' }}>{stats.averages.mem_pct_15min}%</strong></div>
              <div>1 h: <strong style={{ color: 'var(--t2)' }}>{stats.averages.mem_pct_60min}%</strong></div>
            </div>
          </div>
          <div className="track" style={{ height: 8, background: 'var(--bg3)', borderRadius: 4, overflow: 'hidden', marginBottom: 6 }}>
            <div style={{ width: c.memPct + '%', height: '100%', background: memPctColor, transition: 'width .3s' }}></div>
          </div>
          <div style={{ position: 'relative', height: 38 }}>
            <Sparkline data={memSpark} color={memPctColor} />
          </div>
          <div style={{ fontSize: 10.5, color: 'var(--t3)', marginTop: 4, fontFamily: 'var(--ff-mono)' }}>
            Node processo: RSS {humanSize(c.procRss)} · heap {humanSize(c.procHeapUsed)} / {humanSize(c.procHeapTotal)}
          </div>
        </div>

        <div style={{
          padding: '14px 16px',
          background: 'var(--bg1)',
          border: '1px solid var(--b1)',
          borderRadius: 'var(--r-input)',
        }}>
          <div style={{ marginBottom: 6 }}>
            <div style={{ fontSize: 11, color: 'var(--t3)', textTransform: 'uppercase', letterSpacing: '.04em', fontWeight: 600 }}>
              Disco da partição do projeto
            </div>
            {c.diskTotal != null ? (
              <>
                <div style={{ fontSize: 22, fontWeight: 600, marginTop: 2 }}>
                  {c.diskPct.toFixed(1)}<span style={{ fontSize: 14, color: 'var(--t3)', marginLeft: 2 }}>%</span>
                  <span style={{ fontSize: 12, color: 'var(--t3)', marginLeft: 8 }}>usado</span>
                </div>
                <div style={{ fontSize: 11, color: 'var(--t3)', fontFamily: 'var(--ff-mono)' }}>
                  {humanSize(c.diskUsed)} usado · {humanSize(c.diskFree)} livre · {humanSize(c.diskTotal)} total
                </div>
              </>
            ) : (
              <div style={{ fontSize: 13, color: 'var(--t3)', marginTop: 6 }}>
                <i className="fas fa-info-circle"></i> fs.statfs indisponível (Node &lt; 18.15)
              </div>
            )}
          </div>
          {c.diskTotal != null && (
            <div className="track" style={{ height: 8, background: 'var(--bg3)', borderRadius: 4, overflow: 'hidden', marginBottom: 6 }}>
              <div style={{ width: c.diskPct + '%', height: '100%', background: diskPctColor }}></div>
            </div>
          )}
          <div style={{ fontSize: 11, color: 'var(--t3)', marginTop: 10 }}>
            <div style={{ textTransform: 'uppercase', letterSpacing: '.04em', fontWeight: 600, fontSize: 10.5, marginBottom: 4 }}>Load average (1 / 5 / 15 min)</div>
            <div style={{ fontFamily: 'var(--ff-mono)' }}>
              <strong style={{ color: 'var(--t1)' }}>{(c.load1 || 0).toFixed(2)}</strong>
              <span style={{ margin: '0 4px' }}>·</span>
              <strong style={{ color: 'var(--t1)' }}>{(c.load5 || 0).toFixed(2)}</strong>
              <span style={{ margin: '0 4px' }}>·</span>
              <strong style={{ color: 'var(--t1)' }}>{(c.load15 || 0).toFixed(2)}</strong>
              <span style={{ color: 'var(--t3)', marginLeft: 6 }}>
                em {stats.cpu_count} CPU{stats.cpu_count === 1 ? '' : 's'}
                {c.load1 != null && c.load1 > stats.cpu_count && <span style={{ color: 'var(--am)', marginLeft: 4 }}>(sobrecarga)</span>}
              </span>
            </div>
          </div>
        </div>
      </div>

      <div style={{ fontSize: 10.5, color: 'var(--t3)', marginTop: 10, fontFamily: 'var(--ff-mono)', textAlign: 'right' }}>
        Atualizado em {new Date(c.ts).toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit', second: '2-digit' })} · refresh automático
      </div>
    </div>
  );
}

function InfoTile({ icon, iconCls, label, main, sub }) {
  return (
    <div style={{
      padding: '12px 14px',
      background: 'var(--bg1)',
      border: '1px solid var(--b1)',
      borderRadius: 'var(--r-input)',
      display: 'flex',
      alignItems: 'center',
      gap: 12,
    }}>
      <div className={"stat-icon " + (iconCls || '')} style={{ width: 32, height: 32, fontSize: 13, flexShrink: 0 }}>
        <i className={"fas " + icon}></i>
      </div>
      <div style={{ minWidth: 0, flex: 1 }}>
        <div style={{ fontSize: 10.5, color: 'var(--t3)', textTransform: 'uppercase', letterSpacing: '.04em', fontWeight: 600 }}>{label}</div>
        <div style={{ fontSize: 14, fontWeight: 600, color: 'var(--t1)', fontFamily: 'var(--ff-mono)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }} title={main}>{main}</div>
        <div style={{ fontSize: 10.5, color: 'var(--t3)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }} title={sub}>{sub}</div>
      </div>
    </div>
  );
}

function DiagPane() {
  const toast = useToast();
  const [checks, setChecks] = useState(null);
  const [loading, setLoading] = useState(false);
  const [summary, setSummary] = useState({ ok: 0, warn: 0, error: 0, total: 0 });
  const [ts, setTs] = useState(null);

  const run = () => {
    setLoading(true);
    api('/api/admin/diagnostic')
      .then((r) => {
        setChecks(r.checks || []);
        setSummary(r.summary || { ok: 0, warn: 0, error: 0, total: 0 });
        setTs(r.timestamp);
      })
      .catch((e) => toast({ title: "Falha no diagnóstico", msg: e.message, kind: "error" }))
      .finally(() => setLoading(false));
  };

  useEffect(() => { run(); }, []);

  const summaryBadge = summary.error > 0
    ? { cls: 'b-rd', label: `${summary.ok} ok · ${summary.warn} alerta · ${summary.error} falha` }
    : summary.warn > 0
      ? { cls: 'b-am', label: `${summary.ok} saudáveis · ${summary.warn} alerta` }
      : { cls: 'b-gr', label: `${summary.ok} saudáveis` };

  return (
    <>
      <div className="card">
        <div className="card-header">
          <h3><i className="fas fa-stethoscope"></i> Diagnóstico</h3>
          <div className="row gap-sm">
            {checks && <span className={"badge " + summaryBadge.cls}><span className="dot"></span> {summaryBadge.label}</span>}
            <button className="btn btn-soft btn-sm" onClick={run} disabled={loading}>
              <i className={"fas " + (loading ? "fa-circle-notch fa-spin" : "fa-redo")}></i> {loading ? "Rodando…" : "Rodar de novo"}
            </button>
          </div>
        </div>

        {!checks && !loading && (
          <div className="empty" style={{ padding: 18 }}><p>Clique em "Rodar de novo" para iniciar.</p></div>
        )}

        {loading && !checks && (
          <div style={{ textAlign: 'center', padding: 30, color: 'var(--t3)' }}>
            <i className="fas fa-circle-notch fa-spin"></i> Executando checks…
          </div>
        )}

        {checks && (
          <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
            {checks.map((c) => (
              <div key={c.name} style={{
                display: "flex", alignItems: "center", gap: 14,
                padding: "12px 14px",
                background: "var(--bg1)",
                border: "1px solid var(--b1)",
                borderRadius: "var(--r-input)",
              }}>
                <div style={{
                  width: 28, height: 28, borderRadius: 7,
                  background: c.status === "ok" ? "rgba(34,197,94,.13)" : c.status === "warn" ? "rgba(245,158,11,.13)" : "rgba(239,68,68,.13)",
                  color: c.status === "ok" ? "var(--gr)" : c.status === "warn" ? "var(--am)" : "var(--rd)",
                  display: "grid", placeItems: "center",
                }}>
                  <i className={"fas " + (c.status === "ok" ? "fa-check" : c.status === "warn" ? "fa-exclamation" : "fa-times")}></i>
                </div>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontSize: 13, fontWeight: 600 }}>{c.name}</div>
                  <div style={{ fontSize: 11.5, color: "var(--t3)", fontFamily: "var(--ff-mono)", overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }} title={c.value}>{c.value}</div>
                </div>
              </div>
            ))}
          </div>
        )}

        {ts && (
          <div style={{ fontSize: 10.5, color: 'var(--t3)', fontFamily: 'var(--ff-mono)', textAlign: 'right', marginTop: 10 }}>
            Última execução: {new Date(ts).toLocaleString('pt-BR')}
          </div>
        )}
      </div>

      <DebugConsole />
    </>
  );
}

// ---- Console de debug com logs reais do RAG pipeline ---------------------
function DebugConsole() {
  const toast = useToast();
  const [lines, setLines] = useState(null);
  const [loading, setLoading] = useState(false);
  const load = () => {
    setLoading(true);
    DataLoader.debugLogs().then((ls) => {
      setLines(ls || []);
      setLoading(false);
    });
  };
  useEffect(() => { load(); }, []);
  const copy = () => {
    if (!lines) return;
    const text = lines.join('\n');
    if (navigator.clipboard) {
      navigator.clipboard.writeText(text);
      toast({ title: "Log copiado", kind: "success" });
    }
  };
  return (
    <div className="card">
      <div className="card-header">
        <h3><i className="fas fa-terminal"></i> Console de debug</h3>
        <div className="row gap-sm">
          <button className="btn btn-ghost btn-sm" onClick={load} disabled={loading}>
            <i className={"fas " + (loading ? "fa-circle-notch fa-spin" : "fa-redo")}></i> Atualizar
          </button>
          <button className="btn btn-ghost btn-sm" onClick={copy}>
            <i className="fas fa-copy"></i> Copiar log
          </button>
        </div>
      </div>
      <pre style={{
        background: "var(--bg1)",
        border: "1px solid var(--b1)",
        borderRadius: "var(--r-input)",
        padding: "12px 14px",
        fontFamily: "var(--ff-mono)",
        fontSize: 11.5,
        color: "var(--t2)",
        margin: 0,
        maxHeight: 280,
        overflowY: "auto",
        lineHeight: 1.55,
        whiteSpace: 'pre-wrap',
      }}>
        {loading && !lines ? 'Carregando logs reais do pipeline RAG…' :
         !lines || lines.length === 0 ? 'Sem traces no log ainda. Faça uma pergunta no chatbot pra gerar logs.' :
         lines.join('\n')}
      </pre>
      <div style={{ fontSize: 10.5, color: 'var(--t3)', marginTop: 8, fontFamily: 'var(--ff-mono)' }}>
        Origem: GET /api/admin/debug-logs — últimos 20 traces da tabela chat_logs.
      </div>
    </div>
  );
}

function ApiPane() {
  const toast = useToast();
  const copy = (text) => {
    navigator.clipboard && navigator.clipboard.writeText(text);
    toast({ title: "Copiado!", msg: "Snippet copiado para a área de transferência.", kind: "success" });
  };

  // URL real do host atual — sem hardcode da nip.io
  const baseUrl = window.location.origin;
  // Versão atual do servidor (informativa no snippet)
  const [ver, setVer] = useState('');
  useEffect(() => {
    fetch('/api/version').then((r) => r.json()).then((j) => setVer(j.version || '')).catch(() => {});
  }, []);
  const versionTag = ver ? `?v=${ver}` : '';
  const embedCode = `<!-- SINTRACON Chatbot Widget -->
<link rel="stylesheet" href="${baseUrl}/css/widget.css${versionTag}">
<script src="${baseUrl}/js/chat-widget.js${versionTag}"></script>`;

  return (
    <>
      <div className="card">
        <div className="card-header">
          <h3><i className="fas fa-globe"></i> Embedar o widget no site</h3>
          <span className="badge b-bl"><i className="fas fa-check"></i> 3 domínios autorizados</span>
        </div>
        <p style={{ fontSize: 12.5, color: "var(--t2)", margin: 0, marginBottom: 12 }}>
          Para adicionar o chatbot em qualquer página do site SINTRACON, cole o snippet abaixo logo antes do fechamento de <code>{`</body>`}</code>:
        </p>
        <div className="code-block">
          <button className="btn btn-ghost btn-sm copy" onClick={() => copy(embedCode)}><i className="fas fa-copy"></i> Copiar</button>
          {embedCode}
        </div>
        <div style={{ display: "flex", gap: 14, alignItems: "flex-start", marginTop: 14, padding: 12, background: "var(--bg1)", borderRadius: "var(--r-input)", border: "1px solid var(--b1)" }}>
          <i className="fas fa-info-circle" style={{ color: "var(--bl)", marginTop: 2 }}></i>
          <div style={{ fontSize: 12, color: "var(--t2)", lineHeight: 1.55 }}>
            <strong>CORS:</strong> o domínio do site precisa estar autorizado. Use o endpoint <code style={{ background: "var(--bg2)", padding: "1px 5px", borderRadius: 3 }}>POST /api/admin/origins</code> para adicionar o domínio.
            <br />
            <strong>Customização visual:</strong> cores, mascote, fonte, voz e mensagem são controlados pelo painel — nenhuma edição de código necessária.
          </div>
        </div>
      </div>

      <div className="card">
        <div className="card-header">
          <h3><i className="fas fa-plug"></i> Endpoints públicos</h3>
          <span className="meta">sem autenticação · chamados pelo widget</span>
        </div>
        {Data.endpoints.public.map((e, i) => <Endpoint key={i} ep={e} />)}
      </div>

      <div className="card">
        <div className="card-header">
          <h3><i className="fas fa-lock"></i> Endpoints admin <span className="badge b-am" style={{ marginLeft: 4 }}><i className="fas fa-key"></i> JWT</span></h3>
          <span className="meta">requerem <code>Authorization: Bearer &lt;token&gt;</code></span>
        </div>
        <ApiGroup title="Autenticação" eps={Data.endpoints.auth} />
        <div className="divider"></div>
        <ApiGroup title="Configuração & Persona" eps={Data.endpoints.config} />
        <div className="divider"></div>
        <ApiGroup title="Conhecimento (RAG)" eps={Data.endpoints.rag} />
      </div>

      <div className="card">
        <div className="card-header">
          <h3><i className="fas fa-mobile-alt"></i> Integração mobile (iOS/Android)</h3>
        </div>
        <p style={{ fontSize: 12.5, color: "var(--t2)", lineHeight: 1.55 }}>
          Para o app SINTRACON, há duas opções:
        </p>
        <ol style={{ fontSize: 12.5, color: "var(--t2)", lineHeight: 1.7, paddingLeft: 18 }}>
          <li><strong>WebView</strong>: a forma mais rápida — abrir <code style={{ background: "var(--bg1)", padding: "1px 5px", borderRadius: 3 }}>{baseUrl}</code> dentro de um WebView. Funciona com o widget já implementado.</li>
          <li><strong>API REST nativa</strong>: para experiência 100% nativa, consumir <code style={{ background: "var(--bg1)", padding: "1px 5px", borderRadius: 3 }}>/api/v1/chat/stream</code> + <code style={{ background: "var(--bg1)", padding: "1px 5px", borderRadius: 3 }}>/api/tts</code>.</li>
        </ol>
      </div>
    </>
  );
}

function ApiGroup({ title, eps }) {
  return (
    <div style={{ marginBottom: 4 }}>
      <div className="sidebar-section-label" style={{ padding: "10px 4px 6px" }}>{title}</div>
      {eps.map((e, i) => <Endpoint key={i} ep={e} />)}
    </div>
  );
}

function Endpoint({ ep }) {
  return (
    <div className="endpoint">
      <span className={"ep-method " + ep.m}>{ep.m}</span>
      <div className="ep-body">
        <span className="ep-path">{ep.p}</span>
        <span className="ep-desc">{ep.d}</span>
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────
// LlmProviderCard — workbench Unbox: banner do provider ativo + alternativas
// colapsáveis com expand inline (API key + modelo + ações)
// ─────────────────────────────────────────────────────────────────────────

// Metadata visual por provider (mark, tier, latência, preço médio por 1M tokens
// em USD — só pra exibição; cálculo de custo real vem do backend).
const PROV_META = {
  ollama:   { mark: '🦙', tier: 'Grátis',     tierColor: 'gr', tagline: 'local · grátis',            latency: '~820ms', pricePer1M: 0,    desc: 'Roda no servidor. Sem custo, mais lento.' },
  groq:     { mark: '⚡', tier: 'Rápido',     tierColor: 'bl', tagline: 'mais rápido — recomendado', latency: '~140ms', pricePer1M: 0.59, desc: '10–50× mais rápido. Free tier: 30 req/min.' },
  gemini:   { mark: 'G',  tier: 'Balanceado', tierColor: 'or', tagline: 'balanceado',                 latency: '~320ms', pricePer1M: 0.39, desc: 'Boa qualidade, preço competitivo.' },
  openai:   { mark: '○',  tier: 'Premium',    tierColor: 'rd', tagline: 'ChatGPT · referência',      latency: '~410ms', pricePer1M: 0.78, desc: 'Maior qualidade média, mais caro.' },
  deepseek: { mark: '◆',  tier: 'Econômico',  tierColor: 'gr', tagline: 'muito barato',               latency: '~520ms', pricePer1M: 0.27, desc: 'Menor custo entre APIs pagas.' },
};

function LlmProviderCard() {
  const toast = useToast();
  const [data, setData] = useState(null);
  const [busy, setBusy] = useState(false);
  const [keyDrafts, setKeyDrafts] = useState({});      // por provider id
  const [showKey, setShowKey] = useState({});          // toggle visibilidade
  const [expanded, setExpanded] = useState({});        // qual provider está expandido
  const [modelDrafts, setModelDrafts] = useState({});  // modelo selecionado por provider
  const load = () => api('/api/admin/llm/providers').then((d) => {
    setData(d);
    // hidrata modelDrafts com o modelo atualmente selecionado por provider
    const drafts = {};
    (d.providers || []).forEach((p) => { drafts[p.id] = p.selectedModel; });
    setModelDrafts((prev) => ({ ...drafts, ...prev }));
  }).catch((e) => toast({ title: "Falha", msg: e.message, kind: "error" }));
  useEffect(() => { load(); }, []);

  if (!data) {
    return (
      <div className="card">
        <div className="card-header"><h3><i className="fas fa-brain"></i> Provider de LLM</h3></div>
        <div style={{ textAlign: 'center', padding: 20, color: 'var(--t3)' }}>
          <i className="fas fa-circle-notch fa-spin"></i> Carregando…
        </div>
      </div>
    );
  }

  const activeProvider = data.active.provider;

  const selectProvider = async (provider, model) => {
    setBusy(true);
    try {
      await api('/api/admin/llm/select', { method: 'POST', body: { provider, model } });
      toast({ title: "Provider ativo", msg: `${provider} / ${model}`, kind: "success" });
      await load();
    } catch (e) { toast({ title: "Falha", msg: e.message, kind: "error" }); }
    finally { setBusy(false); }
  };

  const saveKey = async (provider) => {
    const key = keyDrafts[provider];
    if (key == null) return;
    setBusy(true);
    try {
      await api('/api/admin/llm/api-key', { method: 'POST', body: { provider, api_key: key } });
      toast({ title: "API key salva", kind: "success" });
      setKeyDrafts((d) => ({ ...d, [provider]: undefined }));
      await load();
    } catch (e) { toast({ title: "Falha", msg: e.message, kind: "error" }); }
    finally { setBusy(false); }
  };

  const testProvider = async (provider) => {
    try {
      const r = await api('/api/admin/llm/ping', { method: 'POST', body: { provider, api_key: keyDrafts[provider] } });
      if (r.ok) toast({ title: "Conexão testada", msg: `API key válida ✓ — ${r.models_available || 0} modelos`, kind: "success" });
      else toast({ title: "Conexão falhou", msg: r.error || 'sem detalhes', kind: "error" });
    } catch (e) { toast({ title: "Falha", msg: e.message, kind: "error" }); }
  };

  const active = data.providers.find((p) => p.id === activeProvider) || data.providers[0];
  const activeMeta = PROV_META[active.id] || { mark: '?', tier: '—', tierColor: 'gr', latency: '', pricePer1M: 0, desc: '' };
  const activeBrl = (activeMeta.pricePer1M * data.usd_to_brl);
  const activeModelDraft = modelDrafts[active.id] || active.selectedModel;

  return (
    <div className="card">
      <div className="card-header">
        <h3><i className="fas fa-bolt"></i> Provider de LLM</h3>
        <span className="badge b-gr"><span className="dot"></span> ativo: {active.label.split(' ')[0]} · {activeModelDraft.split(':')[0]}</span>
      </div>

      {/* Active banner */}
      <div className="prov-active">
        <div className="prov-active-mark" data-mark={active.id}>{activeMeta.mark}</div>
        <div className="prov-active-body">
          <div className="prov-active-name">
            {active.label}
            <span className={"badge b-" + activeMeta.tierColor}>{activeMeta.tier}</span>
            <span className="prov-active-dot"></span>
            <span className="prov-active-status">ativo · responde ao usuário</span>
          </div>
          <div className="prov-active-meta">
            <span><i className="fas fa-microchip"></i> {activeModelDraft}</span>
            <span><i className="fas fa-bolt"></i> {activeMeta.latency}</span>
            <span><i className="fas fa-dollar-sign"></i> {activeMeta.pricePer1M === 0 ? 'grátis' : `R$ ${activeBrl.toFixed(2)} / 1M tokens`}</span>
          </div>
        </div>
        <select
          className="input prov-active-select"
          value={activeModelDraft}
          onChange={(e) => {
            setModelDrafts((d) => ({ ...d, [active.id]: e.target.value }));
            selectProvider(active.id, e.target.value);
          }}
          disabled={busy}
        >
          {active.models.map((m) => <option key={m.id} value={m.id}>{m.label}</option>)}
        </select>
      </div>

      <div className="prov-hint">
        <i className="fas fa-info-circle"></i>
        <span>A troca aplica imediato na próxima pergunta. Custos no card <strong>Custos da IA</strong> do Dashboard.</span>
      </div>

      {/* Alternatives */}
      <div className="prov-alt-label">Alternativas — clique para selecionar</div>
      <div className="prov-grid">
        {data.providers.map((p) => {
          const meta = PROV_META[p.id] || { mark: '?', tier: '—', tierColor: 'gr', latency: '', pricePer1M: 0, desc: '' };
          const isActive = p.id === activeProvider;
          const isExpanded = !!expanded[p.id];
          const draft = keyDrafts[p.id];
          const dirty = draft != null && draft !== '';
          const showK = showKey[p.id];
          const isPaid = p.requiresKey;
          const configured = p.keyConfigured || !isPaid; // free providers are always "configured"
          const modelDraft = modelDrafts[p.id] || p.selectedModel;
          const brlPer1M = (meta.pricePer1M * data.usd_to_brl);

          return (
            <div key={p.id} className={"prov-card" + (isActive ? ' active' : '') + (isExpanded ? ' expanded' : '')}>
              <button
                type="button"
                className="prov-card-head"
                onClick={() => setExpanded((e) => ({ ...e, [p.id]: !e[p.id] }))}
              >
                <span className="prov-mark" data-mark={p.id}>{meta.mark}</span>
                <span className="prov-card-info">
                  <span className="prov-card-name">
                    {p.label.replace(/\s*\(.*$/, '')}
                    {isActive && <span className="prov-active-pill"><span className="dot"></span>ativo</span>}
                  </span>
                  <span className="prov-card-tag">{meta.tagline}</span>
                </span>
                <span className="prov-card-meta">
                  <span className={"prov-tier b-" + meta.tierColor}>{meta.tier}</span>
                  <span className="prov-price">
                    {meta.pricePer1M === 0
                      ? <span className="prov-free">grátis</span>
                      : <>R$ {brlPer1M.toFixed(2)}<small>/1M</small></>
                    }
                  </span>
                  <span className={"prov-status " + (configured ? 'ok' : 'warn')}>
                    <i className={"fas " + (configured ? 'fa-check-circle' : 'fa-key')}></i>
                    {configured ? 'configurado' : 'sem API key'}
                  </span>
                </span>
                <i className={"fas fa-chevron-down prov-chev" + (isExpanded ? ' open' : '')}></i>
              </button>

              {isExpanded && (
                <div className="prov-card-expand">
                  <div className="prov-expand-row">
                    <div className="field" style={{ flex: 1, minWidth: 0 }}>
                      <label>Modelo</label>
                      <select
                        className="input"
                        value={modelDraft}
                        onChange={(e) => {
                          const newModel = e.target.value;
                          setModelDrafts((d) => ({ ...d, [p.id]: newModel }));
                          // Se este provider ja e o ativo, salva o modelo
                          // imediatamente — antes o user mudava o select e o
                          // botao "ativar" ficava disabled (ja era ativo),
                          // entao o save nunca acontecia e o refresh voltava
                          // pro modelo antigo.
                          if (isActive) selectProvider(p.id, newModel);
                        }}
                      >
                        {p.models.map((m) => <option key={m.id} value={m.id}>{m.label}</option>)}
                      </select>
                    </div>
                    {isPaid && (
                      <div className="field" style={{ flex: 2, minWidth: 0 }}>
                        <label>API key</label>
                        <div className="prov-key-row">
                          <input
                            type={showK ? 'text' : 'password'}
                            className="input"
                            placeholder={p.keyConfigured ? `salva (${p.keyMasked}) — digite pra substituir` : `cole sua API key ${p.label.split(' ')[0]} aqui`}
                            value={draft || ''}
                            onChange={(e) => setKeyDrafts((d) => ({ ...d, [p.id]: e.target.value }))}
                          />
                          <button type="button" className="btn btn-ghost btn-sm" onClick={() => setShowKey((s) => ({ ...s, [p.id]: !s[p.id] }))} title={showK ? 'ocultar' : 'mostrar'}>
                            <i className={"fas " + (showK ? 'fa-eye-slash' : 'fa-eye')}></i>
                          </button>
                        </div>
                      </div>
                    )}
                  </div>

                  <div className="prov-expand-actions">
                    <span className="prov-expand-desc">{meta.desc}</span>
                    <div className="row gap-sm">
                      {isPaid && p.signup && !p.keyConfigured && (
                        <a className="link-or" href={p.signup} target="_blank" rel="noopener">obter key <i className="fas fa-external-link-alt"></i></a>
                      )}
                      {isPaid && (
                        <button type="button" className="btn btn-ghost btn-sm" onClick={() => testProvider(p.id)}>
                          <i className="fas fa-vial"></i> testar
                        </button>
                      )}
                      {isPaid && (
                        <button type="button" className="btn btn-soft btn-sm" onClick={() => saveKey(p.id)} disabled={!dirty || busy}>
                          <i className="fas fa-save"></i> salvar key
                        </button>
                      )}
                      <button
                        type="button"
                        className={"btn btn-sm " + (isActive ? 'btn-ghost' : 'btn-primary')}
                        disabled={isActive || !configured || busy}
                        onClick={() => selectProvider(p.id, modelDraft)}
                      >
                        {isActive ? <><i className="fas fa-check"></i> em uso</> : <>ativar como principal <i className="fas fa-arrow-right"></i></>}
                      </button>
                    </div>
                  </div>
                </div>
              )}
            </div>
          );
        })}
      </div>

      <div className="prov-footer">
        <i className="fas fa-exchange-alt"></i>
        <span>Cotação USD→BRL usada no cálculo de custo: <strong>R$ {data.usd_to_brl.toFixed(2)}</strong></span>
        <ExchangeRateEditor current={data.usd_to_brl} onSaved={load} />
      </div>
    </div>
  );
}

function ExchangeRateEditor({ current, onSaved }) {
  const toast = useToast();
  const [editing, setEditing] = useState(false);
  const [val, setVal] = useState(String(current));
  const save = () => {
    const n = parseFloat(val);
    if (!n || n <= 0) { toast({ title: "Valor inválido", kind: "info" }); return; }
    api('/api/admin/llm/exchange-rate', { method: 'POST', body: { rate: n } })
      .then(() => { toast({ title: "Cotação atualizada", kind: "success" }); setEditing(false); onSaved(); })
      .catch((e) => toast({ title: "Falha", msg: e.message, kind: "error" }));
  };
  if (!editing) {
    return <button type="button" className="btn btn-ghost btn-sm" style={{ marginLeft: 8, fontSize: 10.5 }} onClick={() => setEditing(true)}>editar cotação</button>;
  }
  return (
    <span style={{ marginLeft: 8, display: 'inline-flex', gap: 4, alignItems: 'center' }}>
      <input type="number" step="0.01" className="input" value={val} onChange={(e) => setVal(e.target.value)} style={{ width: 80, padding: '2px 6px' }} />
      <button type="button" className="btn btn-primary btn-sm" onClick={save}>ok</button>
      <button type="button" className="btn btn-ghost btn-sm" onClick={() => setEditing(false)}>cancelar</button>
    </span>
  );
}


// ─────────────────────────────────────────────────────────────────────────
// TtsProviderCard — mesma estrutura do LlmProviderCard, adaptada pra TTS
// ─────────────────────────────────────────────────────────────────────────
function TtsProviderCard() {
  const toast = useToast();
  const confirm = useConfirm();
  const [data, setData] = useState(null);
  const [busy, setBusy] = useState(false);
  const [keyDrafts, setKeyDrafts] = useState({});
  const [showKey, setShowKey] = useState({});
  const [expanded, setExpanded] = useState({});
  const [modelDrafts, setModelDrafts] = useState({});
  const [voiceDrafts, setVoiceDrafts] = useState({});
  const [previewBusy, setPreviewBusy] = useState(false);

  const load = () => api('/api/admin/tts/providers').then((d) => {
    setData(d);
    const m = {}, v = {};
    (d.providers || []).forEach((p) => { m[p.id] = p.selectedModel; v[p.id] = p.selectedVoice; });
    setModelDrafts((prev) => ({ ...m, ...prev }));
    setVoiceDrafts((prev) => ({ ...v, ...prev }));
  }).catch((e) => toast({ title: "Falha", msg: e.message, kind: "error" }));
  useEffect(() => { load(); }, []);

  if (!data) {
    return (
      <div className="card">
        <div className="card-header"><h3><i className="fas fa-volume-up"></i> Provider de TTS (voz do bot)</h3></div>
        <div style={{ textAlign: 'center', padding: 20, color: 'var(--t3)' }}>
          <i className="fas fa-circle-notch fa-spin"></i> Carregando…
        </div>
      </div>
    );
  }

  const activeProvider = data.active.provider;
  const active = data.providers.find((p) => p.id === activeProvider) || data.providers[0];
  const activeModel = modelDrafts[active.id] || active.selectedModel;
  const activeVoice = voiceDrafts[active.id] || active.selectedVoice;

  const selectProvider = async (provider, model, voice) => {
    setBusy(true);
    try {
      await api('/api/admin/tts/select', { method: 'POST', body: { provider, model, voice } });
      toast({ title: "Provider de voz ativo", msg: `${provider}` + (model ? ' / ' + model : ''), kind: "success" });
      await load();
    } catch (e) { toast({ title: "Falha", msg: e.message, kind: "error" }); }
    finally { setBusy(false); }
  };

  const saveKey = async (provider) => {
    const key = keyDrafts[provider]; if (key == null) return;
    setBusy(true);
    try {
      await api('/api/admin/tts/api-key', { method: 'POST', body: { provider, api_key: key } });
      toast({ title: "API key salva", kind: "success" });
      setKeyDrafts((d) => ({ ...d, [provider]: undefined }));
      await load();
    } catch (e) { toast({ title: "Falha", msg: e.message, kind: "error" }); }
    finally { setBusy(false); }
  };

  const testProvider = async (provider) => {
    try {
      const r = await api('/api/admin/tts/ping', { method: 'POST', body: { provider, api_key: keyDrafts[provider] } });
      if (r.ok) toast({ title: "Conexão testada", msg: `✓ ${r.voices_available || 0} vozes`, kind: "success" });
      else toast({ title: "Conexão falhou", msg: r.error || 'sem detalhes', kind: "error" });
    } catch (e) { toast({ title: "Falha", msg: e.message, kind: "error" }); }
  };

  const previewActive = async () => {
    setPreviewBusy(true);
    try {
      const r = await fetch('/api/admin/tts/preview', {
        method: 'POST',
        headers: { 'Authorization': 'Bearer ' + Auth.token, 'Content-Type': 'application/json' },
        body: JSON.stringify({}),
      });
      if (!r.ok) throw new Error('HTTP ' + r.status);
      const blob = await r.blob();
      const url = URL.createObjectURL(blob);
      const audio = new Audio(url);
      audio.onended = () => URL.revokeObjectURL(url);
      await audio.play();
    } catch (e) { toast({ title: "Falha no preview", msg: e.message, kind: "error" }); }
    finally { setPreviewBusy(false); }
  };

  const clearCache = async () => {
    const ok = await confirm({
      title: 'Limpar cache de áudio?',
      message: <>Apagar todos os áudios cacheados ({data.cache_stats.entries} entradas, {((data.cache_stats.total_bytes || 0) / (1024 * 1024)).toFixed(1)} MB)?</>,
      details: 'Próximas respostas precisarão gerar áudio novamente (custo). Útil pra forçar refresh quando trocar de provider/voz e quiser ouvir o novo.',
      confirmLabel: 'Limpar cache',
      danger: true,
      icon: 'fa-trash-alt',
    });
    if (!ok) return;
    try {
      await api('/api/admin/tts/cache/clear', { method: 'POST' });
      toast({ title: "Cache de áudio limpo", kind: "success" });
      await load();
    } catch (e) { toast({ title: "Falha", msg: e.message, kind: "error" }); }
  };

  const usdBrl = 5.20; // poderia vir do system_config, mas TTS card é independente

  return (
    <div className="card">
      <div className="card-header">
        <h3><i className="fas fa-volume-up"></i> Provider de TTS (voz que o bot fala)</h3>
        <span className="badge b-gr"><span className="dot"></span> ativo: {active.label.replace(/\s*\(.*$/, '')}</span>
      </div>

      <div className="prov-active">
        <div className="prov-active-mark" data-mark={active.id}>{active.mark}</div>
        <div className="prov-active-body">
          <div className="prov-active-name">
            {active.label}
            <span className={"badge b-" + active.tierColor}>{active.tier}</span>
            <span className="prov-active-dot"></span>
            <span className="prov-active-status">ativo · gera o áudio</span>
          </div>
          <div className="prov-active-meta">
            <span><i className="fas fa-microphone"></i> {activeVoice}</span>
            <span><i className="fas fa-microchip"></i> {activeModel}</span>
            <span><i className="fas fa-dollar-sign"></i> {active.pricePer1M === 0 ? 'grátis' : `R$ ${(active.pricePer1M * usdBrl).toFixed(2)} / 1M chars`}</span>
          </div>
        </div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 6, minWidth: 240 }}>
          <select
            className="input"
            value={activeVoice}
            onChange={(e) => { setVoiceDrafts((d) => ({ ...d, [active.id]: e.target.value })); selectProvider(active.id, activeModel, e.target.value); }}
            disabled={busy}
          >
            {active.voices.map((v) => <option key={v.id} value={v.id}>{v.label}</option>)}
          </select>
          <button type="button" className="btn btn-soft btn-sm" onClick={previewActive} disabled={previewBusy} style={{ justifyContent: 'center' }}>
            {previewBusy ? <><i className="fas fa-circle-notch fa-spin"></i> tocando…</> : <><i className="fas fa-play"></i> ouvir preview</>}
          </button>
        </div>
      </div>

      <div className="prov-hint">
        <i className="fas fa-info-circle"></i>
        <span>
          A troca aplica imediato — próxima resposta usa a voz nova. Cache: <strong>{data.cache_stats.entries || 0}</strong> áudios guardados
          ({((data.cache_stats.total_bytes || 0) / (1024 * 1024)).toFixed(1)} MB), <strong>{data.cache_stats.total_hits || 0}</strong> hits acumulados.
          <button type="button" className="btn btn-ghost btn-sm" style={{ marginLeft: 8 }} onClick={clearCache}>
            <i className="fas fa-trash-alt"></i> limpar cache
          </button>
        </span>
      </div>

      <div className="prov-alt-label">Alternativas — clique para configurar</div>
      <div className="prov-grid">
        {data.providers.map((p) => {
          const isActive = p.id === activeProvider;
          const isExpanded = !!expanded[p.id];
          const draft = keyDrafts[p.id];
          const dirty = draft != null && draft !== '';
          const showK = showKey[p.id];
          const isPaid = p.requiresKey;
          const configured = p.keyConfigured;
          const modelDraft = modelDrafts[p.id] || p.selectedModel;
          const voiceDraft = voiceDrafts[p.id] || p.selectedVoice;
          const brlPer1M = (p.pricePer1M * usdBrl);

          return (
            <div key={p.id} className={"prov-card" + (isActive ? ' active' : '') + (isExpanded ? ' expanded' : '')}>
              <button type="button" className="prov-card-head" onClick={() => setExpanded((e) => ({ ...e, [p.id]: !e[p.id] }))}>
                <span className="prov-mark" data-mark={p.id}>{p.mark}</span>
                <span className="prov-card-info">
                  <span className="prov-card-name">
                    {p.label.replace(/\s*\(.*$/, '')}
                    {isActive && <span className="prov-active-pill"><span className="dot"></span>ativo</span>}
                  </span>
                  <span className="prov-card-tag">{p.tagline}</span>
                </span>
                <span className="prov-card-meta">
                  <span className={"prov-tier b-" + p.tierColor}>{p.tier}</span>
                  <span className="prov-price">
                    {p.pricePer1M === 0
                      ? <span className="prov-free">grátis</span>
                      : <>R$ {brlPer1M.toFixed(2)}<small>/1M</small></>
                    }
                  </span>
                  <span className={"prov-status " + (configured ? 'ok' : 'warn')}>
                    <i className={"fas " + (configured ? 'fa-check-circle' : 'fa-key')}></i>
                    {configured ? 'configurado' : 'sem API key'}
                  </span>
                </span>
                <i className={"fas fa-chevron-down prov-chev" + (isExpanded ? ' open' : '')}></i>
              </button>

              {isExpanded && (
                <div className="prov-card-expand">
                  <div className="prov-expand-row">
                    <div className="field" style={{ flex: 1, minWidth: 0 }}>
                      <label>Modelo</label>
                      <select className="input" value={modelDraft} onChange={(e) => setModelDrafts((d) => ({ ...d, [p.id]: e.target.value }))}>
                        {p.models.map((m) => <option key={m.id} value={m.id}>{m.label}</option>)}
                      </select>
                    </div>
                    <div className="field" style={{ flex: 1, minWidth: 0 }}>
                      <label>Voz</label>
                      <select className="input" value={voiceDraft} onChange={(e) => setVoiceDrafts((d) => ({ ...d, [p.id]: e.target.value }))}>
                        {p.voices.map((v) => <option key={v.id} value={v.id}>{v.label}</option>)}
                      </select>
                    </div>
                    {isPaid && (
                      <div className="field" style={{ flex: 2, minWidth: 0 }}>
                        <label>API key</label>
                        <div className="prov-key-row">
                          <input
                            type={showK ? 'text' : 'password'}
                            className="input"
                            placeholder={p.keyConfigured ? `salva (${p.keyMasked}) — digite pra substituir` : `cole a API key ${p.label.replace(/\s*\(.*$/, '')} aqui`}
                            value={draft || ''}
                            onChange={(e) => setKeyDrafts((d) => ({ ...d, [p.id]: e.target.value }))}
                          />
                          <button type="button" className="btn btn-ghost btn-sm" onClick={() => setShowKey((s) => ({ ...s, [p.id]: !s[p.id] }))} title={showK ? 'ocultar' : 'mostrar'}>
                            <i className={"fas " + (showK ? 'fa-eye-slash' : 'fa-eye')}></i>
                          </button>
                        </div>
                      </div>
                    )}
                  </div>

                  <div className="prov-expand-actions">
                    <span className="prov-expand-desc">{p.desc}</span>
                    <div className="row gap-sm">
                      {isPaid && p.signup && !p.keyConfigured && (
                        <a className="link-or" href={p.signup} target="_blank" rel="noopener">obter key <i className="fas fa-external-link-alt"></i></a>
                      )}
                      {isPaid && (
                        <button type="button" className="btn btn-ghost btn-sm" onClick={() => testProvider(p.id)}>
                          <i className="fas fa-vial"></i> testar
                        </button>
                      )}
                      {isPaid && (
                        <button type="button" className="btn btn-soft btn-sm" onClick={() => saveKey(p.id)} disabled={!dirty || busy}>
                          <i className="fas fa-save"></i> salvar key
                        </button>
                      )}
                      <button
                        type="button"
                        className={"btn btn-sm " + (isActive ? 'btn-ghost' : 'btn-primary')}
                        disabled={isActive || !configured || busy}
                        onClick={() => selectProvider(p.id, modelDraft, voiceDraft)}
                      >
                        {isActive ? <><i className="fas fa-check"></i> em uso</> : <>ativar como principal <i className="fas fa-arrow-right"></i></>}
                      </button>
                    </div>
                  </div>
                </div>
              )}
            </div>
          );
        })}
      </div>

      <div className="prov-footer">
        <i className="fas fa-headphones"></i>
        <span>
          O cache armazena cada frase gerada (hash do texto + voz + provider). Respostas frequentes
          ("Como me filiar?", "Qual o endereço?") tocam de cache em &lt;100ms, custo R$ 0.
        </span>
      </div>
    </div>
  );
}

window.ScreenSystem = ScreenSystem;
