"use client";

import { useCallback, useEffect, useRef, useState } from "react";
import { AlertTriangle, CheckCircle2, ExternalLink, Loader2, Printer, ReceiptText, RotateCcw } from "lucide-react";

import type { Pendencia } from "@/lib/fiscal/montar";
import type { NotaNaTela } from "@/lib/fiscal/servidor/apresentacao";

import { linkParaResolver } from "./comum";
import { imprimirPdf } from "./imprimir-pdf";

/**
 * O cupom fiscal (NFC-e) das vendas do balcão.
 *
 * Mora na página do PDV, e não na janela da venda: se o caixa voltar ao balcão
 * antes de a SEFAZ responder, a nota continua sendo acompanhada e, com a
 * impressão automática ligada, sai na impressora do mesmo jeito.
 */

export type FaseDoCupom = "emitindo" | "aguardando" | "autorizada" | "recusada" | "bloqueada" | "demorando" | "falhou";

export type EstadoDoCupom = {
  pedidoId: string;
  numeroDoPedido: number | null;
  documento: string | null;
  fase: FaseDoCupom;
  nota: NotaNaTela | null;
  motivo: string | null;
  pendencias: Pendencia[];
  inicio: number;
  imprimindo: boolean;
  erroDeImpressao: string | null;
};

type RespostaDaEmissao = {
  situacao?: "desligada" | "bloqueada" | "enviada";
  motivo?: string | null;
  pendencias?: Pendencia[];
  nota?: NotaNaTela | null;
  error?: string;
};

const esperar = (ms: number) => new Promise((resolver) => setTimeout(resolver, ms));
const FINAIS: FaseDoCupom[] = ["autorizada", "recusada", "bloqueada", "demorando", "falhou"];

export function useCupomNoBalcao(opcoes: {
  imprimirSozinho: boolean;
  /** O pedido cuja janela está aberta: o que termina fora dela vira aviso no balcão. */
  pedidoNaTela: string | null;
  avisar: (texto: string, tom: "ok" | "erro") => void;
}) {
  const [cupons, setCupons] = useState<Record<string, EstadoDoCupom>>({});
  const atual = useRef(opcoes);
  const impressas = useRef(new Set<string>());
  const acompanhando = useRef(new Set<string>());

  useEffect(() => {
    atual.current = opcoes;
  });

  const mudar = useCallback((pedidoId: string, parcial: Partial<EstadoDoCupom>) => {
    setCupons((antes) => (antes[pedidoId] ? { ...antes, [pedidoId]: { ...antes[pedidoId], ...parcial } } : antes));
  }, []);

  const avisarSeFora = useCallback((estado: Pick<EstadoDoCupom, "pedidoId" | "numeroDoPedido">, texto: string, tom: "ok" | "erro") => {
    if (atual.current.pedidoNaTela === estado.pedidoId) return;
    const venda = estado.numeroDoPedido ? `do pedido #${estado.numeroDoPedido}` : "da venda";
    atual.current.avisar(`Cupom fiscal ${venda}: ${texto}`, tom);
  }, []);

  const imprimir = useCallback(
    async (pedidoId: string) => {
      mudar(pedidoId, { imprimindo: true, erroDeImpressao: null });
      try {
        await imprimirPdf(`/api/fiscal/pdv/cupom?pedidoId=${encodeURIComponent(pedidoId)}`);
      } catch (erro) {
        mudar(pedidoId, { erroDeImpressao: erro instanceof Error ? erro.message : "Não foi possível imprimir." });
      } finally {
        mudar(pedidoId, { imprimindo: false });
      }
    },
    [mudar]
  );

  /** Aplica a nota que chegou. Devolve true quando a SEFAZ já respondeu. */
  const receberNota = useCallback(
    (estado: Pick<EstadoDoCupom, "pedidoId" | "numeroDoPedido">, nota: NotaNaTela) => {
      const autorizada = nota.status === "autorizada" || nota.status === "contingencia";
      const recusada = ["rejeitada", "erro", "denegada"].includes(nota.status);
      mudar(estado.pedidoId, { nota, fase: autorizada ? "autorizada" : recusada ? "recusada" : "aguardando" });
      if (autorizada) {
        if (atual.current.imprimirSozinho && !impressas.current.has(nota.id)) {
          impressas.current.add(nota.id);
          void imprimir(estado.pedidoId);
        }
        avisarSeFora(estado, `nº ${nota.numero ?? ""} autorizado${atual.current.imprimirSozinho ? " e enviado para a impressora" : ""}.`, "ok");
      } else if (recusada) {
        avisarSeFora(estado, `não saiu (${nota.explicacao?.titulo ?? "recusado"}). Abra o pedido para corrigir.`, "erro");
      }
      return autorizada || recusada;
    },
    [avisarSeFora, imprimir, mudar]
  );

  const acompanhar = useCallback(
    async (estado: Pick<EstadoDoCupom, "pedidoId" | "numeroDoPedido">) => {
      if (acompanhando.current.has(estado.pedidoId)) return;
      acompanhando.current.add(estado.pedidoId);
      mudar(estado.pedidoId, { fase: "aguardando" });
      try {
        const limite = Date.now() + 60_000;
        while (Date.now() < limite) {
          try {
            /* O servidor espera até 5 s pela SEFAZ antes de responder: não precisa de pausa longa aqui. */
            const resposta = await fetch(`/api/fiscal/pdv/emitir?pedidoId=${encodeURIComponent(estado.pedidoId)}`, { cache: "no-store" });
            const corpo = (await resposta.json().catch(() => null)) as { nota?: NotaNaTela | null } | null;
            if (resposta.ok && corpo?.nota && receberNota(estado, corpo.nota)) return;
          } catch {
            /* sem rede agora: tenta de novo */
          }
          await esperar(700);
        }
        mudar(estado.pedidoId, { fase: "demorando" });
      } finally {
        acompanhando.current.delete(estado.pedidoId);
      }
    },
    [mudar, receberNota]
  );

  const iniciar = useCallback(
    async (pedidoId: string, dados: { documento: string | null; numeroDoPedido: number | null }) => {
      const estado = { pedidoId, numeroDoPedido: dados.numeroDoPedido };
      setCupons((antes) => {
        /* Guarda só as vendas recentes: o balcão não precisa do dia inteiro na memória. */
        const recentes = Object.fromEntries(Object.entries(antes).filter(([, item]) => Date.now() - item.inicio < 30 * 60_000));
        return {
          ...recentes,
          [pedidoId]: {
            pedidoId,
            numeroDoPedido: dados.numeroDoPedido,
            documento: dados.documento,
            fase: "emitindo",
            nota: null,
            motivo: null,
            pendencias: [],
            inicio: Date.now(),
            imprimindo: false,
            erroDeImpressao: null,
          },
        };
      });
      try {
        const resposta = await fetch("/api/fiscal/pdv/emitir", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ pedidoId, documento: dados.documento }),
          cache: "no-store",
        });
        const corpo = (await resposta.json().catch(() => null)) as RespostaDaEmissao | null;
        if (!resposta.ok || !corpo) {
          const motivo = corpo?.error || "Não foi possível emitir o cupom fiscal agora.";
          mudar(pedidoId, { fase: "falhou", motivo });
          avisarSeFora(estado, motivo, "erro");
          return;
        }
        if (corpo.situacao !== "enviada" || !corpo.nota) {
          const motivo = corpo.motivo || "O cupom fiscal não pôde sair.";
          mudar(pedidoId, { fase: "bloqueada", motivo, pendencias: corpo.pendencias ?? [] });
          avisarSeFora(estado, `não saiu: ${motivo}`, "erro");
          return;
        }
        if (!receberNota(estado, corpo.nota)) void acompanhar(estado);
      } catch {
        mudar(pedidoId, { fase: "falhou", motivo: "Sem conexão com o servidor agora. O cupom sai sozinho em instantes; confira no pedido." });
      }
    },
    [acompanhar, avisarSeFora, mudar, receberNota]
  );

  const tentarDeNovo = useCallback(
    (pedidoId: string) => {
      const estado = cupons[pedidoId];
      if (estado) void iniciar(pedidoId, { documento: estado.documento, numeroDoPedido: estado.numeroDoPedido });
    },
    [cupons, iniciar]
  );

  const consultarDeNovo = useCallback(
    (pedidoId: string) => {
      const estado = cupons[pedidoId];
      if (estado) void acompanhar(estado);
    },
    [acompanhar, cupons]
  );

  return { cupons, iniciar, imprimir, tentarDeNovo, consultarDeNovo };
}

function useSegundos(inicio: number, correndo: boolean) {
  const [agora, setAgora] = useState(() => Date.now());
  useEffect(() => {
    if (!correndo) return;
    const relogio = setInterval(() => setAgora(Date.now()), 1000);
    return () => clearInterval(relogio);
  }, [correndo]);
  return Math.max(0, Math.round((agora - inicio) / 1000));
}

/** O bloco do cupom fiscal na janela da venda. */
export function CupomFiscalDaVenda({
  estado,
  aoImprimir,
  aoTentarDeNovo,
  aoConsultarDeNovo,
}: {
  estado: EstadoDoCupom;
  aoImprimir: () => void;
  aoTentarDeNovo: () => void;
  aoConsultarDeNovo: () => void;
}) {
  const correndo = !FINAIS.includes(estado.fase);
  const segundos = useSegundos(estado.inicio, correndo);
  const { nota } = estado;
  const teste = nota?.semValorFiscal ? " (teste)" : "";

  if (estado.fase === "autorizada" && nota) {
    return (
      <div className="grid gap-2">
        <div className="pdv-mp pdv-mp-ligado">
          <CheckCircle2 size={18} className="shrink-0 text-[var(--ks-color-status-success)]" />
          <div className="pdv-mp-texto">
            <strong>
              Cupom fiscal{nota.numero ? ` nº ${nota.numero}` : ""} {nota.status === "contingencia" ? "emitido em contingência" : "autorizado"}
              {teste}
            </strong>
            <span>
              {estado.imprimindo
                ? "Mandando para a impressora…"
                : estado.erroDeImpressao
                  ? estado.erroDeImpressao
                  : "Pronto para entregar ao cliente."}
            </span>
          </div>
        </div>
        <button type="button" className="pdv-botao pdv-botao-cheio pdv-botao-largo" onClick={aoImprimir} disabled={estado.imprimindo}>
          {estado.imprimindo ? <Loader2 size={15} className="pdv-girando" /> : <Printer size={15} />}
          Imprimir cupom fiscal
        </button>
      </div>
    );
  }

  if (estado.fase === "recusada" && nota) {
    return (
      <div className="pdv-mp">
        <AlertTriangle size={18} className="shrink-0 text-[var(--ks-color-status-danger)]" />
        <div className="pdv-mp-texto">
          <strong>{nota.explicacao?.titulo ?? "O cupom fiscal foi recusado"}{teste}</strong>
          <span>{nota.explicacao?.dica ?? nota.mensagem ?? "Abra o pedido para ver o motivo."}</span>
        </div>
        <button type="button" className="pdv-botao" onClick={aoTentarDeNovo}>
          <RotateCcw size={14} />
          Tentar de novo
        </button>
      </div>
    );
  }

  if (estado.fase === "bloqueada") {
    return (
      <div className="pdv-mp">
        <AlertTriangle size={18} className="shrink-0 self-start text-[var(--ks-color-status-danger)]" />
        <div className="pdv-mp-texto">
          <strong>O cupom fiscal não saiu</strong>
          <span>A venda está registrada. Corrija o que falta e emita pela tela do pedido.</span>
          {estado.pendencias.length ? (
            <ul className="mt-1.5 grid gap-1">
              {estado.pendencias.slice(0, 4).map((pendencia) => {
                const link = linkParaResolver(pendencia, { pedidoId: estado.pedidoId });
                return (
                  <li key={`${pendencia.codigo}:${pendencia.alvoId ?? ""}`} className="flex items-start justify-between gap-2 text-[11.5px]">
                    <span className="min-w-0 font-semibold text-[var(--ks-color-text-primary)]">
                      {pendencia.titulo}
                      {pendencia.detalhe ? <span className="block font-normal text-[var(--ks-color-text-soft)]">{pendencia.detalhe}</span> : null}
                    </span>
                    {link ? (
                      <a href={link} target="_blank" rel="noopener noreferrer" className="inline-flex shrink-0 items-center gap-1 font-bold text-[var(--ks-color-primary)] hover:underline">
                        Corrigir
                        <ExternalLink size={11} />
                      </a>
                    ) : null}
                  </li>
                );
              })}
            </ul>
          ) : estado.motivo ? (
            <span>{estado.motivo}</span>
          ) : null}
        </div>
      </div>
    );
  }

  if (estado.fase === "demorando") {
    return (
      <div className="pdv-mp">
        <ReceiptText size={18} className="shrink-0 text-[var(--ks-color-text-soft)]" />
        <div className="pdv-mp-texto">
          <strong>A SEFAZ está demorando</strong>
          <span>O cupom continua sendo emitido e aparece no pedido quando sair.</span>
        </div>
        <button type="button" className="pdv-botao" onClick={aoConsultarDeNovo}>
          <RotateCcw size={14} />
          Consultar
        </button>
      </div>
    );
  }

  if (estado.fase === "falhou") {
    return (
      <div className="pdv-mp">
        <AlertTriangle size={18} className="shrink-0 text-[var(--ks-color-status-danger)]" />
        <div className="pdv-mp-texto">
          <strong>Não deu para emitir agora</strong>
          <span>{estado.motivo}</span>
        </div>
        <button type="button" className="pdv-botao" onClick={aoTentarDeNovo}>
          <RotateCcw size={14} />
          Tentar de novo
        </button>
      </div>
    );
  }

  return (
    <div className="pdv-mp pdv-mp-ligado">
      <Loader2 size={18} className="pdv-girando shrink-0 text-[var(--ks-color-primary)]" />
      <div className="pdv-mp-texto">
        <strong>
          {estado.fase === "emitindo" ? "Emitindo o cupom fiscal…" : "Esperando a SEFAZ…"}
          {segundos > 0 ? ` ${segundos} s` : ""}
        </strong>
        <span>Leva poucos segundos. Pode voltar ao balcão: o cupom sai assim mesmo.</span>
      </div>
    </div>
  );
}
