// Analysis detail screen — with polling simulation.

const STAGE_ORDER = ['QUEUED', 'EXTRACTING', 'COMPUTING', 'DRAFTING', 'RENDERING', 'COMPLETE'];
const STAGE_PRETTY = {
  QUEUED:     'Aguardando processador',
  EXTRACTING: 'Extraindo texto do PDF',
  COMPUTING:  'Calculando taxa, CET e capitalização',
  DRAFTING:   'Redigindo painel e memória',
  RENDERING:  'Renderizando planilha e PDF',
  COMPLETE:   'Análise concluída',
};

const EVENT_LABEL = {
  'analysis.queued':           'Análise enfileirada',
  'stage:extracting:started':  'Extração iniciada',
  'stage:extracting:done':     'Extração concluída',
  'stage:computing:started':   'Cálculo iniciado',
  'stage:computing:done':      'Cálculo concluído',
  'stage:drafting:started':    'Redação iniciada',
  'stage:drafting:done':       'Redação concluída',
  'stage:rendering:started':   'Renderização iniciada',
  'stage:rendering:done':      'Renderização concluída',
  'analysis.complete':         'Análise concluída',
  'analysis.failed':           'Análise falhou',
};

function AnalysisDetail({ analysisId, analyses, onBack, onAnalysesUpdate }) {
  const analysis = analyses.find((a) => a.id === analysisId);

  // Polling REAL do backend: refetch GET /v1/analyses/{id} a cada 4s enquanto
  // a análise não tá COMPLETE ou FAILED. Quando completa, atualiza a entrada
  // no estado global com extracted_data + computed_data + artifacts reais.
  useEffect(() => {
    if (!analysisId) return;
    let cancelled = false;

    const fetchOnce = async () => {
      try {
        const res = await window.apiFetch(`/v1/analyses/${analysisId}`);
        if (!res.ok || cancelled) return null;
        const fresh = await res.json();
        onAnalysesUpdate((prev) => prev.map((a) => (a.id === analysisId ? { ...a, ...fresh } : a)));
        return fresh;
      } catch (_e) { return null; }
    };

    // primeiro fetch imediato pra pegar dados que possam ter chegado antes de abrir
    fetchOnce();

    // polling enquanto in-progress
    const stopWhen = (s) => s === 'COMPLETE' || s === 'FAILED';
    if (analysis && stopWhen(analysis.status)) return;

    const tick = setInterval(async () => {
      const fresh = await fetchOnce();
      if (fresh && stopWhen(fresh.status)) clearInterval(tick);
    }, 4000);

    return () => { cancelled = true; clearInterval(tick); };
  }, [analysisId]);

  if (!analysis) {
    return <div className="px-8 py-10 text-ink-500">Análise não encontrada.</div>;
  }

  // Adapta os campos da API real (extracted_data / computed_data) pro formato
  // que esta tela renderiza. Esses objetos só aparecem quando status=COMPLETE.
  const ext = analysis.extracted_data || {};
  const comp = analysis.computed_data || {};
  const num = (v) => v == null ? null : (typeof v === 'string' ? parseFloat(v) : v);
  if (analysis.status === 'COMPLETE' && analysis.extracted_data && !analysis.contrato) {
    const seguros = (ext.lista_seguros || []).map((s) => ({ nome: s.nome, valor: num(s.valor), seguradora: s.seguradora }));
    const tarifas = (ext.lista_tarifas || []).reduce((acc, t) => { acc[t.nome] = num(t.valor); return acc; }, {});
    analysis.contrato = {
      banco: ext.requerido,
      data_contrato: ext.data_contratual,
      valor_financiado: num(ext.valor_total_financiado_f6),
      taxa_aa: num(ext.taxa_juros_aa),
      taxa_am: num(comp.c10_taxa_am),
      cet_aa: num(ext.cet_aa_declarado),
      parcelas: ext.qtd_parcelas_regulares,
      valor_parcela: num(ext.valor_parcela_regular),
      parcela_balao: ext.parcela_balao,
      sistema_amortizacao: (ext.sistema_amortizacao_declarado || 'não declarado').toUpperCase(),
      tarifas, seguros,
      veiculo: ext.veiculo,
      layout: ext.layout,
    };
    const tx_media = num(comp.taxa_media_aa);
    const excesso = num(comp.excesso_medio);
    analysis.resultados = {
      taxa_media_bacen_aa: tx_media,
      excesso_aa: excesso,
      capitalizacao_diaria: ext.capitalizacao_diaria === 'Sim',
      reducao_estimada_total: num(comp.reducao_estimada),
      reducao_estimada_parcela: comp.c12 && comp.reducao_estimada
        ? num(comp.reducao_estimada) / Number(comp.c12)
        : null,
      criterios: [
        { id: 'a_taxa_abusiva',     label: 'Taxa pactuada > 30% acima da média Bacen', ok: comp.criteria_a_taxa_abusiva, detalhe: tx_media != null ? `${(num(ext.taxa_juros_aa)*100).toFixed(2)}% a.a. vs. ${(tx_media*100).toFixed(2)}% a.a. (${excesso>=0?'+':''}${(excesso*100).toFixed(2)} p.p.)` : null },
        { id: 'b_capitalizacao',    label: 'Capitalização diária expressa em cláusula',   ok: comp.criteria_b_capitalizacao, detalhe: ext.capitalizacao_diaria === 'Sim' ? 'Cláusula identificada no contrato' : 'Sem cláusula expressa' },
        { id: 'c_tarifas_seguros',  label: 'Tarifas + seguros indevidos > 3% do valor financiado', ok: comp.criteria_c_tarifas, detalhe: comp.c8 ? `R$ ${num(comp.c9).toFixed(2)} / R$ ${num(comp.c8).toFixed(2)} = ${((num(comp.c9)/num(comp.c8))*100).toFixed(2)}%` : null },
      ],
    };
    analysis.consideracoes = ext.considerations_paragraph || null;
    // Alertas operacionais (compatibilidade com mock antigo do render)
    const alertas = [];
    if (ext.urgencia_processual) alertas.push({ tipo: 'urgencia', severidade: 'alta', texto: `Urgência processual (${ext.urgencia_processual.tipo_acao}) — prazos correndo.` });
    if (ext.plano_atipico) alertas.push({ tipo: 'plano_atipico', severidade: 'media', texto: `Plano atípico — ${ext.parcela_balao ? 'parcela balão de R$ ' + num(ext.parcela_balao.valor).toFixed(2) + ' em ' + ext.parcela_balao.vencimento : 'estrutura atípica detectada'}.` });
    if (ext.custodia === 'integra_fraca' || ext.custodia === 'indefinida') alertas.push({ tipo: 'custodia', severidade: 'media', texto: 'Cadeia de custódia frágil — confira via original antes de qualquer providência.' });
    if (ext.custodia === 'adulteracao') alertas.push({ tipo: 'custodia', severidade: 'alta', texto: 'Indícios de adulteração — solicitar via original imediatamente.' });
    if (ext.divergencia_versoes) alertas.push({ tipo: 'divergencia', severidade: 'media', texto: 'Divergência entre orçamento e cédula — adotada a cédula assinada.' });
    if (ext.veiculo?.zero_km && (ext.lista_tarifas || []).some(t => (t.nome || '').toLowerCase().includes('avalia'))) {
      alertas.push({ tipo: 'zero_km_aval', severidade: 'baixa', texto: 'Veículo zero km com Tarifa de Avaliação — fragilidade jurídica reforçada.' });
    }
    if (ext.refinanciamento) alertas.push({ tipo: 'refi', severidade: 'media', texto: 'Refinanciamento/portabilidade — base pode incluir saldo de operação anterior.' });
    analysis.alertas = alertas;
  }

  const inProgress = INPROGRESS.has(analysis.status) || analysis.status === 'QUEUED';
  const stageIdx = Math.max(0, STAGE_ORDER.indexOf(analysis.status));
  const stageTotal = STAGE_ORDER.length - 1;
  const progressPct = analysis.status === 'COMPLETE' ? 100 : Math.round((stageIdx / stageTotal) * 100);

  return (
    <div className="px-4 sm:px-8 py-5 sm:py-7 max-w-[1280px] mx-auto pb-16">
      {/* breadcrumb */}
      <nav className="text-[12.5px] text-ink-500 mb-3 flex items-center gap-1.5">
        <button onClick={onBack} className="hover:text-ink-900">Dashboard</button>
        <span className="text-ink-300">/</span>
        <button onClick={onBack} className="hover:text-ink-900">Análises</button>
        <span className="text-ink-300">/</span>
        <span className="font-mono text-ink-900">#{shortId(analysis.id)}</span>
      </nav>

      {/* Header */}
      <header className="bg-white rounded-lg border border-ink-200 shadow-card p-6">
        <div className="flex flex-wrap items-start justify-between gap-5">
          <div className="min-w-0">
            <div className="text-[11px] uppercase tracking-tracker text-ink-500 font-medium">
              Análise pericial · Revisional de Financiamento de Veículo {analysis.pericia_version_label}
            </div>
            <h1 className="mt-2 text-[24px] font-bold text-ink-900 tracking-tightish leading-tight">
              {analysis.requerente_nome || <span className="text-ink-400">Requerente não informado</span>}
            </h1>
            <div className="mt-2 text-[12.5px] text-ink-500 tabular flex items-center gap-3 flex-wrap">
              <span>Criada em {fmtDateTime.format(new Date(analysis.created_at))}</span>
              {analysis.completed_at && (
                <>
                  <span className="text-ink-300">·</span>
                  <span>Concluída em {fmtDateTime.format(new Date(analysis.completed_at))}</span>
                </>
              )}
              <span className="text-ink-300">·</span>
              <span className="font-mono">#{analysis.id}</span>
            </div>
          </div>
          <div className="flex flex-col items-end gap-2">
            <StatusBadge status={analysis.status} size="lg" />
            <ViabilityChip value={analysis.viability_result} size="lg" />
          </div>
        </div>

        {/* progress */}
        {inProgress && (
          <div className="mt-6">
            <div className="flex items-center justify-between text-[12.5px] text-ink-700 mb-2">
              <span className="font-medium">{STAGE_PRETTY[analysis.status]}…</span>
              <span className="tabular text-ink-500">Estágio {stageIdx} de {stageTotal} · {progressPct}%</span>
            </div>
            <div className="relative h-2 bg-ink-100 rounded-full overflow-hidden indeterminate-bar" />
            <div className="mt-3 grid grid-cols-5 gap-1.5">
              {STAGE_ORDER.slice(1).map((s, i) => {
                const done = i + 1 < stageIdx || analysis.status === 'COMPLETE';
                const active = STAGE_ORDER[stageIdx] === s;
                return (
                  <div key={s} className="flex flex-col gap-1.5">
                    <div className={`h-1 rounded-full ${done ? 'bg-navy-800' : active ? 'bg-navy-800/50' : 'bg-ink-100'}`} />
                    <div className={`text-[10.5px] uppercase tracking-tracker font-medium ${
                      done ? 'text-navy-800' : active ? 'text-ink-700' : 'text-ink-400'
                    }`}>
                      {s.toLowerCase()}
                    </div>
                  </div>
                );
              })}
            </div>
          </div>
        )}

        {analysis.status === 'FAILED' && analysis.error_message && (
          <div className="mt-5 rounded-md border border-danger-200 bg-danger-50 px-4 py-3 text-[13px] text-danger-700 flex items-start gap-2.5">
            <span className="mt-0.5"><Icon.Alert /></span>
            <div>
              <div className="font-semibold mb-0.5">Falha no processamento</div>
              <div>{analysis.error_message}</div>
            </div>
          </div>
        )}
      </header>

      {/* Downloads (only when complete) */}
      {analysis.status === 'COMPLETE' && (
        <section className="mt-6">
          <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
            <DownloadCard
              icon={<Icon.Xlsx />}
              ext=".xlsx"
              title="Planilha de cálculo"
              desc="Cronograma teórico × pactuado, taxa Bacen, CET XIRR e estimativa de redução."
              href={`/v1/analyses/${analysis.id}/spreadsheet.xlsx`}
              filename={`analise-${shortId(analysis.id)}-planilha.xlsx`}
            />
            <DownloadCard
              icon={<Icon.Pdf />}
              ext=".pdf"
              title="Painel de viabilidade"
              desc="1 página: dados do contrato, critérios atendidos, alertas operacionais."
              href={`/v1/analyses/${analysis.id}/report.pdf`}
              filename={`analise-${shortId(analysis.id)}-painel.pdf`}
              primary
            />
            <DownloadCard
              icon={<Icon.Md />}
              ext=".md"
              title="Memória de cálculo"
              desc="Detalhamento técnico verificável: fórmulas, seguros listados, fontes citadas."
              href={`/v1/analyses/${analysis.id}/memory.md`}
              filename={`analise-${shortId(analysis.id)}-memoria.md`}
            />
          </div>
        </section>
      )}

      {/* Main grid */}
      <div className="mt-6 grid grid-cols-1 lg:grid-cols-[1fr_300px] gap-6">
        <div className="min-w-0 space-y-6">
          {/* Contract + Results */}
          {analysis.contrato && analysis.resultados && (
            <section className="bg-white rounded-lg border border-ink-200 shadow-card">
              <div className="px-6 py-4 border-b border-ink-200 flex items-center justify-between">
                <h2 className="text-[14.5px] font-semibold text-ink-900">Painel de viabilidade</h2>
                <span className="text-[11px] uppercase tracking-tracker text-ink-400 font-mono">espelha o PDF gerado</span>
              </div>
              <div className="grid grid-cols-1 md:grid-cols-2 divide-y md:divide-y-0 md:divide-x divide-ink-100">
                <ContratoBlock c={analysis.contrato} />
                <ResultadosBlock r={analysis.resultados} />
              </div>
            </section>
          )}

          {/* Alerts */}
          {analysis.alertas && analysis.alertas.length > 0 && (
            <section className="bg-white rounded-lg border border-ink-200 shadow-card">
              <div className="px-6 py-4 border-b border-ink-200">
                <h2 className="text-[14.5px] font-semibold text-ink-900">Alertas operacionais</h2>
                <p className="mt-0.5 text-[12.5px] text-ink-500">Pontos que exigem atenção do perito antes do protocolo.</p>
              </div>
              <ul className="divide-y divide-ink-100">
                {analysis.alertas.map((al, i) => <AlertRow key={i} alert={al} />)}
              </ul>
            </section>
          )}

          {/* Considerations */}
          {analysis.consideracoes && (
            <section className="bg-white rounded-lg border border-ink-200 shadow-card px-6 py-5">
              <h2 className="text-[14.5px] font-semibold text-ink-900">Considerações</h2>
              <p className="mt-2 text-[14px] leading-relaxed text-ink-800" style={{ textWrap: 'pretty' }}>
                {analysis.consideracoes}
              </p>
              {/* tokens/custo são métricas internas. Backend só envia esses campos
                  pra SUPERADMIN (cost_brl=null pra cliente). Renderiza só quando vier. */}
              {analysis.cost_brl != null && (
                <div className="mt-5 pt-4 border-t border-ink-100 flex flex-wrap items-center gap-x-5 gap-y-1 text-[11.5px] text-ink-500 font-mono">
                  <span className="text-warn-700 mr-2">[admin]</span>
                  <span>tokens_input <span className="text-ink-900 tabular">{fmtNum.format(analysis.tokens_input)}</span></span>
                  <span>tokens_output <span className="text-ink-900 tabular">{fmtNum.format(analysis.tokens_output)}</span></span>
                  <span>cached <span className="text-ink-900 tabular">{fmtNum.format(analysis.tokens_cached)}</span></span>
                  <span>custo <span className="text-ink-900 tabular">{fmtBRL.format(analysis.cost_brl)}</span></span>
                </div>
              )}
            </section>
          )}

          {/* In-progress placeholder */}
          {inProgress && !analysis.contrato && (
            <section className="bg-white rounded-lg border border-ink-200 shadow-card p-10 text-center">
              <Spinner size={22} />
              <div className="mt-3 text-[14px] text-ink-700 font-medium">{STAGE_PRETTY[analysis.status]}</div>
              <div className="mt-1 text-[12.5px] text-ink-500">Polling automático a cada 3 segundos. Você pode fechar esta página — a análise continua processando.</div>
            </section>
          )}
        </div>

        {/* Sidebar: timeline */}
        <aside className="bg-white rounded-lg border border-ink-200 shadow-card h-fit sticky top-4">
          <div className="px-5 py-4 border-b border-ink-200">
            <h3 className="text-[13.5px] font-semibold text-ink-900">Linha do tempo</h3>
            <p className="mt-0.5 text-[12px] text-ink-500">Eventos do processador</p>
          </div>
          <Timeline events={analysis.events || []} now={Date.now()} />
        </aside>
      </div>
    </div>
  );
}

function DownloadCard({ icon, ext, title, desc, href, filename, primary }) {
  const [busy, setBusy] = useState(false);
  const handle = async (e) => {
    e.preventDefault();
    if (busy) return;
    setBusy(true);
    try {
      // Endpoints precisam de Authorization: Bearer — fetch + blob + anchor sintético
      // pra disparar download mantendo o filename desejado.
      const res = await window.apiFetch(href);
      if (!res.ok) {
        const body = await res.json().catch(() => ({}));
        throw new Error(body.detail || `HTTP ${res.status}`);
      }
      const blob = await res.blob();
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = url;
      a.download = filename;
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
      // Revoke depois de um tick pra Safari/iOS conseguir disparar o save.
      setTimeout(() => URL.revokeObjectURL(url), 1500);
    } catch (err) {
      alert(`Falha ao baixar: ${err.message || err}`);
    } finally {
      setBusy(false);
    }
  };
  return (
    <a
      href={href}
      download={filename}
      onClick={handle}
      aria-disabled={busy}
      className={`group block rounded-lg border bg-white px-5 py-5 shadow-card hover:shadow-cardHover transition-shadow ${
        primary ? 'border-navy-800/30' : 'border-ink-200'
      } ${busy ? 'opacity-60 pointer-events-none' : ''}`}
    >
      <div className="flex items-start justify-between">
        <div className={`h-11 w-11 rounded-md flex items-center justify-center ${primary ? 'bg-navy-50 text-navy-800' : 'bg-ink-50 text-ink-700'}`}>
          {icon}
        </div>
        <span className="text-[10.5px] uppercase tracking-tracker font-mono text-ink-400">{ext}</span>
      </div>
      <div className="mt-3 text-[14.5px] font-semibold text-ink-900">{title}</div>
      <p className="mt-1 text-[12.5px] text-ink-500 leading-snug min-h-[2.5em]">{desc}</p>
      <div className={`mt-4 inline-flex items-center gap-2 text-[13px] font-medium ${primary ? 'text-navy-800' : 'text-ink-900'} group-hover:underline`}>
        {busy ? <><Spinner /> Baixando…</> : <><Icon.Download /> Baixar arquivo</>}
      </div>
    </a>
  );
}

function ContratoBlock({ c }) {
  // Linhas estáticas só aparecem quando temos valor — evita "R$ —" lotando a UI.
  // Tarifas são dinâmicas: vêm como dict {nome: valor} extraído do contrato,
  // então iteramos sobre as chaves reais em vez de assumir slugs fixos.
  const tarifasList = c.tarifas ? Object.entries(c.tarifas).filter(([_, v]) => v != null) : [];
  const dataContratoStr = c.data_contrato ? fmtDate.format(new Date(c.data_contrato)) : null;
  const rows = [
    ['Instituição financeira', c.banco],
    c.contrato_num && ['Nº do contrato', <span className="font-mono">{c.contrato_num}</span>],
    dataContratoStr && ['Data do contrato', dataContratoStr],
    c.sistema_amortizacao && ['Sistema de amortização', c.sistema_amortizacao],
    c.valor_bem != null && ['Valor do bem', fmtBRL.format(c.valor_bem)],
    c.entrada != null && ['Entrada', fmtBRL.format(c.entrada)],
    c.valor_financiado != null && ['Valor financiado', fmtBRL.format(c.valor_financiado)],
    ...tarifasList.map(([nome, valor]) => [nome, fmtBRL.format(valor)]),
    c.taxa_am != null && ['Taxa pactuada (a.m.)', <span className="tabular">{fmtPct(c.taxa_am)}</span>],
    c.taxa_aa != null && ['Taxa pactuada (a.a.)', <span className="tabular font-semibold text-ink-900">{fmtPct(c.taxa_aa)}</span>],
    c.cet_aa != null && ['CET (a.a.)', <span className="tabular">{fmtPct(c.cet_aa)}</span>],
    c.parcelas && c.valor_parcela != null && ['Parcelas', `${c.parcelas}x ${fmtBRL.format(c.valor_parcela)}`],
  ].filter(Boolean);
  return (
    <div className="p-6">
      <div className="text-[11px] uppercase tracking-tracker text-ink-500 font-medium mb-3">Dados do contrato</div>
      <dl className="grid grid-cols-[1fr_auto] gap-y-1.5 text-[13px]">
        {rows.map(([k, v]) => (
          <React.Fragment key={k}>
            <dt className="text-ink-500">{k}</dt>
            <dd className="text-ink-900 text-right tabular">{v}</dd>
          </React.Fragment>
        ))}
      </dl>
      {c.seguros && c.seguros.length > 0 && (
        <div className="mt-5 pt-4 border-t border-ink-100">
          <div className="text-[11px] uppercase tracking-tracker text-ink-500 font-medium mb-2">Seguros embutidos</div>
          <ul className="text-[13px] space-y-1">
            {c.seguros.map((s, i) => (
              <li key={i} className="flex justify-between gap-3">
                <span className="text-ink-700">{s.nome}</span>
                <span className="text-ink-900 tabular">{fmtBRL.format(s.valor)}</span>
              </li>
            ))}
          </ul>
        </div>
      )}
    </div>
  );
}

function ResultadosBlock({ r }) {
  return (
    <div className="p-6">
      <div className="text-[11px] uppercase tracking-tracker text-ink-500 font-medium mb-3">Resultados</div>
      <dl className="grid grid-cols-[1fr_auto] gap-y-1.5 text-[13px]">
        <dt className="text-ink-500">Taxa média Bacen (a.a.)</dt>
        <dd className="text-ink-900 text-right tabular">{fmtPct(r.taxa_media_bacen_aa)}</dd>
        <dt className="text-ink-500">Excesso vs. Bacen</dt>
        <dd className={`text-right tabular font-semibold ${r.excesso_aa != null && r.excesso_aa > 0 ? 'text-danger-700' : 'text-ok-700'}`}>
          {r.excesso_aa != null
            ? `${r.excesso_aa >= 0 ? '+' : ''}${(r.excesso_aa * 100).toFixed(2).replace('.', ',')} p.p.`
            : '—'}
        </dd>
        <dt className="text-ink-500">Capitalização diária</dt>
        <dd className="text-right">
          {r.capitalizacao_diaria
            ? <span className="text-danger-700 font-semibold">SIM</span>
            : <span className="text-ok-700 font-semibold">NÃO</span>}
        </dd>
        <dt className="text-ink-500">Redução por parcela</dt>
        <dd className="text-ink-900 text-right tabular">{fmtBRL.format(r.reducao_estimada_parcela)}</dd>
        <dt className="text-ink-500">Redução total estimada</dt>
        <dd className="text-ok-700 text-right tabular font-bold text-[14.5px]">{fmtBRL.format(r.reducao_estimada_total)}</dd>
      </dl>

      <div className="mt-5 pt-4 border-t border-ink-100">
        <div className="text-[11px] uppercase tracking-tracker text-ink-500 font-medium mb-2">Critérios de viabilidade</div>
        <ul className="space-y-2">
          {r.criterios.map((c) => (
            <li key={c.id} className="flex items-start gap-2.5 text-[12.5px]">
              <span className={`mt-[2px] h-4 w-4 shrink-0 rounded-full flex items-center justify-center ${
                c.ok ? 'bg-ok-600 text-white' : 'bg-ink-200 text-ink-500'
              }`}>
                <Icon.Check width="11" height="11" />
              </span>
              <div>
                <div className="text-ink-900 font-medium">{c.label}</div>
                <div className="text-ink-500 mt-0.5">{c.detalhe}</div>
              </div>
            </li>
          ))}
        </ul>
      </div>
    </div>
  );
}

function AlertRow({ alert }) {
  const sev = alert.severidade;
  const styles = sev === 'alta'
    ? { bar: 'bg-danger-600', text: 'text-danger-700', tag: 'bg-danger-50 text-danger-700 border-danger-200' }
    : sev === 'media'
    ? { bar: 'bg-warn-600', text: 'text-warn-700', tag: 'bg-warn-50 text-warn-700 border-warn-200' }
    : { bar: 'bg-ink-300', text: 'text-ink-700', tag: 'bg-ink-100 text-ink-700 border-ink-200' };
  const TYPE_LABEL = {
    urgencia_processual: 'Urgência processual',
    custodia_fragil:     'Custódia frágil',
    plano_atipico:       'Plano atípico',
    veiculo_zero_km:     'Veículo zero km',
    refinanciamento:     'Refinanciamento',
  };
  return (
    <li className="px-6 py-3.5 flex items-start gap-4">
      <span className={`mt-1.5 h-2 w-2 rounded-full shrink-0 ${styles.bar}`} />
      <div className="flex-1 min-w-0">
        <div className="flex items-center gap-2 flex-wrap">
          <span className={`inline-flex items-center text-[10.5px] uppercase tracking-tracker font-semibold border rounded px-1.5 py-0.5 ${styles.tag}`}>
            {TYPE_LABEL[alert.tipo] || alert.tipo}
          </span>
          <span className={`text-[10.5px] uppercase tracking-tracker font-medium ${styles.text}`}>
            severidade {sev}
          </span>
        </div>
        <p className="mt-1.5 text-[13px] text-ink-800 leading-snug">{alert.texto}</p>
      </div>
    </li>
  );
}

function Timeline({ events, now }) {
  if (!events.length) {
    return <div className="px-5 py-6 text-[12.5px] text-ink-500">Nenhum evento registrado ainda.</div>;
  }
  return (
    <ol className="px-5 py-4 space-y-3 max-h-[480px] overflow-y-auto">
      {events.map((ev, i) => {
        const isStart = ev.type.endsWith(':started') || ev.type === 'analysis.queued';
        const isFail  = ev.type === 'analysis.failed';
        const isDone  = ev.type === 'analysis.complete';
        const dot = isFail ? 'bg-danger-600' : isDone ? 'bg-ok-600' : isStart ? 'bg-navy-800' : 'bg-ink-300';
        return (
          <li key={i} className="relative pl-5">
            {i < events.length - 1 && <span className="absolute left-[5px] top-3 bottom-[-12px] w-px bg-ink-100" />}
            <span className={`absolute left-0 top-1.5 h-2.5 w-2.5 rounded-full ${dot}`} />
            <div className="text-[12.5px] text-ink-900 leading-snug font-medium">{EVENT_LABEL[ev.type] || ev.type}</div>
            <div className="text-[11px] text-ink-500 tabular mt-0.5" title={fmtDateTime.format(new Date(ev.at))}>
              {relativeTime(ev.at)} · <span className="font-mono">{fmtDateTime.format(new Date(ev.at))}</span>
            </div>
          </li>
        );
      })}
    </ol>
  );
}

window.AnalysisDetail = AnalysisDetail;
