"use client";

import { useCallback, useEffect, useRef, useState, type FormEvent, type ReactNode } from "react";
import { Ban, Check, ChevronRight, Copy, Eye, FileText, KeyRound, Loader2, MessageCircle, MessageSquareText, PenLine, PlugZap, RefreshCw, Search, Undo2, Unplug, X, XCircle } from "lucide-react";

import { CabecalhoDoPainel, Opcao, Painel } from "@/components/assinatura/primitivos";
import { Aviso } from "@/components/ui/aviso";
import { Button } from "@/components/ui/button";
import CaixaDeConfirmacao, { type Confirmacao } from "@/components/ui/caixa-de-confirmacao";
import { Input } from "@/components/ui/input";
import {
  aceitaChave,
  CHAVE_DE_EXEMPLO,
  comoConectar,
  comoConectarComChave,
  PROGRAMAS,
  PROGRAMAS_DA_CHAVE,
  type Instrucao,
  type Programa,
  type ProgramaDaChave,
} from "@/lib/assistentes/como-conectar";
import type { AtividadeNaLista, ChaveNaLista } from "@/lib/assistentes/painel";
import type { PersonalizacaoNaLista } from "@/lib/assistentes/personalizacoes/servidor";

import ImportacoesDosAssistentes from "./importacoes-dos-assistentes";

/**
 * A aba "Assistentes e Apps" de Integrações.
 *
 * É aqui que a pessoa aprende a ligar o Claude, o ChatGPT ou outro programa à
 * conta dela, vê o que está ligado (as conexões feitas pelo botão Conectar e
 * as chaves de acesso), desliga, e acompanha o que os assistentes fizeram.
 *
 * A chave inteira passa por esta tela uma única vez, na hora em que nasce, e
 * mora só na memória do componente: nunca vai para o armazenamento do
 * navegador. A conexão pelo botão nem isso: o segredo dela vai direto do
 * servidor para o assistente.
 */

export type DadosDosAssistentes = {
  /* Dono e admin enxergam as chaves de todo mundo da empresa. */
  verTodas: boolean;
  endereco: string;
  maximo: number;
  chaves: ChaveNaLista[];
};

type LinhaDeAtividade = AtividadeNaLista & { titulo: string };

/** Lê a resposta de `/api/assistentes/chaves`. `null` = a aba não aparece. */
export function lerDadosDosAssistentes(json: unknown): DadosDosAssistentes | null {
  const bruto = json as { liberado?: unknown; pode?: unknown; verTodas?: unknown; endereco?: unknown; maximo?: unknown; chaves?: unknown } | null;
  if (!bruto || bruto.liberado !== true || bruto.pode !== true || typeof bruto.endereco !== "string") return null;
  return {
    verTodas: bruto.verTodas === true,
    endereco: bruto.endereco,
    maximo: Number(bruto.maximo) || 10,
    chaves: Array.isArray(bruto.chaves) ? (bruto.chaves as ChaveNaLista[]) : [],
  };
}

const EXEMPLOS = [
  "Como está o meu dia?",
  "Emita a nota fiscal do pedido 22 e me mande o PDF.",
  "Quanto tenho a receber este mês? Dê baixa na conta da gráfica parceira.",
  "Monte um orçamento de 100 canecas para a Maria.",
  "Crie o cupom BLACK10 com 10% de desconto até domingo.",
  "Marque uma visita com o João na quinta, às 10h.",
  "Exporte meus clientes numa planilha.",
  "Quando um orçamento ficar 3 dias sem resposta, me lembre de ligar.",
  "Monte um painel com o faturamento por vendedor deste mês.",
  "Traga os produtos da minha loja antiga: www.minhaloja.com.br.",
];

const DIA = 24 * 60 * 60 * 1000;

/* `agora` vem de quem desenha: o relógio é lido uma vez por atualização da
   tela, e não a cada linha. */
function quando(iso: string | null, agora: number): string {
  if (!iso) return "";
  const data = new Date(iso);
  const passou = agora - data.getTime();
  if (!Number.isFinite(passou)) return "";
  const minutos = Math.floor(passou / 60000);
  if (minutos < 1) return "agora";
  if (minutos < 60) return `há ${minutos} min`;
  const horas = Math.floor(minutos / 60);
  if (horas < 24) return `há ${horas} h`;
  const dias = Math.floor(horas / 24);
  if (dias === 1) return "ontem";
  if (dias < 7) return `há ${dias} dias`;
  return dataCurta(iso);
}

function dataCurta(iso: string): string {
  const data = new Date(iso);
  return Number.isFinite(data.getTime()) ? data.toLocaleDateString("pt-BR", { day: "2-digit", month: "2-digit", year: "numeric" }) : "";
}

/* O programa se apresenta com o nome técnico dele ("claude-code",
   "codex-mcp-client"). Na tela vai o nome que a pessoa conhece. */
function nomeDoPrograma(bruto: string | null): string | null {
  if (!bruto) return null;
  const nome = bruto.toLowerCase();
  if (nome.includes("claude")) return nome.includes("code") ? "Claude Code" : "Claude";
  if (nome.includes("codex")) return "Codex";
  if (nome.includes("cursor")) return "Cursor";
  if (nome.includes("chatgpt") || nome.includes("openai")) return "ChatGPT";
  return bruto;
}

async function copiar(texto: string): Promise<boolean> {
  try {
    if (navigator.clipboard?.writeText) {
      await navigator.clipboard.writeText(texto);
      return true;
    }
  } catch {
    /* Sem permissão ou fora de https: tenta do jeito antigo. */
  }
  try {
    const area = document.createElement("textarea");
    area.value = texto;
    area.setAttribute("readonly", "");
    area.style.position = "fixed";
    area.style.opacity = "0";
    document.body.appendChild(area);
    area.select();
    const deu = document.execCommand("copy");
    area.remove();
    return deu;
  } catch {
    return false;
  }
}

function BotaoDeCopiar({
  texto,
  rotulo = "Copiar",
  aoCopiar,
  escuro = false,
}: {
  texto: string;
  rotulo?: string;
  aoCopiar?: () => void;
  escuro?: boolean;
}) {
  const [estado, setEstado] = useState<"parado" | "copiado" | "falhou">("parado");
  const relogio = useRef<number | null>(null);

  useEffect(
    () => () => {
      if (relogio.current) window.clearTimeout(relogio.current);
    },
    []
  );

  const aoClicar = async () => {
    const deu = await copiar(texto);
    setEstado(deu ? "copiado" : "falhou");
    if (deu) aoCopiar?.();
    if (relogio.current) window.clearTimeout(relogio.current);
    relogio.current = window.setTimeout(() => setEstado("parado"), 2200);
  };

  const cor = escuro
    ? "text-white/85 hover:bg-white/10 hover:text-white"
    : "text-[var(--ks-color-primary)] hover:bg-[var(--ks-color-bg-hover)]";

  return (
    <button
      type="button"
      onClick={() => void aoClicar()}
      className={`inline-flex h-8 shrink-0 items-center gap-1.5 rounded-lg px-2.5 text-xs font-bold transition-colors ${cor}`}
    >
      {estado === "copiado" ? <Check size={14} strokeWidth={2.6} /> : <Copy size={14} />}
      {estado === "copiado" ? "Copiado" : estado === "falhou" ? "Selecione e copie" : rotulo}
    </button>
  );
}

/** O bloco que a pessoa copia e cola. Escuro, para parecer com o lugar onde vai ser colado. */
function Trecho({ texto, destaque, rotulo, aoCopiar }: { texto: string; destaque?: string; rotulo?: string; aoCopiar?: () => void }) {
  const partes = destaque ? texto.split(destaque) : [texto];
  return (
    <div className="overflow-hidden rounded-xl bg-[#17152b]">
      <pre className="overflow-x-auto whitespace-pre-wrap px-4 pb-3 pt-3.5 font-mono text-[12.5px] leading-6 text-[#e4e0ff] [overflow-wrap:anywhere]">
        {partes.map((parte, indice) => (
          <span key={indice}>
            {parte}
            {indice < partes.length - 1 ? <span className="rounded bg-[#7346e8] px-1 font-bold text-white">{destaque}</span> : null}
          </span>
        ))}
      </pre>
      <div className="flex justify-end border-t border-white/10 px-2 py-1.5">
        <BotaoDeCopiar texto={texto} rotulo={rotulo} aoCopiar={aoCopiar} escuro />
      </div>
    </div>
  );
}

function Selo({ cor, children }: { cor: string; children: ReactNode }) {
  return (
    <span className="inline-flex items-center gap-1.5 whitespace-nowrap text-xs font-semibold text-[var(--ks-color-text-secondary)]">
      <span className={`h-1.5 w-1.5 shrink-0 rounded-full ${cor}`} />
      {children}
    </span>
  );
}

function Rotulo({ children }: { children: ReactNode }) {
  return <p className="text-[11px] font-bold uppercase tracking-[0.14em] text-[var(--ks-color-text-soft)]">{children}</p>;
}

function Passos({ passos }: { passos: string[] }) {
  return (
    <ol className="space-y-2.5">
      {passos.map((passo, indice) => (
        <li key={passo} className="flex gap-3 text-sm leading-6 text-[var(--ks-color-text-secondary)]">
          <span className="ks-num mt-0.5 flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-[var(--ks-color-primary)] text-[11px] font-bold text-white">
            {indice + 1}
          </span>
          <span>{passo}</span>
        </li>
      ))}
    </ol>
  );
}

function AbasDosProgramas<T extends string>({
  programas,
  programa,
  aoTrocarPrograma,
}: {
  programas: ReadonlyArray<{ id: T; nome: string }>;
  programa: T;
  aoTrocarPrograma: (programa: T) => void;
}) {
  return (
    /* Pílulas que quebram linha. Seis abas sublinhadas não cabiam na coluna
       estreita: a última sumia cortada no computador, e no celular empurravam
       a página para os lados. */
    <div role="tablist" className="flex flex-wrap gap-2">
      {programas.map(({ id, nome }) => {
        const ativa = id === programa;
        return (
          <button
            key={id}
            type="button"
            role="tab"
            aria-selected={ativa}
            onClick={() => aoTrocarPrograma(id)}
            className={`h-8 rounded-full px-3.5 text-[13px] font-semibold transition-colors ${
              ativa
                ? "bg-[var(--ks-color-primary)] text-white"
                : "bg-[var(--ks-color-bg-soft)] text-[var(--ks-color-text-secondary)] hover:bg-[var(--ks-color-bg-hover)] hover:text-[var(--ks-color-text-primary)]"
            }`}
          >
            {nome}
          </button>
        );
      })}
    </div>
  );
}

/** O passo a passo de um programa: os passos, o que copiar, e o outro jeito recolhido. */
function MostrarInstrucao({
  instrucao,
  esconderTrecho,
  chave,
  aoCopiar,
}: {
  instrucao: Instrucao;
  /* O trecho é só o endereço, que já está à vista logo acima. */
  esconderTrecho?: boolean;
  /* Com a chave em mãos (logo depois de criar), o trecho já sai pronto. Sem
     ela, o lugar da chave aparece marcado. */
  chave?: string;
  aoCopiar?: () => void;
}) {
  const marcaAChave = !chave && instrucao.trecho.includes(CHAVE_DE_EXEMPLO);
  return (
    <div>
      <div className="mt-4">
        <Passos passos={instrucao.passos} />
      </div>

      {esconderTrecho ? null : (
        <div className="mt-4">
          <Trecho
            texto={instrucao.trecho}
            destaque={marcaAChave ? CHAVE_DE_EXEMPLO : undefined}
            rotulo={chave ? "Copiar já com a chave" : "Copiar"}
            aoCopiar={aoCopiar}
          />
        </div>
      )}

      {instrucao.nota ? <p className="mt-3 text-xs leading-5 text-[var(--ks-color-text-muted)]">{instrucao.nota}</p> : null}

      {/* O segundo caminho fica recolhido: quem usa o primeiro não precisa ler. */}
      {instrucao.outroJeito ? (
        <details className="group mt-3">
          <summary className="flex cursor-pointer list-none items-center gap-1 text-xs font-bold text-[var(--ks-color-primary)] [&::-webkit-details-marker]:hidden">
            <ChevronRight size={14} className="transition-transform group-open:rotate-90" />
            {instrucao.outroJeito.chamada}
          </summary>
          <div className="mt-3">
            <Passos passos={instrucao.outroJeito.passos} />
          </div>
          {instrucao.outroJeito.trecho ? (
            <div className="mt-4">
              <Trecho
                texto={instrucao.outroJeito.trecho}
                destaque={!chave && instrucao.outroJeito.trecho.includes(CHAVE_DE_EXEMPLO) ? CHAVE_DE_EXEMPLO : undefined}
                rotulo={chave ? "Copiar já com a chave" : "Copiar"}
                aoCopiar={aoCopiar}
              />
            </div>
          ) : null}
        </details>
      ) : null}

      {marcaAChave ? (
        <p className="mt-3 text-xs leading-5 text-[var(--ks-color-text-muted)]">
          Troque <strong className="font-semibold text-[var(--ks-color-text-secondary)]">{CHAVE_DE_EXEMPLO}</strong> pela chave que você copiou ao
          criar. Não tem mais a chave? Crie outra: ela aparece inteira só na hora em que nasce.
        </p>
      ) : null}
    </div>
  );
}

/** O caminho do botão Conectar, no painel "Como conectar". */
function PassosPeloBotao({ endereco, programa, aoTrocarPrograma }: { endereco: string; programa: Programa; aoTrocarPrograma: (programa: Programa) => void }) {
  const instrucao = comoConectar(programa, endereco);
  return (
    <div>
      <AbasDosProgramas programas={PROGRAMAS} programa={programa} aoTrocarPrograma={aoTrocarPrograma} />
      <MostrarInstrucao instrucao={instrucao} esconderTrecho={instrucao.trecho === endereco} />
    </div>
  );
}

/** O caminho da chave, na janela que acabou de criar uma. */
function PassosComChave({
  endereco,
  chave,
  programa,
  aoTrocarPrograma,
  aoCopiar,
}: {
  endereco: string;
  chave: string;
  programa: ProgramaDaChave;
  aoTrocarPrograma: (programa: ProgramaDaChave) => void;
  aoCopiar?: () => void;
}) {
  return (
    <div>
      <AbasDosProgramas programas={PROGRAMAS_DA_CHAVE} programa={programa} aoTrocarPrograma={aoTrocarPrograma} />
      <MostrarInstrucao instrucao={comoConectarComChave(programa, endereco, chave)} chave={chave} aoCopiar={aoCopiar} />
    </div>
  );
}

const VALIDADES = [
  { valor: "30", rotulo: "30 dias" },
  { valor: "90", rotulo: "90 dias" },
  { valor: "365", rotulo: "1 ano" },
  { valor: "sem", rotulo: "Sem validade" },
] as const;

function ModalDeNovaChave({
  endereco,
  programa,
  aoTrocarPrograma,
  aoCriar,
  aoFechar,
}: {
  endereco: string;
  programa: ProgramaDaChave;
  aoTrocarPrograma: (programa: ProgramaDaChave) => void;
  /* A chave nasceu: a lista de trás precisa ser lida de novo. */
  aoCriar: () => void;
  aoFechar: () => void;
}) {
  const [nome, setNome] = useState("");
  const [acesso, setAcesso] = useState<"leitura" | "escrita" | null>(null);
  const [validade, setValidade] = useState<(typeof VALIDADES)[number]["valor"]>("90");
  const [criando, setCriando] = useState(false);
  const [erro, setErro] = useState<string | null>(null);
  const [criada, setCriada] = useState<string | null>(null);
  const [copiou, setCopiou] = useState(false);
  const [duvida, setDuvida] = useState<Confirmacao | null>(null);

  const pedirParaFechar = useCallback(() => {
    if (criando) return;
    /* A chave não volta a aparecer. Fechar sem copiar é jogar a chave fora, e
       isso merece uma pergunta. */
    if (criada && !copiou) {
      setDuvida({
        titulo: "Fechar sem copiar a chave?",
        texto: "A chave inteira não aparece de novo. Se fechar agora, vai ser preciso revogar esta e criar outra.",
        confirmar: "Fechar mesmo assim",
        cancelar: "Voltar e copiar",
        tom: "perigo",
        aoConfirmar: aoFechar,
      });
      return;
    }
    aoFechar();
  }, [criando, criada, copiou, aoFechar]);

  useEffect(() => {
    /* Com a pergunta aberta, o Escape é dela. */
    if (duvida) return;
    const aoTeclar = (evento: KeyboardEvent) => {
      if (evento.key === "Escape") pedirParaFechar();
    };
    window.addEventListener("keydown", aoTeclar);
    return () => window.removeEventListener("keydown", aoTeclar);
  }, [duvida, pedirParaFechar]);

  const criar = async (evento: FormEvent) => {
    evento.preventDefault();
    if (!acesso || criando) return;
    setCriando(true);
    setErro(null);
    try {
      const resposta = await fetch("/api/assistentes/chaves", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ nome, acesso, validade: validade === "sem" ? "sem" : Number(validade) }),
      });
      const json = (await resposta.json().catch(() => null)) as { chave?: unknown; error?: unknown } | null;
      if (!resposta.ok || typeof json?.chave !== "string") {
        throw new Error(typeof json?.error === "string" ? json.error : "Não foi possível criar a chave. Tente de novo.");
      }
      setCriada(json.chave);
      aoCriar();
    } catch (falha) {
      setErro(falha instanceof Error ? falha.message : "Não foi possível criar a chave. Tente de novo.");
    } finally {
      setCriando(false);
    }
  };

  return (
    <>
    <div
      role="dialog"
      aria-modal="true"
      aria-label={criada ? "Chave criada" : "Nova chave de acesso"}
      /* Depois que a chave nasce, clicar fora não fecha: seria fácil demais
         perder a única vez em que ela aparece. */
      onClick={criada ? undefined : pedirParaFechar}
      className="fixed inset-0 z-[120] flex items-center justify-center bg-[rgba(12,8,24,0.42)] p-4 backdrop-blur-[2px] sm:p-6"
    >
      <div
        onClick={(evento) => evento.stopPropagation()}
        className="flex max-h-full w-[min(580px,100%)] flex-col overflow-hidden rounded-[18px] bg-[var(--ks-color-bg-elevated)] shadow-[0_24px_60px_rgba(26,26,43,0.28)]"
      >
        <header className="flex items-start justify-between gap-4 border-b border-[var(--ks-color-border-subtle)] px-6 py-4">
          <div>
            <h3 className="text-base font-extrabold tracking-tight text-[var(--ks-color-text-primary)]">
              {criada ? "Chave criada" : "Nova chave de acesso"}
            </h3>
            <p className="mt-0.5 text-sm text-[var(--ks-color-text-muted)]">
              {criada
                ? "Copie agora e cole no seu programa."
                : "Para programas que pedem uma chave. O Claude e o ChatGPT não precisam: eles ligam pelo botão Conectar."}
            </p>
          </div>
          <button
            type="button"
            onClick={pedirParaFechar}
            aria-label="Fechar"
            className="-mr-1.5 rounded-lg p-1.5 text-[var(--ks-color-text-soft)] transition hover:bg-[var(--ks-color-bg-soft)] hover:text-[var(--ks-color-text-secondary)]"
          >
            <X size={18} />
          </button>
        </header>

        {criada ? (
          <>
            <div className="space-y-5 overflow-y-auto px-6 py-5">
              <Aviso tom="atencao" titulo="A chave aparece inteira só desta vez">
                Guarde como uma senha. Se perder, revogue esta e crie outra.
              </Aviso>

              <div>
                <Rotulo>Sua chave</Rotulo>
                <div className="mt-2 flex items-center gap-2 rounded-xl border border-[var(--ks-color-border-strong)] py-1.5 pl-3.5 pr-1.5">
                  <code className="min-w-0 flex-1 break-all font-mono text-[13px] leading-5 text-[var(--ks-color-text-primary)]">{criada}</code>
                  <BotaoDeCopiar texto={criada} rotulo="Copiar chave" aoCopiar={() => setCopiou(true)} />
                </div>
              </div>

              <div>
                <Rotulo>Ligar agora</Rotulo>
                <div className="mt-2">
                  <PassosComChave
                    endereco={endereco}
                    chave={criada}
                    programa={programa}
                    aoTrocarPrograma={aoTrocarPrograma}
                    aoCopiar={() => setCopiou(true)}
                  />
                </div>
              </div>
            </div>
            <footer className="flex justify-end border-t border-[var(--ks-color-border-subtle)] bg-[var(--ks-color-bg-inset)] px-6 py-3.5">
              <Button onClick={pedirParaFechar}>{copiou ? "Concluir" : "Fechar"}</Button>
            </footer>
          </>
        ) : (
          <form onSubmit={criar} className="flex min-h-0 flex-col">
            <div className="space-y-5 overflow-y-auto px-6 py-5">
              <div>
                <Rotulo>Nome</Rotulo>
                <div className="mt-2">
                  <Input
                    autoFocus
                    required
                    maxLength={80}
                    value={nome}
                    onChange={(evento) => setNome(evento.target.value)}
                    placeholder="Ex.: Claude do notebook da loja"
                  />
                </div>
                <p className="mt-1.5 text-xs text-[var(--ks-color-text-muted)]">Para reconhecer depois onde esta chave está sendo usada.</p>
              </div>

              <div>
                <Rotulo>O que esta chave pode fazer</Rotulo>
                <div role="radiogroup" className="mt-2 grid gap-3 sm:grid-cols-2">
                  <Opcao selecionada={acesso === "leitura"} aoEscolher={() => setAcesso("leitura")} titulo="Só consultar">
                    <span className="text-[13px] leading-5 text-[var(--ks-color-text-muted)]">
                      Vê produtos, clientes, orçamentos, pedidos, agenda, estoque e financeiro. Não cria nada.
                    </span>
                  </Opcao>
                  <Opcao selecionada={acesso === "escrita"} aoEscolher={() => setAcesso("escrita")} titulo="Consultar e criar">
                    <span className="text-[13px] leading-5 text-[var(--ks-color-text-muted)]">
                      Além de consultar, opera a conta: cadastros, orçamentos, agenda, cupons, financeiro, nota fiscal, loja, regras e painéis. Nota fiscal, dinheiro, exclusão e cancelamento ele mostra antes e espera você aprovar.
                    </span>
                  </Opcao>
                </div>
              </div>

              <div>
                <Rotulo>Validade</Rotulo>
                <div role="radiogroup" className="mt-2 grid grid-cols-2 gap-2 sm:grid-cols-4">
                  {VALIDADES.map(({ valor, rotulo }) => {
                    const escolhida = validade === valor;
                    return (
                      <button
                        key={valor}
                        type="button"
                        role="radio"
                        aria-checked={escolhida}
                        onClick={() => setValidade(valor)}
                        className={`h-10 rounded-xl border-2 text-sm font-semibold transition-[border-color,box-shadow,color] ${
                          escolhida
                            ? "border-[var(--ks-brand-600)] text-[var(--ks-brand-600)] shadow-[0_0_0_4px_var(--ks-brand-50)]"
                            : "border-[var(--ks-color-border-subtle)] text-[var(--ks-color-text-secondary)] hover:border-[var(--ks-color-border-strong)]"
                        }`}
                      >
                        {rotulo}
                      </button>
                    );
                  })}
                </div>
                <p className="mt-1.5 text-xs text-[var(--ks-color-text-muted)]">
                  {validade === "sem" ? "A chave vale até você revogar." : "Passado esse prazo, a chave para de valer sozinha."}
                </p>
              </div>

              {erro ? <Aviso tom="ruim">{erro}</Aviso> : null}
            </div>

            <footer className="flex flex-col gap-3 border-t border-[var(--ks-color-border-subtle)] bg-[var(--ks-color-bg-inset)] px-6 py-3.5 sm:flex-row sm:items-center sm:justify-between">
              <p className="text-xs leading-5 text-[var(--ks-color-text-muted)]">A chave age como você: enxerga e faz só o que as suas permissões deixam.</p>
              <div className="flex shrink-0 justify-end gap-2">
                <Button type="button" variant="ghost" onClick={pedirParaFechar} disabled={criando}>
                  Cancelar
                </Button>
                <Button type="submit" disabled={criando || !acesso || !nome.trim()} icon={criando ? <Loader2 size={16} className="animate-spin" /> : <KeyRound size={16} />}>
                  Criar chave
                </Button>
              </div>
            </footer>
          </form>
        )}
      </div>
    </div>

    <CaixaDeConfirmacao pedido={duvida} aoFechar={() => setDuvida(null)} />
    </>
  );
}

/* No celular a linha empilha (nome em cima, o resto embaixo); a grade de
   colunas só entra quando há largura para ela. */
const COLUNAS_DAS_CHAVES = "md:grid md:grid-cols-[minmax(0,1fr)_150px_150px_140px_44px]";

/**
 * Uma linha da lista de conexões. Serve aos dois jeitos de ligar: a chave
 * colada à mão e a conexão que nasceu do botão Conectar. A conexão não tem
 * começo de chave para mostrar (o segredo dela muda de hora em hora) nem
 * validade escolhida: vale até alguém desligar, ou até ficar três meses
 * parada.
 */
function LinhaDaChave({
  chave,
  verDeQuem,
  agora,
  aoRevogar,
}: {
  chave: ChaveNaLista;
  verDeQuem: boolean;
  agora: number;
  aoRevogar: () => void;
}) {
  const conexao = chave.origem === "conexao";
  const ativa = chave.situacao === "ativa";
  const cria = chave.escopos.includes("escrita");
  const programaDoUso = nomeDoPrograma(chave.ultimoCliente);
  const venceEm = chave.expiraEm ? Math.ceil((new Date(chave.expiraEm).getTime() - agora) / DIA) : null;
  const selo = cria ? <Selo cor="bg-[var(--ks-color-primary)]">Consulta e cria</Selo> : <Selo cor="bg-sky-500">Só consulta</Selo>;
  const Icone = conexao ? PlugZap : KeyRound;
  const encerrada =
    chave.situacao === "revogada" ? (
      <Selo cor="bg-rose-500">{conexao ? "Desligada" : "Revogada"}</Selo>
    ) : chave.situacao === "vencida" ? (
      <Selo cor="bg-amber-500">{conexao ? "Expirada" : "Vencida"}</Selo>
    ) : null;

  return (
    <div
      className={`flex ${COLUNAS_DAS_CHAVES} items-center gap-3 border-t border-slate-100/60 px-5 py-3.5 transition-colors hover:bg-[var(--ks-color-bg-inset)] sm:px-6 ${
        ativa ? "" : "opacity-55"
      }`}
    >
      <div className="flex min-w-0 flex-1 items-center gap-3">
        <span
          className={`flex h-9 w-9 shrink-0 items-center justify-center rounded-xl text-white shadow-sm ${
            !ativa
              ? "bg-slate-400"
              : cria
                ? "bg-gradient-to-br from-violet-500 to-purple-600"
                : "bg-gradient-to-br from-sky-500 to-blue-600"
          }`}
        >
          <Icone size={16} strokeWidth={2.4} />
        </span>
        <div className="min-w-0">
          <p className="truncate text-sm font-semibold text-[var(--ks-color-text-primary)]">{chave.nome}</p>
          <p className="truncate text-[11px] text-slate-400">
            {conexao ? "pelo botão Conectar" : <span className="font-mono">{chave.prefixo}…</span>}
            {verDeQuem && chave.de ? ` · ${chave.minha ? "sua" : `de ${chave.de}`}` : ""}
            {` · ${conexao ? "conectado" : "criada"} em ${dataCurta(chave.criadaEm)}`}
          </p>
          {/* O que no computador ocupa três colunas, no celular vira uma linha. */}
          <p className="mt-1.5 flex flex-wrap items-center gap-x-3 gap-y-1 text-[11px] text-slate-400 md:hidden">
            {selo}
            <span>{chave.ultimoUsoEm ? `${conexao ? "usado" : "usada"} ${quando(chave.ultimoUsoEm, agora)}` : conexao ? "ainda não usado" : "nunca usada"}</span>
            {encerrada ?? <span>{conexao ? "até desligar" : chave.expiraEm ? `até ${dataCurta(chave.expiraEm)}` : "sem validade"}</span>}
          </p>
        </div>
      </div>

      <div className="hidden md:block">{selo}</div>

      <div className="hidden min-w-0 md:block">
        {chave.ultimoUsoEm ? (
          <>
            <p className="ks-num text-sm font-semibold text-[var(--ks-color-text-primary)]">{quando(chave.ultimoUsoEm, agora)}</p>
            {programaDoUso ? <p className="truncate text-[11px] text-slate-400">{programaDoUso}</p> : null}
          </>
        ) : (
          <span className="text-xs text-slate-400">{conexao ? "Ainda não usado" : "Nunca usada"}</span>
        )}
      </div>

      <div className="hidden md:block">
        {encerrada ??
          (conexao ? (
            <span className="text-xs text-slate-400">Até desligar</span>
          ) : chave.expiraEm ? (
            <>
              <p className="ks-num text-sm text-[var(--ks-color-text-primary)]">até {dataCurta(chave.expiraEm)}</p>
              {venceEm !== null && venceEm <= 7 ? (
                <p className="text-[11px] font-semibold text-amber-600">{venceEm <= 1 ? "vence em 1 dia" : `vence em ${venceEm} dias`}</p>
              ) : null}
            </>
          ) : (
            <span className="text-xs text-slate-400">Sem validade</span>
          ))}
      </div>

      <div className="flex justify-end">
        {ativa ? (
          <Button
            size="icon"
            variant="ghost"
            title={conexao ? "Desligar conexão" : "Revogar chave"}
            aria-label={conexao ? `Desligar a conexão ${chave.nome}` : `Revogar a chave ${chave.nome}`}
            onClick={aoRevogar}
          >
            {conexao ? <Unplug className="h-4 w-4 text-rose-500" /> : <Ban className="h-4 w-4 text-rose-500" />}
          </Button>
        ) : null}
      </div>
    </div>
  );
}

const ICONE_DA_ATIVIDADE = {
  leitura: { Icone: Search, cor: "text-slate-500", rotulo: "Consulta" },
  previa: { Icone: Eye, cor: "text-[var(--ks-color-primary)]", rotulo: "Prévia: mostrou antes, nada foi gravado" },
  escrita: { Icone: PenLine, cor: "text-emerald-600", rotulo: "Criou" },
} as const;

/* Quantas linhas de atividade aparecem antes do "ver mais". */
const ATIVIDADE_NA_DOBRA = 10;

function LinhaDeAtividadeNaLista({ linha, agora }: { linha: LinhaDeAtividade; agora: number }) {
  const { Icone, cor, rotulo } = ICONE_DA_ATIVIDADE[linha.tipo] ?? ICONE_DA_ATIVIDADE.leitura;
  const programa = nomeDoPrograma(linha.cliente);
  /* A resposta de erro é escrita para o assistente, e depois do primeiro ponto
     vem a orientação para ele ("use tal comando"). Para quem lê o registro
     basta o que aconteceu; o texto inteiro fica na dica do mouse. */
  const detalhe = linha.ok ? linha.resumo : linha.erro ? linha.erro.split(/(?<=[.!?])\s/)[0] : null;

  return (
    <div className="flex items-center gap-3 border-t border-slate-100/60 px-5 py-3 transition-colors hover:bg-[var(--ks-color-bg-inset)] sm:px-6">
      <span
        className={`flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-[var(--ks-color-bg-soft)] ${linha.ok ? cor : "text-rose-500"}`}
        title={linha.ok ? rotulo : "Não deu certo"}
      >
        {linha.ok ? <Icone size={15} strokeWidth={2.3} /> : <XCircle size={15} strokeWidth={2.3} />}
      </span>
      <div className="min-w-0 flex-1">
        <p className="truncate text-sm font-semibold text-[var(--ks-color-text-primary)]">{linha.titulo}</p>
        {detalhe ? (
          <p className={`truncate text-[11px] ${linha.ok ? "text-slate-400" : "text-rose-500"}`} title={(linha.ok ? linha.resumo : linha.erro) ?? undefined}>
            {detalhe}
          </p>
        ) : null}
      </div>
      <div className="shrink-0 text-right">
        <p className="ks-num text-xs font-semibold text-[var(--ks-color-text-secondary)]" title={new Date(linha.quando).toLocaleString("pt-BR")}>
          {quando(linha.quando, agora)}
        </p>
        <p className="hidden max-w-[200px] truncate text-[11px] text-slate-400 sm:block">{[linha.chave, programa].filter(Boolean).join(" · ")}</p>
      </div>
    </div>
  );
}

const O_QUE_FAZ = [
  { rotulo: "Consulta", cor: "bg-sky-500", texto: "Empresa, produtos e preços, clientes, orçamentos, pedidos, notas fiscais, agenda, estoque, financeiro, relatórios e painéis." },
  {
    rotulo: "Faz",
    cor: "bg-[var(--ks-color-primary)]",
    texto: "Cadastra e altera clientes, produtos, cupons, compromissos, campos próprios, regras e painéis; emite nota fiscal; lança e dá baixa no financeiro; troca a logo e a aparência da loja; liga o domínio próprio; gera PDFs e planilhas.",
  },
  {
    rotulo: "Confirma",
    cor: "bg-amber-500",
    texto: "Nota fiscal, dinheiro, exclusão, cancelamento e domínio: ele mostra antes e espera você aprovar. O resto ele faz direto, do jeito que você escolher abaixo.",
  },
];

/**
 * O que os assistentes personalizaram na conta, com o botão de desfazer. Só
 * aparece quando há o que mostrar.
 */
function PainelDePersonalizacoes({
  personalizacoes,
  verDeQuem,
  agora,
  desfazendo,
  aoDesfazer,
  atraso,
}: {
  personalizacoes: PersonalizacaoNaLista[];
  verDeQuem: boolean;
  agora: number;
  desfazendo: number | null;
  aoDesfazer: (personalizacao: PersonalizacaoNaLista) => void;
  atraso?: number;
}) {
  return (
    <Painel atraso={atraso}>
      <CabecalhoDoPainel titulo="Personalizações" texto="O que os assistentes mudaram nas configurações da conta. Dá para desfazer." />
      <div className="-mt-px">
        {personalizacoes.map((linha) => {
          const Icone = linha.tipo === "mensagem_do_whatsapp" ? MessageSquareText : FileText;
          const desfeita = Boolean(linha.desfeitaEm);
          return (
            <div
              key={linha.id}
              className={`flex items-center gap-3 border-t border-slate-100/60 px-5 py-3 transition-colors hover:bg-[var(--ks-color-bg-inset)] sm:px-6 ${desfeita ? "opacity-55" : ""}`}
            >
              <span className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-[var(--ks-color-bg-soft)] text-[var(--ks-color-primary)]">
                <Icone size={15} strokeWidth={2.3} />
              </span>
              <div className="min-w-0 flex-1">
                <p className="truncate text-sm font-semibold text-[var(--ks-color-text-primary)]">{linha.titulo}</p>
                <p className="truncate text-[11px] text-slate-400" title={`Antes: ${linha.antes}\nDepois: ${linha.depois}`}>
                  {desfeita ? "Desfeita · voltou ao que era antes" : `Agora: ${linha.depois}`}
                </p>
              </div>
              <div className="hidden shrink-0 text-right sm:block">
                <p className="ks-num text-xs font-semibold text-[var(--ks-color-text-secondary)]" title={new Date(linha.quando).toLocaleString("pt-BR")}>
                  {quando(linha.quando, agora)}
                </p>
                <p className="max-w-[180px] truncate text-[11px] text-slate-400">
                  {[linha.por, verDeQuem && linha.de && !linha.minha ? linha.de : null].filter(Boolean).join(" · ")}
                </p>
              </div>
              <div className="w-[104px] shrink-0 text-right">
                {linha.podeDesfazer ? (
                  <Button
                    variant="ghost"
                    size="sm"
                    onClick={() => aoDesfazer(linha)}
                    disabled={desfazendo !== null}
                    icon={desfazendo === linha.id ? <Loader2 size={14} className="animate-spin" /> : <Undo2 size={14} />}
                  >
                    Desfazer
                  </Button>
                ) : desfeita ? (
                  <span className="text-xs font-semibold text-slate-400">Desfeita</span>
                ) : null}
              </div>
            </div>
          );
        })}
      </div>
    </Painel>
  );
}

/**
 * Quando o assistente pede a sua aprovação antes de gravar (migration 185).
 * "Só no que é sensível" é o padrão: cadastrar e consultar passam direto.
 */
function PainelDeConfirmacao({ atraso }: { atraso?: number }) {
  const [escolha, setEscolha] = useState<"sensivel" | "sempre" | null>(null);
  const [podeMudar, setPodeMudar] = useState(false);
  const [salvando, setSalvando] = useState(false);
  const [erro, setErro] = useState<string | null>(null);

  useEffect(() => {
    let vivo = true;
    fetch("/api/assistentes/ajustes", { cache: "no-store" })
      .then((resposta) => (resposta.ok ? resposta.json() : null))
      .then((corpo: { confirmacao?: "sensivel" | "sempre"; podeMudar?: boolean } | null) => {
        if (!vivo || !corpo) return;
        setEscolha(corpo.confirmacao === "sempre" ? "sempre" : "sensivel");
        setPodeMudar(corpo.podeMudar === true);
      })
      .catch(() => undefined);
    return () => {
      vivo = false;
    };
  }, []);

  const escolher = async (nova: "sensivel" | "sempre") => {
    if (!podeMudar || salvando || nova === escolha) return;
    const anterior = escolha;
    setEscolha(nova);
    setSalvando(true);
    setErro(null);
    try {
      const resposta = await fetch("/api/assistentes/ajustes", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ confirmacao: nova }),
      });
      if (!resposta.ok) throw new Error(((await resposta.json().catch(() => null)) as { error?: string } | null)?.error || "Não foi possível salvar agora.");
    } catch (e) {
      setEscolha(anterior);
      setErro(e instanceof Error ? e.message : "Não foi possível salvar agora.");
    } finally {
      setSalvando(false);
    }
  };

  if (!escolha) return null;
  const opcoes = [
    {
      valor: "sensivel" as const,
      titulo: "Só no que é sensível (recomendado)",
      texto: "Nota fiscal, dinheiro, exclusão, cancelamento e domínio pedem a sua aprovação. Cadastrar, alterar e consultar ele faz direto, sem perguntar de novo.",
    },
    {
      valor: "sempre" as const,
      titulo: "Sempre",
      texto: "Tudo o que ele criar ou alterar aparece antes como prévia e só grava depois que você aprovar.",
    },
  ];
  return (
    <Painel atraso={atraso}>
      <CabecalhoDoPainel titulo="Quando ele pede a sua aprovação" texto="Vale para todos os assistentes ligados à empresa." />
      <div className="space-y-2 px-6 py-5">
        {opcoes.map((opcao) => {
          const marcada = escolha === opcao.valor;
          return (
            <button
              key={opcao.valor}
              type="button"
              onClick={() => void escolher(opcao.valor)}
              disabled={!podeMudar || salvando}
              className={`flex w-full items-start gap-3 rounded-xl border px-4 py-3 text-left transition-colors ${
                marcada ? "border-[var(--ks-color-primary)] bg-[var(--ks-color-bg-soft)]" : "border-[var(--ks-color-border-subtle)] hover:bg-[var(--ks-color-bg-inset)]"
              } ${!podeMudar ? "cursor-default" : ""}`}
            >
              <span
                className={`mt-1 flex h-4 w-4 shrink-0 items-center justify-center rounded-full border-2 ${
                  marcada ? "border-[var(--ks-color-primary)]" : "border-slate-300"
                }`}
              >
                {marcada ? <span className="h-2 w-2 rounded-full bg-[var(--ks-color-primary)]" /> : null}
              </span>
              <span className="min-w-0">
                <span className="block text-sm font-semibold text-[var(--ks-color-text-primary)]">{opcao.titulo}</span>
                <span className="mt-0.5 block text-[13px] leading-5 text-[var(--ks-color-text-muted)]">{opcao.texto}</span>
              </span>
            </button>
          );
        })}
        {!podeMudar ? <p className="text-xs text-slate-400">Só o dono ou um administrador muda esta escolha.</p> : null}
        {erro ? <p className="text-xs font-semibold text-rose-600">{erro}</p> : null}
        <p className="pt-1 text-xs leading-5 text-slate-400">
          A janela &quot;Permitir&quot; que aparece no Claude ou no ChatGPT é do próprio programa: escolha &quot;Permitir sempre&quot; para o conector da Ktalogu e ele para de perguntar.
        </p>
      </div>
    </Painel>
  );
}

/**
 * Exemplos de pedido e os limites do assistente: o que consulta, o que faz e
 * o que ele confirma antes.
 */
function OQueDaParaPedir({ atraso }: { atraso?: number }) {
  return (
    <Painel atraso={atraso}>
      <CabecalhoDoPainel titulo="O que dá para pedir" texto="Peça em português, do seu jeito. Alguns exemplos:" />
      <div className="space-y-5 px-6 py-5">
        <ul className="space-y-2">
          {EXEMPLOS.map((exemplo) => (
            <li key={exemplo} className="flex items-start gap-2.5 text-sm font-medium text-[var(--ks-color-text-secondary)]">
              <MessageCircle size={15} className="mt-0.5 shrink-0 text-[var(--ks-color-primary)]" />
              {exemplo}
            </li>
          ))}
        </ul>
        <dl className="space-y-3 border-t border-[var(--ks-color-border-subtle)] pt-5 text-sm leading-6">
          {O_QUE_FAZ.map(({ rotulo, cor, texto }) => (
            <div key={rotulo} className="grid gap-x-4 gap-y-0.5 sm:grid-cols-[84px_minmax(0,1fr)]">
              <dt className="pt-0.5">
                <Selo cor={cor}>{rotulo}</Selo>
              </dt>
              <dd className="text-[var(--ks-color-text-muted)]">{texto}</dd>
            </div>
          ))}
        </dl>
      </div>
    </Painel>
  );
}

/* O caminho inteiro em três passos, para quem abre a aba pela primeira vez. */
const COMO_FUNCIONA = [
  { titulo: "Copie o endereço do conector", texto: "Ele está aqui mesmo, pronto para copiar. O passo a passo de cada assistente fica logo abaixo." },
  { titulo: "Adicione no seu assistente", texto: "No Claude ou no ChatGPT, como conector personalizado, e clique em Conectar." },
  { titulo: "Entre e permita", texto: "Abre a tela da Ktalogu: você entra com a sua conta e escolhe o que ele pode fazer. Depois é só pedir em português." },
];

export default function AssistentesEApps({ dados, aoMudar }: { dados: DadosDosAssistentes; aoMudar: (dados: DadosDosAssistentes) => void }) {
  const [atividade, setAtividade] = useState<LinhaDeAtividade[] | null>(null);
  const [personalizacoes, setPersonalizacoes] = useState<PersonalizacaoNaLista[]>([]);
  const [desfazendo, setDesfazendo] = useState<number | null>(null);
  const [atualizando, setAtualizando] = useState(false);
  const [verTudo, setVerTudo] = useState(false);
  const [programa, setPrograma] = useState<Programa>("claude");
  /* A janela da chave só mostra os programas que aceitam chave. */
  const [programaDaChave, setProgramaDaChave] = useState<ProgramaDaChave>("claude-code");
  const [modalAberto, setModalAberto] = useState(false);
  const [confirmacao, setConfirmacao] = useState<Confirmacao | null>(null);
  const [revogando, setRevogando] = useState(false);
  const [erro, setErro] = useState<string | null>(null);
  /* O "há 3 min" das linhas anda sozinho, de minuto em minuto. */
  const [agora, setAgora] = useState(() => Date.now());
  useEffect(() => {
    const relogio = window.setInterval(() => setAgora(Date.now()), 60_000);
    return () => window.clearInterval(relogio);
  }, []);

  /* Quem recebe a lista nova é a página. Guardado numa referência para a
     leitura das chaves não nascer de novo a cada desenho da página. */
  const avisarAPagina = useRef(aoMudar);
  useEffect(() => {
    avisarAPagina.current = aoMudar;
  }, [aoMudar]);

  const carregarChaves = useCallback(async () => {
    const resposta = await fetch("/api/assistentes/chaves", { cache: "no-store" });
    const lidos = lerDadosDosAssistentes(await resposta.json().catch(() => null));
    if (!resposta.ok || !lidos) throw new Error("Não foi possível carregar as chaves.");
    avisarAPagina.current(lidos);
    setAgora(Date.now());
  }, []);

  const carregarAtividade = useCallback(async () => {
    try {
      const resposta = await fetch("/api/assistentes/atividade", { cache: "no-store" });
      const json = (await resposta.json().catch(() => null)) as { atividade?: LinhaDeAtividade[] } | null;
      if (!resposta.ok) throw new Error();
      setAtividade(Array.isArray(json?.atividade) ? json.atividade : []);
      setAgora(Date.now());
    } catch {
      /* A lista é um registro para olhar, não uma ação: se não veio, fica o
         que já estava na tela. */
      setAtividade((atual) => atual ?? []);
    }
  }, []);

  const carregarPersonalizacoes = useCallback(async () => {
    try {
      const resposta = await fetch("/api/assistentes/personalizacoes", { cache: "no-store" });
      const json = (await resposta.json().catch(() => null)) as { personalizacoes?: PersonalizacaoNaLista[] } | null;
      if (resposta.ok && Array.isArray(json?.personalizacoes)) setPersonalizacoes(json.personalizacoes);
    } catch {
      /* Fica o que já estava na tela. */
    }
  }, []);

  const atualizar = useCallback(async () => {
    setAtualizando(true);
    await Promise.all([carregarAtividade(), carregarPersonalizacoes(), carregarChaves().catch(() => undefined)]);
    setAtualizando(false);
  }, [carregarAtividade, carregarPersonalizacoes, carregarChaves]);

  /* A pessoa liga o assistente em outra janela e volta para cá: a lista se
     atualiza sozinha na volta, sem pedir um F5. */
  useEffect(() => {
    void carregarAtividade();
    void carregarPersonalizacoes();
    let ultima = Date.now();
    const aoVoltar = () => {
      if (document.visibilityState !== "visible" || Date.now() - ultima < 15_000) return;
      ultima = Date.now();
      void carregarAtividade();
      void carregarPersonalizacoes();
      void carregarChaves().catch(() => undefined);
    };
    window.addEventListener("focus", aoVoltar);
    document.addEventListener("visibilitychange", aoVoltar);
    return () => {
      window.removeEventListener("focus", aoVoltar);
      document.removeEventListener("visibilitychange", aoVoltar);
    };
  }, [carregarAtividade, carregarPersonalizacoes, carregarChaves]);

  const desfazer = async (linha: PersonalizacaoNaLista) => {
    setDesfazendo(linha.id);
    setErro(null);
    try {
      const resposta = await fetch(`/api/assistentes/personalizacoes/${linha.id}`, { method: "POST" });
      const json = (await resposta.json().catch(() => null)) as { error?: unknown } | null;
      if (!resposta.ok) throw new Error(typeof json?.error === "string" ? json.error : "Não foi possível desfazer.");
      await carregarPersonalizacoes();
    } catch (falha) {
      setErro(falha instanceof Error ? falha.message : "Não foi possível desfazer.");
    } finally {
      setDesfazendo(null);
      setConfirmacao(null);
    }
  };

  const pedirParaDesfazer = (linha: PersonalizacaoNaLista) => {
    setConfirmacao({
      titulo: `Desfazer: ${linha.titulo}?`,
      texto: `Volta a ser como era antes do assistente: ${linha.antes}.`,
      confirmar: "Desfazer",
      aoConfirmar: () => desfazer(linha),
    });
  };

  const revogar = async (chave: ChaveNaLista) => {
    setRevogando(true);
    setErro(null);
    try {
      const resposta = await fetch(`/api/assistentes/chaves/${chave.id}`, { method: "DELETE" });
      const json = (await resposta.json().catch(() => null)) as { error?: unknown } | null;
      if (!resposta.ok) {
        throw new Error(
          typeof json?.error === "string" ? json.error : chave.origem === "conexao" ? "Não foi possível desligar a conexão." : "Não foi possível revogar a chave."
        );
      }
      await carregarChaves();
    } catch (falha) {
      setErro(falha instanceof Error ? falha.message : "Não foi possível revogar a chave.");
    } finally {
      setRevogando(false);
      setConfirmacao(null);
    }
  };

  const pedirParaRevogar = (chave: ChaveNaLista) => {
    if (chave.origem === "conexao") {
      setConfirmacao({
        titulo: `Desligar a conexão “${chave.nome}”?`,
        texto: `O ${chave.nome} perde o acesso à conta na hora. Para ligar de novo, é só clicar em Conectar no próprio ${chave.nome} e permitir outra vez.`,
        confirmar: "Desligar conexão",
        tom: "perigo",
        aoConfirmar: () => revogar(chave),
      });
      return;
    }
    setConfirmacao({
      titulo: `Revogar a chave “${chave.nome}”?`,
      texto: "Ela para de valer na hora, e o programa que usa esta chave perde o acesso à conta. Não dá para desfazer: para ligar de novo, crie outra chave.",
      confirmar: "Revogar chave",
      tom: "perigo",
      aoConfirmar: () => revogar(chave),
    });
  };

  const abrirNovaChave = () => {
    /* A janela abre no programa que a pessoa estava olhando, se ele aceitar chave. */
    if (aceitaChave(programa)) setProgramaDaChave(programa);
    setModalAberto(true);
  };

  const ativas = dados.chaves.filter((chave) => chave.situacao === "ativa");
  /* O teto de chaves conta só as chaves coladas à mão: conexão pelo botão tem o dela. */
  const minhasAtivas = ativas.filter((chave) => chave.minha && chave.origem !== "conexao").length;
  const usadasNaSemana = ativas.filter((chave) => chave.ultimoUsoEm && agora - new Date(chave.ultimoUsoEm).getTime() < 7 * DIA).length;
  const noLimite = minhasAtivas >= dados.maximo;
  const atividadeVisivel = atividade ? (verTudo ? atividade : atividade.slice(0, ATIVIDADE_NA_DOBRA)) : [];
  /* Primeiro uso: sem chave nenhuma e sem nada registrado. A lista de
     atividade ainda não tem o que mostrar, e o lugar dela vai para o painel
     que explica o que dá para pedir. */
  const primeiroUso = dados.chaves.length === 0 && (atividade?.length ?? 0) === 0;

  const painelDeConexao = (
    <Painel className="min-w-0 xl:col-span-5" atraso={0.1}>
      <CabecalhoDoPainel titulo="Como conectar" texto="Escolha o assistente e siga os passos." />
      <div className="space-y-5 px-6 py-5">
        <div>
          <Rotulo>Endereço do conector</Rotulo>
          <div className="mt-2 flex items-center gap-2 rounded-xl border border-[var(--ks-color-border-strong)] py-1 pl-3.5 pr-1">
            <code className="min-w-0 flex-1 truncate font-mono text-[13px] text-[var(--ks-color-text-primary)]">{dados.endereco}</code>
            <BotaoDeCopiar texto={dados.endereco} />
          </div>
        </div>
        <PassosPeloBotao endereco={dados.endereco} programa={programa} aoTrocarPrograma={setPrograma} />
        <p className="border-t border-[var(--ks-color-border-subtle)] pt-4 text-xs leading-5 text-[var(--ks-color-text-muted)]">
          Você entra sempre na tela da própria Ktalogu: nenhum assistente pede a sua senha. E o assistente age como você, com as suas permissões.
        </p>
      </div>
    </Painel>
  );

  return (
    <div className="space-y-5">
      {erro ? (
        <Aviso tom="ruim" aoFechar={() => setErro(null)}>
          {erro}
        </Aviso>
      ) : null}

      <Painel>
        <CabecalhoDoPainel titulo="Conexões" texto="Os assistentes e programas ligados à sua conta. Desligou, perde o acesso na hora.">
          {/* Sem chave nenhuma, o convite para criar a primeira está logo abaixo. */}
          {dados.chaves.length > 0 ? (
            <div className="flex flex-wrap items-center gap-x-4 gap-y-3">
              <div className="flex items-center gap-4 text-xs font-medium text-slate-500">
                <span className="flex items-center gap-1.5">
                  <span className="h-1.5 w-1.5 rounded-full bg-[var(--ks-color-primary)]" />
                  <span className="ks-num">{ativas.length}</span> {ativas.length === 1 ? "ligada" : "ligadas"}
                </span>
                <span className="flex items-center gap-1.5">
                  <span className="h-1.5 w-1.5 rounded-full bg-emerald-500" />
                  <span className="ks-num">{usadasNaSemana}</span> em uso nesta semana
                </span>
              </div>
              {/* O caminho principal é o botão Conectar do assistente; a chave é
                  para o programa que pede uma. */}
              <Button
                variant="secondary"
                onClick={abrirNovaChave}
                disabled={noLimite}
                title={noLimite ? `Você já tem ${dados.maximo} chaves ativas. Revogue uma para criar outra.` : "Para programas que pedem uma chave de acesso"}
                icon={<KeyRound size={16} />}
              >
                Nova chave
              </Button>
            </div>
          ) : null}
        </CabecalhoDoPainel>

        {dados.chaves.length === 0 ? (
          <div className="grid gap-x-12 gap-y-8 px-6 py-9 md:grid-cols-[minmax(0,5fr)_minmax(0,6fr)] md:items-center md:px-10">
            <div>
              <span className="flex h-11 w-11 items-center justify-center rounded-2xl bg-gradient-to-br from-violet-500 to-purple-600 text-white shadow-[0_8px_20px_rgba(95,49,214,0.28)]">
                <PlugZap size={20} strokeWidth={2.4} />
              </span>
              <h3 className="mt-4 text-xl font-extrabold tracking-tight text-[var(--ks-color-text-primary)]">Converse com a sua Ktalogu</h3>
              <p className="mt-2 max-w-md text-sm leading-6 text-[var(--ks-color-text-muted)]">
                Ligue o Claude ou o ChatGPT à sua conta. Ele consulta e cria por você, com as suas permissões, e tudo o que fizer fica registrado
                aqui.
              </p>
              <div className="mt-5 max-w-md">
                <Rotulo>Endereço do conector</Rotulo>
                <div className="mt-2 flex items-center gap-2 rounded-xl border border-[var(--ks-color-border-strong)] py-1 pl-3.5 pr-1">
                  <code className="min-w-0 flex-1 truncate font-mono text-[13px] text-[var(--ks-color-text-primary)]">{dados.endereco}</code>
                  <BotaoDeCopiar texto={dados.endereco} />
                </div>
              </div>
              <p className="mt-3 text-xs leading-5 text-[var(--ks-color-text-muted)]">
                Usa um programa que pede chave?{" "}
                <button type="button" onClick={abrirNovaChave} className="font-bold text-[var(--ks-color-primary)] hover:underline">
                  Criar uma chave de acesso
                </button>
              </p>
            </div>
            <ol className="space-y-4">
              {COMO_FUNCIONA.map(({ titulo, texto }, indice) => (
                <li key={titulo} className="flex gap-3.5">
                  <span className="ks-num mt-0.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-[var(--ks-color-primary)] text-xs font-bold text-white">
                    {indice + 1}
                  </span>
                  <div>
                    <p className="text-sm font-semibold text-[var(--ks-color-text-primary)]">{titulo}</p>
                    <p className="text-sm leading-6 text-[var(--ks-color-text-muted)]">{texto}</p>
                  </div>
                </li>
              ))}
            </ol>
          </div>
        ) : (
          <div>
            <div className={`hidden ${COLUNAS_DAS_CHAVES} gap-3 px-6 py-3 text-[10px] font-semibold uppercase tracking-[0.14em] text-slate-400`}>
              <span>Conexão</span>
              <span>Pode</span>
              <span>Último uso</span>
              <span>Validade</span>
              <span />
            </div>
            {dados.chaves.map((chave) => (
              <LinhaDaChave key={chave.id} chave={chave} verDeQuem={dados.verTodas} agora={agora} aoRevogar={() => pedirParaRevogar(chave)} />
            ))}
          </div>
        )}
      </Painel>

      {/* Duas colunas, cada uma com a própria altura. A da esquerda empilha a
          atividade e os exemplos; a da direita é o passo a passo, que é alto.
          Assim as duas terminam perto uma da outra, com muita ou pouca
          atividade. */}
      <div className="grid items-start gap-5 xl:grid-cols-12">
        <div className="min-w-0 space-y-5 xl:col-span-7">
          {primeiroUso ? null : (
            <Painel atraso={0.05}>
              <CabecalhoDoPainel titulo="Atividade recente" texto="O que os assistentes consultaram e criaram na conta.">
                <Button
                  variant="ghost"
                  size="sm"
                  onClick={() => void atualizar()}
                  disabled={atualizando}
                  icon={<RefreshCw size={14} className={atualizando ? "animate-spin" : ""} />}
                >
                  Atualizar
                </Button>
              </CabecalhoDoPainel>

              {atividade === null ? (
                <div className="flex justify-center py-14">
                  <Loader2 className="h-6 w-6 animate-spin text-[var(--ks-color-primary)]" />
                </div>
              ) : atividade.length === 0 ? (
                <div className="px-6 py-10 text-center">
                  <p className="text-sm font-semibold text-[var(--ks-color-text-primary)]">Nada por aqui ainda</p>
                  <p className="mx-auto mt-1 max-w-sm text-sm leading-6 text-[var(--ks-color-text-muted)]">
                    Quando um assistente consultar ou criar alguma coisa na conta, aparece nesta lista: o que foi, quando, e por qual conexão.
                  </p>
                </div>
              ) : (
                <div className="-mt-px">
                  {atividadeVisivel.map((linha) => (
                    <LinhaDeAtividadeNaLista key={linha.id} linha={linha} agora={agora} />
                  ))}
                  {atividade.length > ATIVIDADE_NA_DOBRA ? (
                    <div className="border-t border-slate-100/60 px-6 py-3">
                      <button
                        type="button"
                        onClick={() => setVerTudo((atual) => !atual)}
                        className="text-xs font-bold text-[var(--ks-color-primary)] hover:underline"
                      >
                        {verTudo ? "Mostrar só as últimas" : `Ver as ${atividade.length} últimas`}
                      </button>
                    </div>
                  ) : null}
                </div>
              )}
            </Painel>
          )}

          {personalizacoes.length ? (
            <PainelDePersonalizacoes
              personalizacoes={personalizacoes}
              verDeQuem={dados.verTodas}
              agora={agora}
              desfazendo={desfazendo}
              aoDesfazer={pedirParaDesfazer}
              atraso={0.1}
            />
          ) : null}

          <PainelDeConfirmacao atraso={0.11} />

          <ImportacoesDosAssistentes atraso={0.12} />

          <OQueDaParaPedir atraso={primeiroUso ? 0.05 : 0.15} />
        </div>

        {painelDeConexao}
      </div>

      {modalAberto ? (
        <ModalDeNovaChave
          endereco={dados.endereco}
          programa={programaDaChave}
          aoTrocarPrograma={setProgramaDaChave}
          aoCriar={() => void carregarChaves().catch(() => undefined)}
          aoFechar={() => setModalAberto(false)}
        />
      ) : null}

      <CaixaDeConfirmacao pedido={confirmacao} ocupado={revogando || desfazendo !== null} aoFechar={() => setConfirmacao(null)} />
    </div>
  );
}
