/**
 * Back office — rever os factos de uma marca.
 *
 * É **o passo humano** do pipeline, e sem este ecrã ele não existe: a API está
 * escrita desde 22 Set e não havia sítio nenhum onde alguém carregasse no
 * botão. Sem isso os factos ficam todos em `proposto`, e o agente não tem nada
 * que possa afirmar.
 *
 * Até 24 Set curava-se aqui também o dossiê de marca (doze secções escritas
 * pela IA). Saiu: o que tinha de útil passou para a voz da marca e para os
 * factos (avaliação da marca, secção 0). A API do dossiê fica, parada.
 *
 * ## A citação não é decoração
 *
 * Cada facto mostra **a frase original de onde saiu**, e é o elemento mais
 * importante do ecrã. Verificar um facto sem ler a citação é carimbar, e o que
 * se está a decidir é o que o agente vai poder afirmar sobre a marca de um
 * cliente. A citação foi conferida por máquina contra o texto da página — ou
 * seja, existe mesmo — mas se ela diz o que o facto diz é juízo de quem lê.
 *
 * ## Proibido e recusado não são o mesmo
 *
 * - **Recusado** é "isto não é verdade, ou não interessa". Sai do caminho.
 * - **Proibido** é "isto nunca se diz". Entra em todos os prompts como
 *   restrição, e é provavelmente a decisão mais valiosa que aqui se toma —
 *   é o que impede o agente de repetir uma frase que meteu a marca em
 *   sarilhos.
 *
 * ## Nada promove sozinho
 *
 * Nenhum passo automático põe um facto em `verificado` ou `proibido`. A AI
 * propõe; a decisão é de uma pessoa, fica com nome e data, e é auditada.
 */

const FACTO_ESTADOS = {
  proposto: { texto: 'Por rever', variante: 'primario' },
  verificado: { texto: 'Verificado', variante: 'sucesso' },
  proibido: { texto: 'Proibido', variante: 'erro' },
  recusado: { texto: 'Recusado', variante: 'neutro' },
  // Desde 24 Set o cliente confirma os dados da sua marca; a equipa fica com as
  // excepções (as alegações que pedem prova e as sinalizações).
  confirmado_cliente: { texto: 'Confirmado pelo cliente', variante: 'contorno' },
};

const SECCAO_ESTADOS = {
  vazia: 'vazia',
  rascunho: 'escrita pela AI',
  curada: 'corrigida à mão',
};

/** Um facto, com a prova à vista. */
function FactoCartao({ slug, facto, ocupado, onDecidir }) {
  const [nota, setNota] = React.useState('');
  const [resposta, setResposta] = React.useState('');
  const [aEscrever, setAEscrever] = React.useState(false);
  const pedeProva = facto.estado === 'proposto' && facto.confirmado_em;
  // Retirado pelo cliente: não se verifica por ele (ronda 7: o cartão dizia
  // "Confirmado pelo cliente" com um botão Verificar que dava erro).
  const retirado = Boolean(facto.retirado_pelo_cliente);
  const estado = retirado ? { texto: 'Retirado pelo cliente', variante: 'neutro' }
    : pedeProva ? { texto: 'Pede prova', variante: 'primario' }
    : FACTO_ESTADOS[facto.estado] || { texto: facto.estado, variante: 'neutro' };

  // Com o cliente a dizer que está errado, manter o estado é uma decisão (fecha
  // a sinalização): o botão do estado actual não pode ficar desactivado.
  const bloqueado = (e) => ocupado || (facto.estado === e && !facto.sinalizacao);
  const rotulo = (e, texto) => (facto.estado === e && facto.sinalizacao ? 'Manter' : texto);

  // O que se escreveu só se limpa quando a decisão ficou (ronda 10: num
  // conflito, a lista refrescava e a resposta escrita perdia-se).
  const decidir = async (novo) => {
    const ficou = await onDecidir(facto, novo, nota.trim() || null, resposta.trim() || null);
    if (!ficou) return;
    setNota('');
    setResposta('');
    setAEscrever(false);
  };

  return (
    <li className="border-t border-border px-5 py-4 first:border-t-0">
      <div className="flex flex-wrap items-start justify-between gap-3">
        <p className="min-w-0 max-w-prose text-sm leading-relaxed text-foreground">
          {facto.afirmacao}
        </p>
        <Badge variante={estado.variante}>{estado.texto}</Badge>
      </div>

      {/* A prova. Sem isto, verificar é carimbar. */}
      {facto.citacao && (
        <blockquote className="mt-3 border-l-2 border-border pl-3 text-xs leading-relaxed text-muted-foreground">
          {facto.citacao}
        </blockquote>
      )}

      <p className="mt-2 text-xs text-muted-foreground">
        {facto.fonte || 'origem desconhecida'}
        {facto.pagina ? `, página ${facto.pagina}` : ''}
        {/* Por decidir, não se diz quem decidiu: era a decisão anterior (ronda 9). */}
        {facto.estado !== 'proposto' && facto.decidido_por ? ` · decidido por ${facto.decidido_por}` : ''}
        {facto.estado !== 'proposto' && facto.decidido_em ? ` em ${formatDataCurta(facto.decidido_em)}` : ''}
      </p>
      {/* As campanhas não terminadas que o repetem (28 Set, D2): ao recusar ou
          proibir, sabe-se o que fica marcado e deixa de se poder enviar. */}
      {(facto.campanhas || []).length > 0 && (
        <p className={`mt-1 text-xs ${['recusado', 'proibido'].includes(facto.estado) ? 'text-accent' : 'text-muted-foreground'}`}>
          {['recusado', 'proibido'].includes(facto.estado) ? 'Ainda aparece em' : 'Aparece em'}
          {' '}{facto.campanhas.length === 1 ? '1 campanha' : `${facto.campanhas.length} campanhas`}:
          {' '}{facto.campanhas.map((c) => `"${c.titulo}"`).join(', ')}.
          {' '}{['recusado', 'proibido'].includes(facto.estado)
            ? 'Estão marcadas para o cliente, e não se enviam assim.'
            : 'Se o recusares ou proibires, ficam marcadas e não se enviam assim.'}
        </p>
      )}
      {/* A resposta que o cliente recebeu da última vez: a equipa decide a saber
          o que pediu (ronda 9: pedia o certificado e não o via ao rever). */}
      {facto.resposta_cliente && (
        <p className="mt-1 text-xs text-muted-foreground">{`Última resposta ao cliente: «${facto.resposta_cliente}»`}</p>
      )}

      {/* O cliente confirmou uma alegação que pede prova: é a equipa que decide
          se entra. A resposta chega-lhe, se recusares. */}
      {facto.confirmado_em && !retirado && (
        <div className="mt-3 border-l-2 border-primary bg-[hsl(var(--primary)/0.06)] px-3 py-2">
          <p className="text-xs text-foreground">
            Confirmado pelo cliente · {facto.confirmado_por_nome || facto.confirmado_por || 'utilizador removido'} · {formatDataCurta(facto.confirmado_em)}
          </p>
          {pedeProva && facto.motivo_prova && (
            <p className="mt-1 text-xs text-muted-foreground">{`Pede prova porque ${semPontoFinal(facto.motivo_prova)}. Verifica se há como o provar antes de o deixares entrar.`}</p>
          )}
          {/* A prova que o cliente mandou (migração 046). */}
          {/* Todas, da primeira à mais recente (migração 047): decide-se a saber
              sobre que ficheiro (ronda 10). A mais recente é a que se viu. */}
          {(facto.provas || []).length > 0 && (
            <div className="mt-1 text-xs text-foreground">
              <p>{facto.provas.length === 1 ? 'Prova enviada pelo cliente:' : `${facto.provas.length} provas enviadas pelo cliente:`}</p>
              <ul className="mt-0.5 space-y-0.5">
                {facto.provas.map((p, i) => (
                  <li key={p.id}>
                    <a href={`/api/${slug}/materiais/${p.id}/ficheiro`} target="_blank" rel="noreferrer" className="underline underline-offset-2 hover:text-primary">
                      {p.nome}
                    </a>
                    {p.em ? ` · ${formatDataCurta(p.em)}` : ''}
                    {i === facto.provas.length - 1 && facto.provas.length > 1 ? ' · a mais recente' : ''}
                  </li>
                ))}
              </ul>
            </div>
          )}
          {pedeProva && !facto.sinalizacao && (
            <>
              <label htmlFor={`resposta-${facto.id}`} className="mt-2 block text-xs text-muted-foreground">
                Resposta ao cliente se recusares ou proibires
              </label>
              <textarea
                id={`resposta-${facto.id}`}
                value={resposta}
                rows={5}
                maxLength={1000}
                onChange={(e) => setResposta(e.target.value)}
                className="mt-1 w-full border border-border bg-background px-2 py-1.5 text-xs text-foreground outline-none focus-visible:border-primary"
              />
            </>
          )}
        </div>
      )}

      {/* O cliente disse que isto está errado. Enquanto ninguém decidir, o
          facto não vai ao Agente PM (lib/factos.js). A resposta chega-lhe. */}
      {facto.sinalizacao && (
        <div className="mt-3 border-l-2 border-accent bg-[hsl(var(--accent)/0.08)] px-3 py-2">
          <p className="text-xs text-accent">
            Sinalizado pelo cliente · {facto.sinalizacao.criada_por_nome || facto.sinalizacao.criada_por} · {formatDataCurta(facto.sinalizacao.criada_em)}
          </p>
          <p className="mt-1 text-sm text-foreground">"{facto.sinalizacao.texto}"</p>
          <label htmlFor={`resposta-${facto.id}`} className="mt-2 block text-xs text-muted-foreground">
            Resposta ao cliente — vai com a decisão
          </label>
          <textarea
            id={`resposta-${facto.id}`}
            value={resposta}
            rows={5}
            maxLength={1000}
            onChange={(e) => setResposta(e.target.value)}
            className="mt-1 w-full border border-border bg-background px-2 py-1.5 text-xs text-foreground outline-none focus-visible:border-primary"
          />
        </div>
      )}

      {facto.nota && (
        <p className="mt-2 max-w-prose text-xs italic leading-relaxed text-muted-foreground">
          {facto.nota}
        </p>
      )}

      <div className="mt-3 flex flex-wrap items-center gap-2">
        <button
          type="button"
          disabled={bloqueado('verificado') || retirado}
          title={retirado ? 'O cliente retirou este dado: só ele o pode repor.' : undefined}
          onClick={() => decidir('verificado')}
          className="bg-primary px-3 py-1.5 text-xs uppercase tracking-wider text-primary-foreground outline-none hover:bg-primary/90 focus-visible:ring-1 focus-visible:ring-accent disabled:bg-secondary disabled:text-muted-foreground"
        >
          {rotulo('verificado', 'Verificar')}
        </button>
        <button
          type="button"
          disabled={bloqueado('proibido')}
          onClick={() => decidir('proibido')}
          title="Nunca dizer isto. A IA fica proibida de o escrever em qualquer peça."
          className="border border-border px-3 py-1.5 text-xs text-muted-foreground transition-colors hover:border-primary hover:text-foreground disabled:opacity-50"
        >
          {rotulo('proibido', 'Proibir')}
        </button>
        <button
          type="button"
          disabled={bloqueado('recusado')}
          onClick={() => decidir('recusado')}
          title="Não é verdade, ou não interessa."
          className="border border-border px-3 py-1.5 text-xs text-muted-foreground transition-colors hover:border-primary hover:text-foreground disabled:opacity-50"
        >
          {rotulo('recusado', 'Recusar')}
        </button>

        {aEscrever ? (
          <input
            type="text"
            value={nota}
            autoFocus
            placeholder="Porquê? (fica no registo)"
            onChange={(e) => setNota(e.target.value)}
            className="min-w-[14rem] grow border border-border bg-background px-2 py-1.5 text-xs text-foreground outline-none placeholder:text-muted-foreground focus-visible:border-primary"
          />
        ) : (
          <button
            type="button"
            onClick={() => setAEscrever(true)}
            className="text-xs text-muted-foreground underline underline-offset-2 hover:text-foreground"
          >
            Deixar nota
          </button>
        )}
      </div>
    </li>
  );
}

function ScreenAdminDossie({ slug, onNavegar }) {
  const [dados, setDados] = React.useState(null);
  // Começa nas excepções: é o trabalho da equipa desde 24 Set. O resto é do cliente.
  // O filtro vem do endereço (a fila "A tratar" abre as correcções com
  // ?ver=sinalizado); sem ele, as excepções. Abrir em "Nada aqui" com trabalho
  // noutro filtro obrigava a um segundo clique (cliente simulado, 24 Set).
  const [filtro, setFiltro] = React.useState(() => new URLSearchParams(window.location.search).get('ver') || 'a_rever');
  // O filtro da lista que está à vista: diferente do escolhido, a nova está a chegar (8 Out).
  const [filtroLido, setFiltroLido] = React.useState(null);
  const [escolheu, setEscolheu] = React.useState(false);
  // Sem nada que peça prova e com correcções, abre nas correcções: uma vez só,
  // para não mudar o filtro debaixo de quem o escolheu.
  React.useEffect(() => {
    if (escolheu || !dados || filtro !== 'a_rever') return;
    setEscolheu(true);
    if (!dados.contagem.a_rever && dados.contagem.sinalizado) setFiltro('sinalizado');
  }, [dados]);
  const [erro, setErro] = React.useState(null);
  const [ocupado, setOcupado] = React.useState(false);
  const [toast, setToast] = React.useState(null);
  const fecharToast = React.useCallback(() => setToast(null), []);

  const carregar = React.useCallback(
    () =>
      api.get(`/api/admin/accounts/${slug}/factos${filtro ? `?estado=${filtro}` : ''}`)
        .then((d) => { setDados(d); setFiltroLido(filtro); })
        .catch((e) => setErro(e.message || 'Não foi possível ler os factos.')),
    [slug, filtro]
  );

  React.useEffect(() => {
    setErro(null);
    carregar();
  }, [carregar]);
  // Factos que o cliente sinalizou, ou que a extracção trouxe, chegam sem
  // recarregar (sessão de 24 Set). Pára enquanto se decide.
  useActualizar(carregar, { activo: !ocupado });

  // Devolve se correu bem: o editor de uma secção só fecha quando guardou.
  const comAviso = async (accao, mensagem) => {
    setOcupado(true);
    try {
      const r = await accao();
      await carregar();
      if (mensagem) setToast({ id: Date.now(), mensagem: mensagem(r) });
      return true;
    } catch (e) {
      setToast({ id: Date.now(), mensagem: e.message, erro: true });
      // Num conflito, a versão nova vem ler-se: o editor mostra-a ao lado.
      if (e.status === 409) carregar();
      return false;
    } finally {
      setOcupado(false);
    }
  };

  const cabecalho = (
    <header>
      {/* O dossiê não está no menu: sem isto não havia como voltar (Ricardo, 4 Nov). */}
      <button type="button" onClick={() => onNavegar(`/admin/clientes/${slug}`)}
        className="mb-3 inline-flex items-center gap-1.5 text-sm text-muted-foreground underline-offset-4 hover:text-foreground hover:underline">
        <span aria-hidden="true">←</span> Voltar a {(dados && dados.marca) || slug}
      </button>
      <p className="label-eyebrow">Back office · {(dados && dados.marca) || slug}</p>
      <h1 className="mt-1 font-display text-4xl uppercase leading-none tracking-wide">
        Factos da marca
      </h1>
      <p className="mt-3 max-w-prose text-sm leading-relaxed text-muted-foreground">
        O cliente confirma os dados correntes da sua marca. Aqui ficam as excepções: as afirmações
        que pedem prova e as correcções dele. O que decidires fica registado, e a resposta chega-lhe.
      </p>
    </header>
  );

  if (erro) {
    return (
      <div className="space-y-6">
        {cabecalho}
        <div className="border border-border bg-card px-6 py-12 text-center">
          <p className="font-display text-xl tracking-wide">Não deu para ler</p>
          <p className="mt-2 text-sm text-muted-foreground">{erro}</p>
        </div>
      </div>
    );
  }

  if (!dados) {
    return (
      <div className="space-y-6">
        {cabecalho}
        <SkeletonTabela linhas={4} />
      </div>
    );
  }

  const c = dados.contagem;

  return (
    <div className="space-y-6">
      {cabecalho}

      <section>
        <div className="flex flex-wrap items-end justify-between gap-3">
          <h2 className="label-eyebrow">Factos</h2>
          <p className="text-xs text-muted-foreground">
            {[
              // `proposto` já não conta os confirmados: subtrair a_rever dava 3 em vez de 21 (administrador
              // virgem, 4 Nov: a ficha do cliente dizia 21 e aqui 3).
              [c.a_rever, 'confirmado pelo cliente e à espera da tua prova', 'confirmados pelo cliente e à espera da tua prova'],
              [c.proposto, 'por confirmar pelo cliente', 'por confirmar pelo cliente'],
              [c.confirmado_cliente, 'confirmado pelo cliente', 'confirmados pelo cliente'],
              [c.verificado, 'verificado', 'verificados'],
              [c.proibido, 'proibido', 'proibidos'],
              [c.recusado, 'recusado', 'recusados'],
            ].map(([n, um, varios]) => `${n} ${n === 1 ? um : varios}`).join(' · ')}
          </p>
        </div>

        {c.sinalizado > 0 && filtro !== 'sinalizado' && (
          <button
            type="button"
            onClick={() => setFiltro('sinalizado')}
            className="mt-3 block w-full border-l-2 border-accent bg-[hsl(var(--accent)/0.08)] px-4 py-3 text-left text-sm text-foreground hover:bg-[hsl(var(--accent)/0.14)]"
          >
            {c.sinalizado} {c.sinalizado === 1 ? 'facto sinalizado' : 'factos sinalizados'} pelo cliente, por responder.
            Até decidires, não vão ao Agente PM. Ver
          </button>
        )}

        {c.a_rever > 0 && filtro !== 'a_rever' && (
          <button
            type="button"
            onClick={() => setFiltro('a_rever')}
            className="mt-3 block w-full border-l-2 border-primary bg-[hsl(var(--primary)/0.06)] px-4 py-3 text-left text-sm text-foreground hover:bg-[hsl(var(--primary)/0.12)]"
          >
            {c.a_rever} {c.a_rever === 1 ? 'afirmação confirmada pelo cliente pede' : 'afirmações confirmadas pelo cliente pedem'} prova.
            Não entram nas campanhas antes de decidires. Ver
          </button>
        )}

        {/* A análise que desistiu de páginas (migração 061, becos de 9 Out, ponto 12): o cliente lê que
            não se conseguiu tirar delas o que interessa; daqui voltam à fila do worker. */}
        {c.analise_desistiu > 0 && (
          <div className="mt-3 flex flex-wrap items-center justify-between gap-3 border-l-2 border-accent bg-[hsl(var(--accent)/0.08)] px-4 py-3">
            <div className="min-w-0 text-sm text-foreground">
              {c.analise_desistiu} {c.analise_desistiu === 1 ? 'página que a análise desistiu de ler' : 'páginas que a análise desistiu de ler'}, ao fim de 3 falhas.
              {c.analise_erro && <span className="mt-0.5 block text-xs text-muted-foreground">Último erro: {c.analise_erro}</span>}
              <span className="mt-0.5 block text-xs text-muted-foreground">Resolvida a causa, voltam à fila: o worker analisa-as outra vez, com os créditos da conta.</span>
            </div>
            <button
              type="button"
              disabled={ocupado}
              onClick={() => comAviso(
                () => api.post(`/api/admin/accounts/${slug}/dossie/analisar-outra-vez`, {}),
                (r) => `${r.paginas} ${r.paginas === 1 ? 'página voltou' : 'páginas voltaram'} à fila da análise.`
              )}
              className="ns-btn ns-btn-sm ns-btn-suave shrink-0 text-xs disabled:opacity-40"
            >
              {ocupado ? 'A pedir…' : 'Analisar outra vez'}
            </button>
          </div>
        )}

        <div className="mt-2 flex flex-wrap gap-2">
          {[
            ['a_rever', 'Pedem prova'],
            ['sinalizado', 'Sinalizados'],
            ['proposto', 'Por confirmar'],
            ['confirmado_cliente', 'Confirmados pelo cliente'],
            ['verificado', 'Verificados'],
            ['proibido', 'Proibidos'],
            ['recusado', 'Recusados'],
            ['', 'Todos'],
          ].map(([chave, texto]) => (
            <button
              key={chave || 'todos'}
              type="button"
              onClick={() => setFiltro(chave)}
              className={`px-3 py-1.5 text-xs uppercase tracking-wider transition-colors ${
                filtro === chave
                  ? 'bg-secondary text-foreground'
                  : 'text-muted-foreground hover:text-foreground'
              }`}
            >
              {texto}
            </button>
          ))}
        </div>

        {filtroLido !== null && filtroLido !== filtro && <AProcessar className="mt-3" texto="A ler os factos deste filtro" />}
        {filtroLido !== null && filtroLido !== filtro ? null : dados.factos.length === 0 ? (
          <div className="mt-3 border border-dashed border-border bg-card px-6 py-10 text-center">
            <p className="text-sm text-foreground">Nada aqui.</p>
            <p className="mx-auto mt-2 max-w-prose text-xs leading-relaxed text-muted-foreground">
              {filtro === 'proposto'
                ? 'Não há factos por rever. Ou já foram todos decididos, ou ainda não se leu material desta marca.'
                : 'Muda o filtro para ver os outros.'}
            </p>
          </div>
        ) : (
          <ul className="mt-3 border border-border bg-card">
            {dados.factos.map((f) => (
              <FactoCartao
                key={f.id}
                facto={f}
                slug={slug}
                ocupado={ocupado}
                onDecidir={(facto, estado, nota, resposta) =>
                  comAviso(
                    () => api.patch(`/api/admin/factos/${facto.id}`, { estado, nota, resposta, de: facto.estado, prova_vista: facto.prova_material_id || null }),
                    // Onde o dado ainda aparece, se deixou de valer (28 Set, D2).
                    (r) => (r && r.campanhas && r.campanhas.length
                      ? `Decidido. ${r.campanhas.length === 1 ? 'Uma campanha repete-o' : `${r.campanhas.length} campanhas repetem-no`}: ficam marcadas para o cliente e não se enviam assim.`
                      : 'Decidido.')
                  )
                }
              />
            ))}
          </ul>
        )}
      </section>

      <Toast toast={toast} onFechar={fecharToast} />
    </div>
  );
}
