"use client";

import { useCallback, useEffect, useMemo, useRef, useState, type ReactNode } from "react";
import { createPortal } from "react-dom";
import { AnimatePresence, motion, useReducedMotion } from "framer-motion";
import {
  Activity,
  CalendarClock,
  ChartPie,
  CheckCircle2,
  Clock3,
  Eye,
  FileDown,
  Gauge,
  History,
  Hourglass,
  Image as IconeDeImagem,
  Link2,
  Loader2,
  MapPin,
  MessageCircle,
  Minus,
  Monitor,
  MousePointerClick,
  PackageCheck,
  PackageSearch,
  RefreshCw,
  Repeat,
  Send,
  Smartphone,
  Tablet,
  Timer,
  TrendingDown,
  TrendingUp,
  Undo2,
  UserCheck,
  Users,
  X,
  XCircle,
  type LucideIcon,
} from "lucide-react";
import {
  duracaoLegivel,
  FUSO,
  haQuantoTempo,
  linkDoWhatsapp,
  MINIMO_PARA_MEDIR_SINAIS,
  porcentoLegivel,
  type AcaoDoCliente,
  type AparelhoDaEquipe,
  type AparelhoDoLink,
  type ChanceDoOrcamento,
  type MetricasDoLink,
  type Passo,
  type SecaoDaFolha,
  type TipoDeAparelho,
  type VisitaDoLink,
} from "@/lib/metricas-do-link";
import { AnelDeValor, COR_DO_TIPO, COR_DOS_OUTROS, CORES_DE_SERIE, MiniAnel, RelogioDeHoras, Rosca, type Fatia } from "./graficos";

/*
  A janela "Métricas do link" do orçamento.

  Quatro abas: Resumo (a chance pelo link, os números e o que fazer agora), Visitas (a
  linha do tempo e o relógio de quando o cliente abre), Aparelhos (em quantos
  aparelhos foi aberto, e o "fui eu") e Chance (de onde sai o número).

  Os números vêm prontos da rota /api/quotes/[id]/metricas-do-link; aqui é só
  desenho. Enquanto a janela está aberta, ela pergunta de novo a cada 20
  segundos: dá para ver o cliente abrindo o orçamento na hora.
*/

type Aba = "resumo" | "visitas" | "aparelhos" | "chance";

const ABAS: Array<{ id: Aba; rotulo: string; curto: string; Icone: LucideIcon }> = [
  { id: "resumo", rotulo: "Resumo", curto: "Resumo", Icone: Gauge },
  { id: "visitas", rotulo: "Visitas", curto: "Visitas", Icone: History },
  { id: "aparelhos", rotulo: "Aparelhos", curto: "Aparelhos", Icone: Smartphone },
  { id: "chance", rotulo: "Chance pelo link", curto: "Chance", Icone: TrendingUp },
];

const ROTULO_DA_SECAO: Record<SecaoDaFolha, string> = {
  itens: "os itens",
  total: "o total",
  condicoes: "as condições",
  observacoes: "as observações",
  fim: "até o fim",
};

const ACAO: Record<AcaoDoCliente, { rotulo: string; Icone: LucideIcon }> = {
  pdf: { rotulo: "Baixou o PDF", Icone: FileDown },
  aprovar: { rotulo: "Clicou em aprovar", Icone: MousePointerClick },
  aprovou: { rotulo: "Aprovou", Icone: PackageCheck },
  artes: { rotulo: "Abriu as artes", Icone: IconeDeImagem },
  acompanhar: { rotulo: "Acompanhou o pedido", Icone: PackageSearch },
};

const ICONE_DO_TIPO: Record<TipoDeAparelho, LucideIcon> = { celular: Smartphone, computador: Monitor, tablet: Tablet };
const NOME_DO_TIPO: Record<TipoDeAparelho, string> = { celular: "Celular", computador: "Computador", tablet: "Tablet" };

const ICONE_DO_PASSO: Record<string, LucideIcon> = {
  renovar: Link2,
  comecou: MousePointerClick,
  agora: Activity,
  "nao-abriu": Send,
  aparelhos: Users,
  pdf: FileDown,
  esfriando: Hourglass,
  rapido: Timer,
  validade: CalendarClock,
  horario: Clock3,
};

/* ---------------------------------------------------------------- */
/* Datas                                                             */
/* ---------------------------------------------------------------- */

const formatoDiaMes = new Intl.DateTimeFormat("pt-BR", { timeZone: FUSO, day: "2-digit", month: "2-digit" });
const formatoHora = new Intl.DateTimeFormat("pt-BR", { timeZone: FUSO, hour: "2-digit", minute: "2-digit" });
const formatoSemana = new Intl.DateTimeFormat("pt-BR", { timeZone: FUSO, weekday: "short" });
const formatoChave = new Intl.DateTimeFormat("en-CA", { timeZone: FUSO, year: "numeric", month: "2-digit", day: "2-digit" });

const dataHora = (iso: string | null) => (iso ? `${formatoDiaMes.format(new Date(iso))} às ${formatoHora.format(new Date(iso))}` : "");

/** "25/09 às 13:19", no horário de Brasília (o cartão do orçamento usa). */
export const quandoFoi = dataHora;
const hora = (iso: string) => formatoHora.format(new Date(iso));
const porcento = porcentoLegivel;

function rotuloDoDia(iso: string, agora: number) {
  const chave = formatoChave.format(new Date(iso));
  if (chave === formatoChave.format(new Date(agora))) return "Hoje";
  if (chave === formatoChave.format(new Date(agora - 86_400_000))) return "Ontem";
  const semana = formatoSemana.format(new Date(iso)).replace(".", "");
  return `${semana.charAt(0).toUpperCase()}${semana.slice(1)}, ${formatoDiaMes.format(new Date(iso))}`;
}

function tempoDesde(dias: number) {
  const horas = dias * 24;
  if (horas < 1) return `${Math.max(1, Math.round(horas * 60))} min`;
  if (horas < 24) return `${Math.round(horas)} ${Math.round(horas) === 1 ? "hora" : "horas"}`;
  const inteiros = Math.floor(dias);
  return `${inteiros} ${inteiros === 1 ? "dia" : "dias"}`;
}

/* ---------------------------------------------------------------- */
/* Dados da rota                                                     */
/* ---------------------------------------------------------------- */

export function useMetricasDoLink(quoteId: string | null, opcoes: { ativo: boolean; aoVivo: boolean }) {
  const [metricas, setMetricas] = useState<MetricasDoLink | null>(null);
  const [erro, setErro] = useState<string | null>(null);
  const [carregando, setCarregando] = useState(false);
  const vez = useRef(0);

  const recarregar = useCallback(async () => {
    if (!quoteId) return;
    const minha = ++vez.current;
    setCarregando(true);
    try {
      const resposta = await fetch(`/api/quotes/${quoteId}/metricas-do-link`, { cache: "no-store" });
      const dados = (await resposta.json().catch(() => null)) as (MetricasDoLink & { error?: string }) | null;
      if (minha !== vez.current) return;
      if (!resposta.ok || !dados) {
        setErro(dados?.error || "Não deu para carregar as métricas agora.");
        return;
      }
      setMetricas(dados);
      setErro(null);
    } catch {
      if (minha === vez.current) setErro("Sem conexão com o servidor. Tente de novo em instantes.");
    } finally {
      if (minha === vez.current) setCarregando(false);
    }
  }, [quoteId]);

  useEffect(() => {
    if (opcoes.ativo) void recarregar();
  }, [opcoes.ativo, recarregar]);

  useEffect(() => {
    if (!opcoes.aoVivo) return;
    const relogio = window.setInterval(() => {
      if (document.visibilityState === "visible") void recarregar();
    }, 20_000);
    return () => window.clearInterval(relogio);
  }, [opcoes.aoVivo, recarregar]);

  return { metricas, erro, carregando, recarregar };
}

/* ---------------------------------------------------------------- */
/* Peças                                                             */
/* ---------------------------------------------------------------- */

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

const SELO: Record<Passo["tom"], string> = {
  atencao: "linear-gradient(135deg, #ff4d4f 0%, #e01329 100%)",
  bom: "linear-gradient(135deg, var(--ks-emerald) 0%, #0e9e70 100%)",
  neutro: "linear-gradient(135deg, var(--ks-brand-500) 0%, var(--ks-brand-700) 100%)",
};

function Selo({ tom, Icone }: { tom: Passo["tom"]; Icone: LucideIcon }) {
  return (
    <span aria-hidden className="flex h-8 w-8 shrink-0 items-center justify-center rounded-xl text-white" style={{ background: SELO[tom] }}>
      <Icone size={15} strokeWidth={2.4} />
    </span>
  );
}

export function faixaDaChance(chance: number) {
  if (chance >= 0.5) return { rotulo: "Chance alta", cor: "var(--ks-color-status-success)", Icone: TrendingUp };
  if (chance >= 0.25) return { rotulo: "Chance média", cor: "var(--ks-color-status-warning)", Icone: Minus };
  return { rotulo: "Chance baixa", cor: "var(--ks-color-status-danger)", Icone: TrendingDown };
}

/* Um botão cheio só: o da primeira sugestão. Os outros ficam em contorno,
   para a lista não virar uma coluna de botões verdes iguais. */
function BotaoDoWhatsapp({ href, cheio, rotulo = "Chamar no WhatsApp" }: { href: string; cheio: boolean; rotulo?: string }) {
  return (
    <a
      href={href}
      target="_blank"
      rel="noopener noreferrer"
      className={`inline-flex shrink-0 items-center gap-1.5 rounded-full px-3.5 py-2 text-xs font-bold transition focus:outline-none focus-visible:ring-2 focus-visible:ring-emerald-500/40 ${
        cheio
          ? "bg-gradient-to-r from-[#25D366] to-[#128C7E] text-white shadow-sm hover:brightness-105"
          : "border border-[var(--ks-color-border-strong)] text-[var(--ks-color-text-primary)] hover:border-[#25D366] hover:bg-[var(--ks-color-bg-hover)]"
      }`}
    >
      <MessageCircle size={14} className={cheio ? "" : "text-[#1faa53]"} />
      {rotulo}
    </a>
  );
}

/* ---------------------------------------------------------------- */
/* Aba Resumo                                                        */
/* ---------------------------------------------------------------- */

function AnelDaChance({ chance, tamanho = 132 }: { chance: ChanceDoOrcamento; tamanho?: number }) {
  if (chance.estado === "fechado") {
    return (
      <AnelDeValor fracao={1} tamanho={tamanho} cor="var(--ks-color-status-success)" rotulo="Orçamento fechado: virou pedido">
        <CheckCircle2 size={tamanho * 0.2} className="text-[var(--ks-color-status-success)]" />
        <span className="mt-1.5 text-[11px] font-semibold text-[var(--ks-color-text-muted)]">virou pedido</span>
      </AnelDeValor>
    );
  }
  if (chance.estado === "aprovado") {
    return (
      <AnelDeValor fracao={1} tamanho={tamanho} cor="var(--ks-color-status-warning)" rotulo="Aprovado no painel, sem pedido gerado">
        <CheckCircle2 size={tamanho * 0.2} className="text-[var(--ks-color-status-warning)]" />
        <span className="mt-1.5 text-[11px] font-semibold text-[var(--ks-color-text-muted)]">sem pedido</span>
      </AnelDeValor>
    );
  }
  if (chance.estado === "recusado") {
    return (
      <AnelDeValor fracao={0} tamanho={tamanho} rotulo="Orçamento recusado">
        <XCircle size={tamanho * 0.2} className="text-[var(--ks-color-status-danger)]" />
        <span className="mt-1.5 text-[11px] font-semibold text-[var(--ks-color-text-muted)]">recusado</span>
      </AnelDeValor>
    );
  }
  if (chance.estado === "sem-conta") {
    return (
      <AnelDeValor fracao={0} tamanho={tamanho} rotulo="Chance de fechar sem conta">
        <span className="text-2xl font-extrabold text-[var(--ks-color-text-soft)]">—</span>
        <span className="mt-1.5 text-[11px] font-semibold text-[var(--ks-color-text-muted)]">sem conta</span>
      </AnelDeValor>
    );
  }
  const faixa = faixaDaChance(chance.atual.chance);
  return (
    <AnelDeValor fracao={chance.atual.chance} tamanho={tamanho} cor={faixa.cor} rotulo={`Chance de fechar: ${porcento(chance.atual.chance)}`}>
      <span className="font-extrabold tracking-tight text-[var(--ks-color-text-primary)]" style={{ fontSize: tamanho * 0.24 }}>
        {porcento(chance.atual.chance).replace("%", "")}
        <span style={{ fontSize: tamanho * 0.12 }}>%</span>
      </span>
      <span className="mt-1 text-[11px] font-semibold text-[var(--ks-color-text-muted)]">de fechar</span>
    </AnelDeValor>
  );
}

function FraseDaChance({ chance, recorrente }: { chance: ChanceDoOrcamento; recorrente?: boolean }) {
  if (chance.estado === "fechado") {
    return (
      <p className="text-sm text-[var(--ks-color-text-secondary)]">
        Este orçamento virou pedido{chance.em ? ` em ${dataHora(chance.em)}` : ""}. As aberturas continuam registradas abaixo.
      </p>
    );
  }
  if (chance.estado === "aprovado") {
    return (
      <p className="text-sm text-[var(--ks-color-text-secondary)]">
        Marcado como aprovado no painel, mas o pedido ainda não foi gerado. O pedido nasce quando você clica em Gerar pedido ou quando o
        cliente aprova pelo link.
      </p>
    );
  }
  if (chance.estado === "recusado") return <p className="text-sm text-[var(--ks-color-text-secondary)]">Este orçamento foi recusado.</p>;
  if (chance.estado === "sem-conta") return <p className="text-sm text-[var(--ks-color-text-secondary)]">{chance.motivo}</p>;
  const { atual } = chance;
  const situacao = chance.aberto
    ? `O cliente abriu pela primeira vez há ${tempoDesde(chance.diasDesde)} e ainda não respondeu.`
    : `O link ainda não foi aberto, ${tempoDesde(chance.diasDesde)} depois de criado.`;
  /* Quando o histórico da empresa move o número, a frase diz: senão o anel
     mostra um valor e o texto outro, sem explicação. */
  const puxou = atual.empresa.casos > 0 && Math.round(atual.chance * 100) !== Math.round(atual.geral.taxa * 100);
  return (
    <p className="text-sm leading-6 text-[var(--ks-color-text-secondary)]">
      {situacao}
      {recorrente ? " Ele já comprou de você antes." : ""} Orçamentos nessa situação viraram pedido em{" "}
      <strong className="font-semibold text-[var(--ks-color-text-primary)]">{porcento(atual.geral.taxa)}</strong> das vezes (
      {atual.geral.casos.toLocaleString("pt-BR")} casos)
      {puxou ? (
        <>
          ; com o histórico da sua empresa, fica em{" "}
          <strong className="font-semibold text-[var(--ks-color-text-primary)]">{porcento(atual.chance)}</strong>.
        </>
      ) : (
        "."
      )}
    </p>
  );
}

function Kpi({ Icone, valor, rotulo, sub }: { Icone: LucideIcon; valor: string; rotulo: string; sub?: string }) {
  return (
    <div className="flex min-w-0 items-start gap-3 px-1 py-1 sm:px-4">
      <Icone size={17} className="mt-1 shrink-0 text-[var(--ks-brand-600)]" aria-hidden />
      <div className="min-w-0">
        <div className="text-[22px] font-extrabold leading-none tracking-tight text-[var(--ks-color-text-primary)]">{valor}</div>
        <div className="mt-1 text-xs font-medium text-[var(--ks-color-text-muted)]">{rotulo}</div>
        {sub ? <div className="mt-0.5 truncate text-[11px] text-[var(--ks-color-text-soft)]">{sub}</div> : null}
      </div>
    </div>
  );
}

function LinhaDoPasso({
  passo,
  whatsapp,
  destaque,
  aoRenovar,
  aoReenviar,
  ocupado,
}: {
  passo: Passo;
  whatsapp: string | null;
  destaque: boolean;
  aoRenovar?: () => void;
  aoReenviar?: () => void;
  ocupado: boolean;
}) {
  const link = passo.botao === "whatsapp" && passo.mensagem ? linkDoWhatsapp(whatsapp, passo.mensagem) : null;
  return (
    <li className="flex flex-col gap-3 border-t border-[var(--ks-color-border-subtle)]/60 py-3.5 first:border-t-0 sm:flex-row sm:items-center">
      <div className="flex min-w-0 flex-1 items-start gap-3">
        <Selo tom={passo.tom} Icone={ICONE_DO_PASSO[passo.id] ?? Activity} />
        <div className="min-w-0">
          <div className="text-sm font-semibold text-[var(--ks-color-text-primary)]">{passo.titulo}</div>
          <p className="mt-0.5 text-[13px] leading-5 text-[var(--ks-color-text-muted)]">{passo.texto}</p>
        </div>
      </div>
      <div className="flex shrink-0 pl-11 sm:pl-0">
        {link ? <BotaoDoWhatsapp href={link} cheio={destaque} /> : null}
        {passo.botao === "renovar" && aoRenovar ? (
          <button type="button" onClick={aoRenovar} disabled={ocupado} className="ks-button ks-button-primary ks-button-sm">
            {ocupado ? <Loader2 size={14} className="animate-spin" /> : <Link2 size={14} />}
            Gerar e copiar o link
          </button>
        ) : null}
        {passo.botao === "reenviar" && aoReenviar ? (
          <button type="button" onClick={aoReenviar} disabled={ocupado} className="ks-button ks-button-secondary ks-button-sm">
            {ocupado ? <Loader2 size={14} className="animate-spin" /> : <Send size={14} />}
            Reenviar o link
          </button>
        ) : null}
      </div>
    </li>
  );
}

function AbaResumo({
  metricas,
  aoRenovar,
  aoReenviar,
  ocupado,
  irPara,
}: {
  metricas: MetricasDoLink;
  aoRenovar?: () => void;
  aoReenviar?: () => void;
  ocupado: boolean;
  irPara: (aba: Aba) => void;
}) {
  const { resumo, chance, passos, sinais } = metricas;
  const recorrente = chance.estado === "aberto" && chance.recorrente;
  return (
    <div className="space-y-6">
      <section className="flex flex-col items-center gap-5 sm:flex-row sm:items-center sm:gap-7">
        <AnelDaChance chance={chance} />
        <div className="min-w-0 flex-1 text-center sm:text-left">
          {chance.estado === "aberto" ? (
            <div className="mb-1.5 inline-flex items-center gap-1.5 text-sm font-bold text-[var(--ks-color-text-primary)]">
              {(() => {
                const faixa = faixaDaChance(chance.atual.chance);
                return (
                  <>
                    <faixa.Icone size={16} style={{ color: faixa.cor }} aria-hidden />
                    {faixa.rotulo} pelo link
                  </>
                );
              })()}
            </div>
          ) : null}
          <FraseDaChance chance={chance} recorrente={recorrente} />
          {chance.estado === "aberto" && chance.daqui.length ? (
            <p className="mt-2 text-[13px] text-[var(--ks-color-text-muted)]">
              Se ninguém fizer nada:{" "}
              {chance.daqui.map((ponto, indice) => (
                <span key={ponto.dias}>
                  {indice ? " · " : ""}
                  {ponto.rotulo.toLowerCase()} <strong className="font-semibold text-[var(--ks-color-text-secondary)]">{porcento(ponto.chance)}</strong>
                </span>
              ))}
              .{" "}
              <button type="button" onClick={() => irPara("chance")} className="font-semibold text-[var(--ks-brand-600)] hover:underline">
                Como a conta é feita
              </button>
            </p>
          ) : null}
        </div>
      </section>

      <section className="grid grid-cols-2 gap-y-4 border-y border-[var(--ks-color-border-subtle)] py-4 sm:grid-cols-4 sm:divide-x sm:divide-[var(--ks-color-border-subtle)]">
        <Kpi
          Icone={Eye}
          valor={resumo.aberturas.toLocaleString("pt-BR")}
          rotulo={resumo.aberturas === 1 ? "abertura" : "aberturas"}
          sub={resumo.ultima ? `última ${haQuantoTempo(resumo.ultima, Date.parse(metricas.geradoEm))}` : "nenhuma ainda"}
        />
        {/* Link aberto só antes do registro detalhado: não há visita,
            aparelho nem tempo guardados. Zero ali diria "não leu", o que não
            se sabe — então fica um traço. */}
        {resumo.aberturasDetalhadas === 0 && resumo.legado ? (
          <>
            <Kpi Icone={Repeat} valor="—" rotulo="visitas" sub="antes do registro detalhado" />
            <Kpi Icone={Smartphone} valor="—" rotulo="aparelhos" sub="antes do registro detalhado" />
            <Kpi Icone={Timer} valor="—" rotulo="lendo o orçamento" sub="antes do registro detalhado" />
          </>
        ) : (
          <>
            <Kpi
              Icone={Repeat}
              valor={String(resumo.visitas)}
              rotulo={resumo.visitas === 1 ? "visita" : "visitas"}
              sub={
                resumo.visitas > 1
                  ? `voltou ${resumo.visitas - 1} ${resumo.visitas - 1 === 1 ? "vez" : "vezes"}`
                  : resumo.visitas === 1
                    ? "ainda não voltou"
                    : undefined
              }
            />
            <Kpi
              Icone={Smartphone}
              valor={String(resumo.aparelhos)}
              rotulo={resumo.aparelhos === 1 ? "aparelho" : "aparelhos"}
              sub={resumo.aparelhos > 1 ? "pode ter sido repassado" : undefined}
            />
            <Kpi
              Icone={Timer}
              valor={duracaoLegivel(resumo.segundos)}
              rotulo="lendo o orçamento"
              sub={resumo.rolagem ? `leu até ${resumo.rolagem}% da folha` : undefined}
            />
          </>
        )}
      </section>

      {/* Só aberturas de antes do registro detalhado (em produção desde 1º/10/2026):
          o traço nos números precisa de explicação, senão parece que o painel
          escondeu os dados. */}
      {resumo.aberturasDetalhadas === 0 && resumo.legado ? (
        <p className="-mt-2 text-[12.5px] leading-5 text-[var(--ks-color-text-muted)]">
          Essas aberturas são de antes de 1º/10/2026, quando o registro detalhado entrou no ar: o painel sabe quantas foram e quando, não o
          aparelho nem o tempo de leitura. As próximas aberturas entram completas, mesmo depois do pedido.
        </p>
      ) : null}

      <section>
        <Eyebrow>O que fazer agora</Eyebrow>
        {passos.length ? (
          <ul className="mt-1">
            {passos.slice(0, 4).map((passo, indice, lista) => {
              const primeiroComWhatsapp = lista.findIndex(
                (item) => item.botao === "whatsapp" && item.mensagem && linkDoWhatsapp(metricas.orcamento.cliente.whatsapp, item.mensagem)
              );
              return (
                <LinhaDoPasso
                  key={passo.id}
                  passo={passo}
                  whatsapp={metricas.orcamento.cliente.whatsapp}
                  destaque={indice === primeiroComWhatsapp}
                  aoRenovar={aoRenovar}
                  aoReenviar={aoReenviar}
                  ocupado={ocupado}
                />
              );
            })}
          </ul>
        ) : (
          <p className="mt-2 text-sm text-[var(--ks-color-text-muted)]">
            {chance.estado === "fechado"
              ? "Pedido gerado. Se o cliente abrir o link de novo, a abertura continua entrando aqui."
              : chance.estado === "recusado"
                ? "Nada a fazer: o orçamento foi recusado."
              : "Nenhuma ação pedida agora. Assim que o cliente abrir ou fizer algo no link, aparece aqui."}
          </p>
        )}
      </section>

      {sinais.length ? (
        <section>
          <Eyebrow>O que aconteceu no link</Eyebrow>
          <div className="mt-2.5 flex flex-wrap gap-2">
            {sinais.map((sinal) => (
              <span
                key={sinal.id}
                className="inline-flex items-center gap-1.5 rounded-full border border-[var(--ks-color-border-subtle)] bg-[var(--ks-color-bg-elevated)] px-3 py-1 text-xs font-medium text-[var(--ks-color-text-secondary)]"
              >
                <span
                  aria-hidden
                  className="h-1.5 w-1.5 rounded-full"
                  style={{
                    background:
                      sinal.tom === "bom"
                        ? "var(--ks-color-status-success)"
                        : sinal.tom === "atencao"
                          ? "var(--ks-color-status-danger)"
                          : "var(--ks-brand-500)",
                  }}
                />
                {sinal.texto}
              </span>
            ))}
          </div>
        </section>
      ) : null}
    </div>
  );
}

/* ---------------------------------------------------------------- */
/* Aba Visitas                                                       */
/* ---------------------------------------------------------------- */

type ItemDaLinha =
  | { tipo: "visita"; em: string; visita: VisitaDoLink }
  | { tipo: "envio"; em: string; canal: "whatsapp" | "link" };

function LinhaDaVisita({ visita, aparelho, cor }: { visita: VisitaDoLink; aparelho: AparelhoDoLink | undefined; cor: string }) {
  const Icone = ICONE_DO_TIPO[aparelho?.tipo ?? "celular"];
  const duracao = Date.parse(visita.fim) - Date.parse(visita.inicio);
  const vistas = visita.secoes.filter((secao) => secao !== "fim").map((secao) => ROTULO_DA_SECAO[secao]);
  const detalhes = [
    visita.segundos ? `${duracaoLegivel(visita.segundos)} lendo` : "sem leitura ativa",
    vistas.length ? `viu ${vistas.join(", ")}` : "",
    visita.aberturas > 1 ? `${visita.aberturas} aberturas` : "",
  ].filter(Boolean);
  return (
    <li className="flex items-start gap-3 border-t border-[var(--ks-color-border-subtle)]/60 py-3 first:border-t-0">
      <span
        aria-hidden
        className="mt-0.5 flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[var(--ks-color-bg-inset)]"
        style={{ color: cor }}
      >
        <Icone size={15} strokeWidth={2.2} />
      </span>
      <div className="min-w-0 flex-1">
        <div className="flex flex-wrap items-baseline gap-x-2 gap-y-0.5">
          <span className="text-sm font-semibold tabular-nums text-[var(--ks-color-text-primary)]">
            {hora(visita.inicio)}
            {duracao >= 60_000 ? `–${hora(visita.fim)}` : ""}
          </span>
          <span className="truncate text-sm text-[var(--ks-color-text-secondary)]">{aparelho?.nome ?? "Aparelho"}</span>
          {visita.depoisDoPedido ? (
            <span className="rounded-full bg-[var(--ks-color-bg-inset)] px-2 py-0.5 text-[10.5px] font-semibold text-[var(--ks-color-text-muted)]">
              depois do pedido
            </span>
          ) : null}
        </div>
        <div className="mt-0.5 text-[12.5px] text-[var(--ks-color-text-muted)]">{detalhes.join(" · ")}</div>
        {visita.origem || visita.lugar ? (
          <div className="mt-0.5 flex flex-wrap gap-x-3 text-[11.5px] text-[var(--ks-color-text-soft)]">
            {visita.origem ? <span>veio de {visita.origem}</span> : null}
            {visita.lugar ? (
              <span className="inline-flex items-center gap-1">
                <MapPin size={11} aria-hidden />
                {visita.lugar}
              </span>
            ) : null}
          </div>
        ) : null}
        {visita.acoes.length ? (
          <div className="mt-1.5 flex flex-wrap gap-1.5">
            {[...new Set(visita.acoes.map((acao) => acao.tipo))].map((tipo) => {
              const { rotulo, Icone: IconeDaAcao } = ACAO[tipo];
              return (
                <span
                  key={tipo}
                  className="inline-flex items-center gap-1 rounded-full border border-[var(--ks-color-border-subtle)] px-2 py-0.5 text-[11px] font-semibold text-[var(--ks-color-text-secondary)]"
                >
                  <IconeDaAcao size={11} aria-hidden />
                  {rotulo}
                </span>
              );
            })}
          </div>
        ) : null}
      </div>
      <MiniAnel fracao={visita.rolagem / 100} rotulo={`Leu até ${visita.rolagem}% da folha`} />
    </li>
  );
}

function AbaVisitas({
  metricas,
  cores,
  agora,
  irPara,
}: {
  metricas: MetricasDoLink;
  cores: Map<string, string>;
  agora: number;
  irPara: (aba: Aba) => void;
}) {
  const { resumo, visitas, envios, aparelhos, equipe } = metricas;
  const porId = useMemo(() => new Map(aparelhos.map((aparelho) => [aparelho.id, aparelho])), [aparelhos]);
  const dias = useMemo(() => {
    const itens: ItemDaLinha[] = [
      ...visitas.map((visita) => ({ tipo: "visita" as const, em: visita.inicio, visita })),
      ...envios.map((envio) => ({ tipo: "envio" as const, em: envio.em, canal: envio.canal })),
    ].sort((a, b) => Date.parse(b.em) - Date.parse(a.em));
    const grupos: Array<{ rotulo: string; itens: ItemDaLinha[] }> = [];
    for (const item of itens) {
      const rotulo = rotuloDoDia(item.em, agora);
      const ultimo = grupos[grupos.length - 1];
      if (ultimo && ultimo.rotulo === rotulo) ultimo.itens.push(item);
      else grupos.push({ rotulo, itens: [item] });
    }
    return grupos;
  }, [visitas, envios, agora]);
  const totalDeHoras = resumo.horas.reduce((soma, valor) => soma + valor, 0);

  return (
    <div className="space-y-6">
      <section className="flex flex-col items-center gap-5 sm:flex-row sm:items-center sm:gap-7">
        <RelogioDeHoras horas={resumo.horas} faixa={resumo.faixa ? { inicio: resumo.faixa.inicio, fim: resumo.faixa.fim } : null}>
          {resumo.faixa ? (
            <>
              <span className="text-[15px] font-extrabold tracking-tight text-[var(--ks-color-text-primary)]">
                {resumo.faixa.inicio}h–{resumo.faixa.fim}h
              </span>
              <span className="mt-1 text-[10px] font-semibold text-[var(--ks-color-text-muted)]">mais abre</span>
            </>
          ) : (
            <>
              <span className="text-[15px] font-extrabold text-[var(--ks-color-text-primary)]">{totalDeHoras}</span>
              <span className="mt-1 text-[10px] font-semibold text-[var(--ks-color-text-muted)]">{totalDeHoras === 1 ? "abertura" : "aberturas"}</span>
            </>
          )}
        </RelogioDeHoras>
        <div className="min-w-0 flex-1 text-center sm:text-left">
          <div className="text-sm font-bold text-[var(--ks-color-text-primary)]">Quando ele abre</div>
          <p className="mt-1 text-sm leading-6 text-[var(--ks-color-text-secondary)]">
            {resumo.faixa
              ? `${resumo.faixa.aberturas} das ${resumo.faixa.total} aberturas foram entre ${resumo.faixa.inicio}h e ${resumo.faixa.fim}h (horário de Brasília).`
              : totalDeHoras
                ? "Cada barra é uma hora do dia; quanto mais comprida, mais aberturas naquela hora. Ainda não dá para dizer um horário preferido."
                : "As horas aparecem aqui quando o cliente abrir o link."}
          </p>
          {resumo.primeira ? (
            <p className="mt-1.5 text-[12.5px] text-[var(--ks-color-text-muted)]">
              Primeira abertura: {dataHora(resumo.primeira)}
              {resumo.ultima ? ` · Última: ${dataHora(resumo.ultima)}` : ""}
            </p>
          ) : null}
        </div>
      </section>

      <section>
        <Eyebrow>Linha do tempo</Eyebrow>
        {dias.length ? (
          <div className="mt-2 space-y-4">
            {dias.map((dia) => (
              <div key={dia.rotulo}>
                <div className="text-xs font-bold text-[var(--ks-color-text-secondary)]">{dia.rotulo}</div>
                <ul className="mt-1">
                  {dia.itens.map((item) =>
                    item.tipo === "visita" ? (
                      <LinhaDaVisita
                        key={`v-${item.visita.id}`}
                        visita={item.visita}
                        aparelho={porId.get(item.visita.aparelhoId)}
                        cor={cores.get(item.visita.aparelhoId) ?? COR_DOS_OUTROS}
                      />
                    ) : (
                      <li key={`e-${item.em}`} className="flex items-center gap-3 border-t border-[var(--ks-color-border-subtle)]/60 py-2.5 first:border-t-0">
                        <span aria-hidden className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full text-[var(--ks-color-text-soft)]">
                          <Send size={14} />
                        </span>
                        <span className="text-[13px] text-[var(--ks-color-text-muted)]">
                          <span className="tabular-nums">{hora(item.em)}</span> ·{" "}
                          {item.canal === "whatsapp" ? "A empresa mandou o link pelo WhatsApp" : "A empresa copiou o link"}
                        </span>
                      </li>
                    )
                  )}
                </ul>
              </div>
            ))}
          </div>
        ) : (
          <p className="mt-2 text-sm text-[var(--ks-color-text-muted)]">Nenhuma visita registrada ainda.</p>
        )}
        {resumo.legado ? (
          <p className="mt-4 flex items-start gap-2 text-[12.5px] leading-5 text-[var(--ks-color-text-muted)]">
            <History size={14} className="mt-0.5 shrink-0" aria-hidden />
            <span>
              Antes do registro detalhado, o link foi aberto {resumo.legado.aberturas} {resumo.legado.aberturas === 1 ? "vez" : "vezes"}
              {resumo.legado.primeira ? `, a primeira em ${dataHora(resumo.legado.primeira)}` : ""}. Dessas não há aparelho nem tempo de leitura.
            </span>
          </p>
        ) : null}
        {equipe.aberturas ? (
          <p className="mt-2 flex items-start gap-2 text-[12.5px] leading-5 text-[var(--ks-color-text-muted)]">
            <UserCheck size={14} className="mt-0.5 shrink-0" aria-hidden />
            <span>
              {equipe.aberturas} {equipe.aberturas === 1 ? "abertura foi" : "aberturas foram"} da própria equipe e não {equipe.aberturas === 1 ? "entra" : "entram"} na conta.{" "}
              <button type="button" onClick={() => irPara("aparelhos")} className="font-semibold text-[var(--ks-brand-600)] hover:underline">
                Ver aparelhos
              </button>
            </span>
          </p>
        ) : null}
      </section>
    </div>
  );
}

/* ---------------------------------------------------------------- */
/* Aba Aparelhos                                                     */
/* ---------------------------------------------------------------- */

function Legenda({ itens }: { itens: Array<{ id: string; cor: string; rotulo: string; valor: number; total: number }> }) {
  return (
    <ul className="min-w-0 flex-1 space-y-1.5">
      {itens.map((item) => (
        <li key={item.id} className="flex items-center gap-2 text-[13px]">
          <span aria-hidden className="h-2.5 w-2.5 shrink-0 rounded-full" style={{ background: item.cor }} />
          <span className="min-w-0 flex-1 truncate text-[var(--ks-color-text-secondary)]">{item.rotulo}</span>
          <span className="shrink-0 tabular-nums font-semibold text-[var(--ks-color-text-primary)]">{item.valor}</span>
          <span className="w-10 shrink-0 text-right tabular-nums text-[var(--ks-color-text-soft)]">
            {item.total ? porcento(item.valor / item.total) : "0%"}
          </span>
        </li>
      ))}
    </ul>
  );
}

function LinhaDoAparelho({
  aparelho,
  cor,
  agora,
  acao,
}: {
  aparelho: AparelhoDoLink;
  cor: string;
  agora: number;
  acao?: ReactNode;
}) {
  const Icone = ICONE_DO_TIPO[aparelho.tipo];
  const numeros = [
    `${aparelho.aberturas} ${aparelho.aberturas === 1 ? "abertura" : "aberturas"}`,
    aparelho.visitas ? `${aparelho.visitas} ${aparelho.visitas === 1 ? "visita" : "visitas"}` : "",
    aparelho.segundos ? `${duracaoLegivel(aparelho.segundos)} lendo` : "",
    `última ${haQuantoTempo(aparelho.ultima, agora)}`,
  ].filter(Boolean);
  return (
    <li className="flex flex-col gap-2 border-t border-[var(--ks-color-border-subtle)]/60 py-3 first:border-t-0 sm:flex-row sm:items-center">
      <div className="flex min-w-0 flex-1 items-start gap-3">
        <span aria-hidden className="mt-0.5 flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-[var(--ks-color-bg-inset)]" style={{ color: cor }}>
          <Icone size={17} strokeWidth={2.2} />
        </span>
        <div className="min-w-0">
          <div className="text-sm font-semibold text-[var(--ks-color-text-primary)]">{aparelho.nome}</div>
          {aparelho.detalhe ? <div className="text-[12.5px] text-[var(--ks-color-text-muted)]">{aparelho.detalhe}</div> : null}
          <div className="mt-0.5 flex flex-wrap gap-x-3 text-[12px] text-[var(--ks-color-text-soft)]">
            <span>{numeros.join(" · ")}</span>
            {aparelho.lugar ? (
              <span className="inline-flex items-center gap-1">
                <MapPin size={11} aria-hidden />
                {aparelho.lugar}
              </span>
            ) : null}
          </div>
        </div>
      </div>
      {acao ? <div className="flex shrink-0 pl-12 sm:pl-0">{acao}</div> : null}
    </li>
  );
}

function AbaAparelhos({
  metricas,
  cores,
  agora,
  podeMarcar,
  aoMarcar,
}: {
  metricas: MetricasDoLink;
  cores: Map<string, string>;
  agora: number;
  podeMarcar: boolean;
  aoMarcar: (leitores: string[], marcar: boolean) => Promise<boolean>;
}) {
  const { aparelhos, equipe, resumo } = metricas;
  const [confirmando, setConfirmando] = useState<string | null>(null);
  const [marcando, setMarcando] = useState<string | null>(null);
  const totalDeAberturas = aparelhos.reduce((soma, aparelho) => soma + aparelho.aberturas, 0);

  const porTipo = (["celular", "computador", "tablet"] as TipoDeAparelho[])
    .map((tipo) => ({
      id: tipo,
      rotulo: NOME_DO_TIPO[tipo],
      cor: COR_DO_TIPO[tipo],
      valor: aparelhos.filter((aparelho) => aparelho.tipo === tipo).reduce((soma, aparelho) => soma + aparelho.aberturas, 0),
    }))
    .filter((fatia) => fatia.valor > 0);

  const principais = aparelhos.slice(0, 4);
  const resto = aparelhos.slice(4);
  const porAparelho: Fatia[] = [
    ...principais.map((aparelho) => ({ id: aparelho.id, rotulo: aparelho.nome, valor: aparelho.aberturas, cor: cores.get(aparelho.id) ?? COR_DOS_OUTROS })),
    ...(resto.length
      ? [{ id: "outros", rotulo: `Outros ${resto.length}`, valor: resto.reduce((soma, aparelho) => soma + aparelho.aberturas, 0), cor: COR_DOS_OUTROS }]
      : []),
  ];

  async function marcar(id: string, leitores: string[], valor: boolean) {
    setMarcando(id);
    const deu = await aoMarcar(leitores, valor);
    setMarcando(null);
    if (deu) setConfirmando(null);
  }

  if (!aparelhos.length && !equipe.aparelhos.length) {
    return (
      <p className="py-6 text-center text-sm text-[var(--ks-color-text-muted)]">
        {resumo.legado
          ? "As aberturas deste link são de antes do registro detalhado: não há aparelho guardado delas. As próximas aparecem aqui."
          : "Quando o cliente abrir o link, o aparelho dele aparece aqui."}
      </p>
    );
  }

  return (
    <div className="space-y-6">
      {aparelhos.length ? (
        <section className="grid gap-6 sm:grid-cols-2">
          <div className="flex min-w-0 items-center gap-4">
            <Rosca
              fatias={porTipo}
              tamanho={120}
              espessura={14}
              rotulo={`Aberturas por tipo de aparelho: ${porTipo.map((fatia) => `${fatia.rotulo} ${fatia.valor}`).join(", ")}`}
            >
              <span className="text-lg font-extrabold text-[var(--ks-color-text-primary)]">{totalDeAberturas}</span>
              <span className="mt-0.5 text-[10px] font-semibold text-[var(--ks-color-text-muted)]">{totalDeAberturas === 1 ? "abertura" : "aberturas"}</span>
            </Rosca>
            <div className="min-w-0 flex-1">
              <div className="mb-2 text-xs font-bold text-[var(--ks-color-text-secondary)]">Tipo de aparelho</div>
              <Legenda itens={porTipo.map((fatia) => ({ ...fatia, total: totalDeAberturas }))} />
            </div>
          </div>
          <div className="flex min-w-0 items-center gap-4">
            <Rosca
              fatias={porAparelho}
              tamanho={120}
              espessura={14}
              rotulo={`Aberturas por aparelho: ${porAparelho.map((fatia) => `${fatia.rotulo} ${fatia.valor}`).join(", ")}`}
            >
              <span className="text-lg font-extrabold text-[var(--ks-color-text-primary)]">{aparelhos.length}</span>
              <span className="mt-0.5 text-[10px] font-semibold text-[var(--ks-color-text-muted)]">{aparelhos.length === 1 ? "aparelho" : "aparelhos"}</span>
            </Rosca>
            <div className="min-w-0 flex-1">
              <div className="mb-2 text-xs font-bold text-[var(--ks-color-text-secondary)]">Aberturas por aparelho</div>
              <Legenda itens={porAparelho.map((fatia) => ({ ...fatia, total: totalDeAberturas }))} />
            </div>
          </div>
        </section>
      ) : null}

      {aparelhos.length >= 2 ? (
        <p className="flex items-start gap-2.5 rounded-2xl bg-[var(--ks-color-bg-inset)] px-4 py-3 text-[13px] leading-5 text-[var(--ks-color-text-secondary)]">
          <Users size={16} className="mt-0.5 shrink-0 text-[var(--ks-brand-600)]" aria-hidden />
          <span>
            Aberto em <strong className="font-semibold text-[var(--ks-color-text-primary)]">{aparelhos.length} aparelhos diferentes</strong>. Pode ser o
            mesmo cliente no celular e no computador, ou o orçamento repassado a outra pessoa (um sócio, o financeiro). Se algum desses foi você,
            marque em &ldquo;Fui eu&rdquo; para ele sair da conta.
          </span>
        </p>
      ) : null}

      {aparelhos.length ? (
        <section>
          <Eyebrow>Aparelhos do cliente</Eyebrow>
          <ul className="mt-1">
            {aparelhos.map((aparelho) => (
              <LinhaDoAparelho
                key={aparelho.id}
                aparelho={aparelho}
                cor={cores.get(aparelho.id) ?? COR_DOS_OUTROS}
                agora={agora}
                acao={
                  !podeMarcar ? null : confirmando === aparelho.id ? (
                    <span className="inline-flex flex-wrap items-center gap-2">
                      <span className="text-xs font-medium text-[var(--ks-color-text-muted)]">Tirar da conta do cliente?</span>
                      <button
                        type="button"
                        onClick={() => void marcar(aparelho.id, aparelho.leitores, true)}
                        disabled={marcando === aparelho.id}
                        className="ks-button ks-button-primary ks-button-sm"
                      >
                        {marcando === aparelho.id ? <Loader2 size={13} className="animate-spin" /> : <UserCheck size={13} />}
                        Sim, fui eu
                      </button>
                      <button type="button" onClick={() => setConfirmando(null)} className="ks-button ks-button-ghost ks-button-sm">
                        Cancelar
                      </button>
                    </span>
                  ) : (
                    <button
                      type="button"
                      onClick={() => setConfirmando(aparelho.id)}
                      title="Este aparelho é seu ou da sua equipe: as aberturas dele saem da conta do cliente"
                      className="ks-button ks-button-ghost ks-button-sm"
                    >
                      <UserCheck size={13} />
                      Fui eu
                    </button>
                  )
                }
              />
            ))}
          </ul>
        </section>
      ) : null}

      {equipe.aparelhos.length ? (
        <section>
          <Eyebrow>Da própria equipe (não entram na conta)</Eyebrow>
          <ul className="mt-1">
            {equipe.aparelhos.map((aparelho: AparelhoDaEquipe) => (
              <LinhaDoAparelho
                key={aparelho.id}
                aparelho={aparelho}
                cor="var(--ks-color-text-soft)"
                agora={agora}
                acao={
                  podeMarcar && aparelho.marcadoAMao ? (
                    <button
                      type="button"
                      onClick={() => void marcar(aparelho.id, aparelho.leitores, false)}
                      disabled={marcando === aparelho.id}
                      className="ks-button ks-button-ghost ks-button-sm"
                    >
                      {marcando === aparelho.id ? <Loader2 size={13} className="animate-spin" /> : <Undo2 size={13} />}
                      Não fui eu
                    </button>
                  ) : (
                    <span className="text-[11.5px] text-[var(--ks-color-text-soft)]">
                      {aparelho.marcadoAMao ? "marcado como da equipe" : "navegador do painel"}
                    </span>
                  )
                }
              />
            ))}
          </ul>
        </section>
      ) : null}

      <p className="text-[11.5px] leading-5 text-[var(--ks-color-text-soft)]">
        O aparelho vem do que o próprio navegador informa. Dois navegadores no mesmo celular (o do WhatsApp e o Chrome, por exemplo) contam como um
        aparelho só; dois aparelhos iguais, do mesmo modelo e com o mesmo sistema, podem aparecer como um. A cidade aparece quando a rede informa.
      </p>
    </div>
  );
}

/* ---------------------------------------------------------------- */
/* Aba Chance                                                        */
/* ---------------------------------------------------------------- */

function Fator({ Icone, titulo, children }: { Icone: LucideIcon; titulo: string; children: ReactNode }) {
  return (
    <li className="flex items-start gap-3 border-t border-[var(--ks-color-border-subtle)]/60 py-3 first:border-t-0">
      <Icone size={16} className="mt-0.5 shrink-0 text-[var(--ks-brand-600)]" aria-hidden />
      <div className="min-w-0">
        <div className="text-sm font-semibold text-[var(--ks-color-text-primary)]">{titulo}</div>
        <div className="mt-0.5 text-[13px] leading-5 text-[var(--ks-color-text-muted)]">{children}</div>
      </div>
    </li>
  );
}

function AbaChance({ metricas }: { metricas: MetricasDoLink }) {
  const { chance } = metricas;
  if (chance.estado !== "aberto") {
    return (
      <div className="flex flex-col items-center gap-4 py-4 text-center sm:flex-row sm:text-left">
        <AnelDaChance chance={chance} tamanho={140} />
        <div className="max-w-md">
          <FraseDaChance chance={chance} />
        </div>
      </div>
    );
  }
  const { atual } = chance;
  const faixa = faixaDaChance(atual.chance);
  const inicio = chance.coorte.desde ? formatoDiaMes.format(new Date(chance.coorte.desde)) : null;
  const fim = chance.coorte.ate ? formatoDiaMes.format(new Date(chance.coorte.ate)) : null;
  const precisao = { alta: "alta", media: "média", baixa: "baixa" }[atual.precisao];

  return (
    <div className="space-y-6">
      <section className="flex flex-col items-center gap-5 sm:flex-row sm:gap-7">
        <AnelDaChance chance={chance} tamanho={148} />
        <div className="min-w-0 flex-1 text-center sm:text-left">
          <div className="mb-1.5 inline-flex items-center gap-1.5 text-sm font-bold text-[var(--ks-color-text-primary)]">
            <faixa.Icone size={16} style={{ color: faixa.cor }} aria-hidden />
            {faixa.rotulo} pelo link · precisão {precisao}
          </div>
          <FraseDaChance chance={chance} recorrente={chance.recorrente} />
        </div>
      </section>

      {chance.daqui.length ? (
        <section>
          <Eyebrow>Se ninguém fizer nada</Eyebrow>
          <div className="mt-3 grid grid-cols-3 gap-2 sm:max-w-md">
            {chance.daqui.map((ponto, indice) => (
              <div key={ponto.dias} className="flex flex-col items-center gap-1.5">
                <AnelDeValor
                  fracao={ponto.chance}
                  tamanho={72}
                  espessura={7}
                  cor={faixaDaChance(ponto.chance).cor}
                  atraso={0.1 + 0.1 * indice}
                  rotulo={`${ponto.rotulo}: ${porcento(ponto.chance)}`}
                >
                  <span className="text-[15px] font-extrabold text-[var(--ks-color-text-primary)]">{porcento(ponto.chance)}</span>
                </AnelDeValor>
                <span className="text-xs font-semibold text-[var(--ks-color-text-muted)]">{ponto.rotulo}</span>
              </div>
            ))}
          </div>
          <p className="mt-3 text-[12.5px] text-[var(--ks-color-text-muted)]">
            É o que aconteceu com os orçamentos que ficaram esse tempo a mais sem resposta. A cada dia parado, menos deles viram pedido.
          </p>
        </section>
      ) : null}

      <section>
        <Eyebrow>Como a conta é feita</Eyebrow>
        <ul className="mt-1">
          <Fator Icone={Clock3} titulo={chance.aberto ? `Tempo desde a primeira abertura: ${tempoDesde(chance.diasDesde)}` : `Tempo sem abrir: ${tempoDesde(chance.diasDesde)}`}>
            {chance.aberto && chance.naPrimeiraAbertura ? (
              <>
                Quando o cliente abre, {porcento(chance.naPrimeiraAbertura.geral.taxa)} dos orçamentos acabam virando pedido. Dos que continuam sem
                resposta depois de {tempoDesde(chance.diasDesde)}, {porcento(atual.geral.taxa)} viram.
              </>
            ) : (
              <>
                Dos orçamentos com link que continuavam sem ser abertos e sem pedido depois de {tempoDesde(chance.diasDesde)}, {porcento(atual.geral.taxa)}{" "}
                viraram pedido (muitos fechados por outro caminho, como uma conversa direta).
              </>
            )}
          </Fator>
          <Fator Icone={Repeat} titulo={chance.recorrente ? "Cliente que já comprou de você" : "Cliente de primeira compra"}>
            {chance.recorrencia && chance.recorrencia.outroGrupo !== null ? (
              <>
                Nessa mesma situação, {chance.recorrente ? "quem já comprou antes" : "quem compra pela primeira vez"} fecha{" "}
                {porcento(chance.recorrencia.mesmoGrupo)}; {chance.recorrente ? "quem compra pela primeira vez" : "quem já comprou antes"} fecha{" "}
                {porcento(chance.recorrencia.outroGrupo)}. A conta usa o grupo deste cliente.
              </>
            ) : (
              <>Ainda não há casos suficientes nessa situação para separar quem já comprou de quem compra pela primeira vez: vale a média de todos.</>
            )}
          </Fator>
          <Fator Icone={ChartPie} titulo={`Histórico da sua empresa: ${atual.empresa.casos} ${atual.empresa.casos === 1 ? "caso parecido" : "casos parecidos"}`}>
            {atual.empresa.casos ? (
              <>
                {atual.empresa.fechados} {atual.empresa.fechados === 1 ? "virou" : "viraram"} pedido. Com esse tanto de casos, a sua empresa pesa{" "}
                {porcento(atual.empresa.peso)} na conta; o resto vem dos {atual.geral.casos.toLocaleString("pt-BR")} orçamentos parecidos da plataforma.
                Quanto mais orçamentos a empresa tiver, mais a conta fica com a cara dela.
              </>
            ) : (
              <>Ainda sem casos parecidos na sua empresa: a conta usa só os {atual.geral.casos.toLocaleString("pt-BR")} orçamentos parecidos da plataforma.</>
            )}
          </Fator>
          <Fator Icone={History} titulo="De onde vêm os números">
            Orçamentos reais com link, criados {inicio && fim ? `entre ${inicio} e ${fim}` : "de janeiro em diante"}, com pelo menos 60 dias de
            história para já se saber se viraram pedido. Nenhum peso foi inventado: cada número acima é a proporção do que de fato aconteceu.
          </Fator>
        </ul>
      </section>

      <p className="flex items-start gap-2.5 rounded-2xl bg-[var(--ks-color-bg-inset)] px-4 py-3 text-[12.5px] leading-5 text-[var(--ks-color-text-secondary)]">
        <Hourglass size={15} className="mt-0.5 shrink-0 text-[var(--ks-brand-600)]" aria-hidden />
        <span>
          Voltas ao link, aparelhos diferentes, tempo de leitura e cliques ainda não entram na conta: começaram a ser registrados agora. Eles passam
          a pesar quando {MINIMO_PARA_MEDIR_SINAIS} orçamentos com esse registro tiverem terminado, para o peso de cada um ser medido e não
          chutado (hoje: {chance.comRegistroDetalhado.toLocaleString("pt-BR")}).
        </span>
      </p>
    </div>
  );
}

/* ---------------------------------------------------------------- */
/* A janela                                                          */
/* ---------------------------------------------------------------- */

export function JanelaDasMetricas({
  aberta,
  aoFechar,
  quoteId,
  metricas,
  erro,
  carregando,
  aoRecarregar,
  aoRenovarLink,
  aoReenviarLink,
  podeMarcarEquipe,
}: {
  aberta: boolean;
  aoFechar: () => void;
  quoteId: string;
  metricas: MetricasDoLink | null;
  erro: string | null;
  carregando: boolean;
  aoRecarregar: () => Promise<void> | void;
  aoRenovarLink?: () => Promise<void> | void;
  aoReenviarLink?: () => Promise<void> | void;
  podeMarcarEquipe: boolean;
}) {
  const parado = useReducedMotion();
  const [aba, setAba] = useState<Aba>("resumo");
  const [ocupado, setOcupado] = useState(false);
  const [aviso, setAviso] = useState<string | null>(null);
  const [montado, setMontado] = useState(false);
  const painel = useRef<HTMLDivElement | null>(null);
  const antes = useRef<HTMLElement | null>(null);

  useEffect(() => setMontado(true), []);

  useEffect(() => {
    if (!aberta) return;
    /* Toda vez que abre, começa pelo Resumo: é onde está o que fazer agora. */
    setAba("resumo");
    antes.current = document.activeElement as HTMLElement | null;
    const rolagem = document.body.style.overflow;
    document.body.style.overflow = "hidden";
    const aoTeclar = (evento: KeyboardEvent) => {
      if (evento.key === "Escape") {
        evento.stopPropagation();
        aoFechar();
      }
    };
    window.addEventListener("keydown", aoTeclar);
    window.setTimeout(() => painel.current?.focus(), 30);
    return () => {
      document.body.style.overflow = rolagem;
      window.removeEventListener("keydown", aoTeclar);
      antes.current?.focus?.();
    };
  }, [aberta, aoFechar]);

  useEffect(() => {
    if (!aviso) return;
    const relogio = window.setTimeout(() => setAviso(null), 4000);
    return () => window.clearTimeout(relogio);
  }, [aviso]);

  const cores = useMemo(() => {
    const mapa = new Map<string, string>();
    if (!metricas) return mapa;
    [...metricas.aparelhos]
      .sort((a, b) => Date.parse(a.primeira) - Date.parse(b.primeira))
      .forEach((aparelho, indice) => mapa.set(aparelho.id, CORES_DE_SERIE[indice] ?? COR_DOS_OUTROS));
    return mapa;
  }, [metricas]);

  const agora = metricas ? Date.parse(metricas.geradoEm) : 0;

  /* O botão fica girando até a janela já mostrar o resultado: sem isso, o
     aviso de "pronto" aparece com a lista ainda antiga. */
  async function executar(tarefa: (() => Promise<void> | void) | undefined, mensagem: string) {
    if (!tarefa) return;
    setOcupado(true);
    try {
      await tarefa();
      await aoRecarregar();
      setAviso(mensagem);
    } finally {
      setOcupado(false);
    }
  }

  async function marcarEquipe(leitores: string[], marcar: boolean) {
    try {
      const resposta = await fetch(`/api/quotes/${quoteId}/metricas-do-link`, {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({ acao: "equipe", leitores, marcar }),
      });
      const dados = (await resposta.json().catch(() => null)) as { error?: string } | null;
      if (!resposta.ok) {
        setAviso(dados?.error || "Não deu para marcar o aparelho.");
        return false;
      }
      await aoRecarregar();
      setAviso(marcar ? "Pronto: esse aparelho saiu da conta do cliente." : "Pronto: esse aparelho voltou para a conta do cliente.");
      return true;
    } catch {
      setAviso("Sem conexão. Tente de novo.");
      return false;
    }
  }

  if (!montado) return null;

  const cliente = metricas?.orcamento.cliente.nome;
  const numero = metricas?.orcamento.numero;
  const agoraAberto = Boolean(metricas?.resumo.abertoAgora);

  return createPortal(
    <AnimatePresence>
      {aberta ? (
        <motion.div
          key="fundo"
          className="fixed inset-0 z-[80] flex items-end justify-center bg-[rgba(20,16,48,0.45)] backdrop-blur-[2px] sm:items-center sm:p-6"
          initial={parado ? false : { opacity: 0 }}
          animate={{ opacity: 1 }}
          exit={parado ? { opacity: 0 } : { opacity: 0 }}
          onMouseDown={(evento) => {
            if (evento.target === evento.currentTarget) aoFechar();
          }}
        >
          <motion.div
            ref={painel}
            role="dialog"
            aria-modal="true"
            aria-labelledby="titulo-metricas-do-link"
            tabIndex={-1}
            className="flex max-h-[92dvh] w-full flex-col overflow-hidden rounded-t-3xl border border-[var(--ks-color-border-subtle)] bg-[var(--ks-color-bg-elevated)] shadow-[var(--ks-shadow-overlay)] outline-none sm:max-h-[min(88vh,860px)] sm:max-w-[880px] sm:rounded-3xl"
            initial={parado ? false : { y: 24, opacity: 0 }}
            animate={{ y: 0, opacity: 1 }}
            exit={parado ? { opacity: 0 } : { y: 16, opacity: 0 }}
            transition={{ duration: 0.22, ease: [0.22, 1, 0.36, 1] }}
          >
            {/* Alça do celular: diz que a folha desce. */}
            <div className="flex justify-center pt-2 sm:hidden" aria-hidden>
              <span className="h-1 w-10 rounded-full bg-[var(--ks-color-border-strong)]" />
            </div>

            <header className="flex items-start gap-3 px-5 pb-3 pt-3 sm:px-7 sm:pt-6">
              <span
                aria-hidden
                className="flex h-10 w-10 shrink-0 items-center justify-center rounded-2xl text-white"
                style={{ background: SELO.neutro, boxShadow: "var(--ks-shadow-brand)" }}
              >
                <ChartPie size={19} strokeWidth={2.3} />
              </span>
              <div className="min-w-0 flex-1">
                <h2 id="titulo-metricas-do-link" className="text-base font-extrabold tracking-tight text-[var(--ks-color-text-primary)] sm:text-lg">
                  Métricas do link
                </h2>
                <p className="truncate text-[13px] text-[var(--ks-color-text-muted)]">
                  {numero ? `Orçamento #${numero}` : "Orçamento"}
                  {cliente ? ` · ${cliente}` : ""}
                </p>
                {/* No celular não cabe a pílula do lado: o estado vem aqui. */}
                {metricas ? (
                  <p
                    className={`mt-0.5 flex items-center gap-1.5 text-[11.5px] font-semibold sm:hidden ${
                      agoraAberto ? "text-[var(--ks-color-status-success)]" : "text-[var(--ks-color-text-soft)]"
                    }`}
                  >
                    <span
                      aria-hidden
                      className="h-1.5 w-1.5 rounded-full"
                      style={{ background: agoraAberto ? "var(--ks-color-status-success)" : "var(--ks-color-border-strong)" }}
                    />
                    {agoraAberto
                      ? "Aberto agora"
                      : metricas.resumo.ultima
                        ? `Última atividade ${haQuantoTempo(metricas.resumo.ultima, agora)}`
                        : "Ainda não aberto"}
                  </p>
                ) : null}
              </div>
              <div className="flex shrink-0 items-center gap-1.5">
                {metricas ? (
                  <span
                    className={`hidden items-center gap-1.5 rounded-full px-2.5 py-1 text-[11.5px] font-semibold sm:inline-flex ${
                      agoraAberto ? "text-[var(--ks-color-status-success)]" : "text-[var(--ks-color-text-muted)]"
                    }`}
                    aria-live="polite"
                  >
                    <span className="relative flex h-2 w-2">
                      {agoraAberto ? (
                        <span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-[var(--ks-color-status-success)] opacity-60 motion-reduce:hidden" />
                      ) : null}
                      <span
                        className="relative inline-flex h-2 w-2 rounded-full"
                        style={{ background: agoraAberto ? "var(--ks-color-status-success)" : "var(--ks-color-border-strong)" }}
                      />
                    </span>
                    {agoraAberto
                      ? "Aberto agora"
                      : metricas.resumo.ultima
                        ? `Última atividade ${haQuantoTempo(metricas.resumo.ultima, agora)}`
                        : "Ainda não aberto"}
                  </span>
                ) : null}
                <button
                  type="button"
                  onClick={() => void aoRecarregar()}
                  disabled={carregando}
                  aria-label="Atualizar"
                  title="Atualizar"
                  className="ks-icon-button ks-icon-button-sm"
                >
                  <RefreshCw size={15} className={carregando ? "animate-spin" : ""} />
                </button>
                <button type="button" onClick={aoFechar} aria-label="Fechar" className="ks-icon-button ks-icon-button-sm">
                  <X size={15} />
                </button>
              </div>
            </header>

            <nav className="flex gap-5 overflow-x-auto border-b border-[var(--ks-color-border-subtle)] px-5 sm:gap-7 sm:px-7" role="tablist" aria-label="Seções das métricas">
              {ABAS.map(({ id, rotulo, curto, Icone }) => {
                const ativa = aba === id;
                return (
                  <button
                    key={id}
                    type="button"
                    role="tab"
                    aria-selected={ativa}
                    onClick={() => setAba(id)}
                    className={`-mb-px inline-flex shrink-0 items-center gap-1.5 border-b-2 pb-3 pt-1 text-sm font-semibold transition-colors ${
                      ativa
                        ? "border-[var(--ks-brand-600)] text-[var(--ks-color-text-primary)]"
                        : "border-transparent text-[var(--ks-color-text-muted)] hover:text-[var(--ks-color-text-primary)]"
                    }`}
                  >
                    <Icone size={15} aria-hidden />
                    {rotulo === curto ? (
                      rotulo
                    ) : (
                      <>
                        <span className="sm:hidden">{curto}</span>
                        <span className="hidden sm:inline">{rotulo}</span>
                      </>
                    )}
                  </button>
                );
              })}
            </nav>

            <div className="relative min-h-0 flex-1 overflow-y-auto px-5 py-5 sm:px-7 sm:py-6">
              {!metricas ? (
                erro ? (
                  <div className="flex flex-col items-center gap-3 py-10 text-center">
                    <p className="text-sm text-[var(--ks-color-text-secondary)]">{erro}</p>
                    <button type="button" onClick={() => void aoRecarregar()} className="ks-button ks-button-secondary ks-button-sm">
                      <RefreshCw size={14} />
                      Tentar de novo
                    </button>
                  </div>
                ) : (
                  <div className="flex items-center justify-center gap-2 py-16 text-sm text-[var(--ks-color-text-muted)]">
                    <Loader2 size={16} className="animate-spin" />
                    Juntando as aberturas do link…
                  </div>
                )
              ) : (
                /* Ao atualizar, o que já estava na tela fica, só mais claro:
                   nada pula de lugar. */
                <div className={`transition-opacity ${carregando ? "opacity-70" : "opacity-100"}`}>
                  {aba === "resumo" ? (
                    <AbaResumo
                      metricas={metricas}
                      ocupado={ocupado}
                      irPara={setAba}
                      aoRenovar={aoRenovarLink ? () => void executar(aoRenovarLink, "Link gerado de novo e copiado. Mande outra vez para o cliente.") : undefined}
                      aoReenviar={aoReenviarLink ? () => void executar(aoReenviarLink, "Abrimos o WhatsApp com a mensagem e o link.") : undefined}
                    />
                  ) : null}
                  {aba === "visitas" ? <AbaVisitas metricas={metricas} cores={cores} agora={agora} irPara={setAba} /> : null}
                  {aba === "aparelhos" ? (
                    <AbaAparelhos metricas={metricas} cores={cores} agora={agora} podeMarcar={podeMarcarEquipe} aoMarcar={marcarEquipe} />
                  ) : null}
                  {aba === "chance" ? <AbaChance metricas={metricas} /> : null}
                </div>
              )}
            </div>

            <AnimatePresence>
              {aviso ? (
                <motion.div
                  key={aviso}
                  initial={parado ? false : { y: 8, opacity: 0 }}
                  animate={{ y: 0, opacity: 1 }}
                  exit={{ opacity: 0 }}
                  className="border-t border-[var(--ks-color-border-subtle)] bg-[var(--ks-color-bg-inset)] px-5 py-2.5 text-center text-[13px] font-medium text-[var(--ks-color-text-secondary)] sm:px-7"
                  role="status"
                >
                  {aviso}
                </motion.div>
              ) : null}
            </AnimatePresence>
          </motion.div>
        </motion.div>
      ) : null}
    </AnimatePresence>,
    document.body
  );
}
