import { ambienteFiscal, pendenciasDoServidor, type AmbienteFiscal } from "@/lib/fiscal/ambiente";
import { lerNfce, lerPreferencias, type PreferenciasFiscais } from "@/lib/fiscal/emissor";
import type { Pendencia } from "@/lib/fiscal/montar";
import { createAdminClient } from "@/lib/supabase-server";

import { agendarTarefa, carregarEmissor, type Admin } from "./base";
import { aguardarResposta, arquivoDaNota, carregarNota, emitirDoPedido, ErroFiscal, type NotaGuardada } from "./notas";

/**
 * A NFC-e no balcão.
 *
 * Venda de balcão é rápida, e o cupom fiscal tem de sair na hora. Assim que a
 * venda fecha, a tela do cupom pede a emissão direto (sem esperar a fila) e o
 * servidor pergunta ao serviço de notas a cada instante até a SEFAZ responder
 * — a NFC-e costuma ser autorizada em poucos segundos. Se algo impedir a nota
 * (cadastro incompleto, sem saldo), a tela recebe o motivo na hora; a venda
 * continua valendo e a nota fica para emitir pela tela do pedido.
 *
 * A venda também deixa uma tarefa de reserva na fila, para a nota sair mesmo
 * se o navegador fechar antes de pedir. Ela só age se a nota ainda não existe.
 */

export type NotaNoPdv = {
  disponivel: boolean;
  modo: "perguntar" | "sempre" | "nunca";
  teste: boolean;
  motivo: string | null;
};

export async function situacaoDaNotaNoPdv(empresaId: string, admin: Admin = createAdminClient()): Promise<NotaNoPdv> {
  if (pendenciasDoServidor().length) return { disponivel: false, modo: "nunca", teste: false, motivo: "Emissão de notas indisponível no momento." };
  const emissor = await carregarEmissor(admin, empresaId, ambienteFiscal());
  if (!emissor) return { disponivel: false, modo: "nunca", teste: false, motivo: null };
  const nfce = lerNfce(emissor.nfce);
  const preferencias = lerPreferencias(emissor.preferencias);
  if (emissor.status !== "ativo" || !nfce.ativo) {
    return { disponivel: false, modo: preferencias.nfceNoPdv, teste: nfce.ambiente !== "producao", motivo: "A NFC-e não está ligada na configuração." };
  }
  return { disponivel: preferencias.nfceNoPdv !== "nunca", modo: preferencias.nfceNoPdv, teste: nfce.ambiente !== "producao", motivo: null };
}

/** Quanto a reserva da fila espera: o balcão pede a nota no primeiro segundo depois da venda. */
const RESERVA_EM_SEGUNDOS = 45;

function documentoDaNota(documento: string | null | undefined) {
  const digitos = String(documento || "").replace(/\D/g, "");
  return digitos.length === 11 || digitos.length === 14 ? digitos : null;
}

/** Marcar o cupom por escolha gasta nota do pacote: é de quem pode emitir. Com "emitir em toda venda" ligado, a escolha foi da empresa. */
export const MOTIVO_SEM_PERMISSAO_DE_CUPOM = "Só quem pode emitir notas fiscais marca o cupom por escolha própria. Peça a permissão ao responsável da empresa.";

export async function agendarNotaDaVenda(input: {
  empresaId: string;
  pedidoId: string;
  usuarioId: string | null;
  documento: string | null;
  /** Quem fechou a venda tem `fiscal.emitir`? */
  podeEmitir: boolean;
}) {
  const admin = createAdminClient();
  const situacao = await situacaoDaNotaNoPdv(input.empresaId, admin);
  if (!situacao.disponivel) return { situacao: "desligada" as const, motivo: situacao.motivo };
  if (!input.podeEmitir && situacao.modo !== "sempre") return { situacao: "desligada" as const, motivo: MOTIVO_SEM_PERMISSAO_DE_CUPOM };
  await agendarTarefa(admin, {
    ambiente: ambienteFiscal(),
    tipo: "emitir_pedido",
    empresaId: input.empresaId,
    pedidoId: input.pedidoId,
    emSegundos: RESERVA_EM_SEGUNDOS,
    dados: {
      reserva: true,
      usuarioId: input.usuarioId,
      escolhas: {
        modeloDeMercadoria: "nfce",
        presenca: "presence",
        documento: documentoDaNota(input.documento),
        somente: ["nfce"],
      },
    },
  });
  return { situacao: "enviando" as const, motivo: null };
}

export type CupomNoBalcao = {
  situacao: "desligada" | "bloqueada" | "enviada";
  motivo: string | null;
  pendencias: Pendencia[];
  nota: NotaGuardada | null;
};

async function cupomDaVenda(admin: Admin, input: { empresaId: string; ambiente: AmbienteFiscal; pedidoId: string }) {
  const { data } = await admin
    .from("fiscal_notas")
    .select("id")
    .eq("empresa_id", input.empresaId)
    .eq("ambiente", input.ambiente)
    .eq("pedido_id", input.pedidoId)
    .eq("modelo", "nfce")
    .neq("status", "removida")
    .order("created_at", { ascending: false })
    .limit(1)
    .maybeSingle();
  return (data as { id: string } | null)?.id ?? null;
}

/** O cupom fiscal da venda, na hora: emite (ou reaproveita o que já saiu) e espera a SEFAZ por alguns segundos. */
export async function emitirCupomNoBalcao(
  admin: Admin,
  input: {
    empresaId: string;
    ambiente: AmbienteFiscal;
    pedidoId: string;
    usuarioId: string | null;
    documento: string | null;
    /** Quem só tem o PDV emite o cupom das vendas do PDV; quem emite notas, de qualquer pedido. */
    podeQualquerPedido: boolean;
  }
): Promise<CupomNoBalcao> {
  const situacao = await situacaoDaNotaNoPdv(input.empresaId, admin);
  if (!situacao.disponivel) {
    return { situacao: "desligada", motivo: situacao.motivo ?? "O cupom fiscal está desligado no PDV.", pendencias: [], nota: null };
  }

  if (!input.podeQualquerPedido) {
    await garantirVendaDoPdv(admin, input.empresaId, input.pedidoId);
    /* Sem `fiscal.emitir`, o cupom só sai quando a empresa decidiu emitir em toda venda. */
    if (situacao.modo !== "sempre") throw new ErroFiscal(MOTIVO_SEM_PERMISSAO_DE_CUPOM, 403);
  }

  const resultado = await emitirDoPedido(admin, {
    empresaId: input.empresaId,
    ambiente: input.ambiente,
    pedidoId: input.pedidoId,
    usuarioId: input.usuarioId,
    escolhas: { modeloDeMercadoria: "nfce", presenca: "presence", documento: documentoDaNota(input.documento), somente: ["nfce"] },
  });

  let notaId: string | null = resultado.notas[0]?.id ?? null;
  if (!notaId) {
    const bloqueios = resultado.pendencias.filter((p) => p.nivel === "bloqueio");
    if (bloqueios.length) {
      return { situacao: "bloqueada", motivo: bloqueios.map((p) => p.titulo).join(" · "), pendencias: bloqueios, nota: null };
    }
    /* Nada a emitir sem bloqueio: o cupom desta venda já saiu (clique repetido ou a reserva da fila). */
    notaId = await cupomDaVenda(admin, input);
  }
  if (!notaId) {
    return { situacao: "bloqueada", motivo: "Esta venda não tem produto para cupom fiscal.", pendencias: [], nota: null };
  }

  const nota = await aguardarResposta(admin, notaId, { ateMs: 12_000, intervaloMs: 800 });
  return { situacao: "enviada", motivo: null, pendencias: [], nota };
}

/**
 * Quem só tem o PDV mexe só nas vendas do PDV. A marca "origem:pdv" fica no
 * orçamento da venda; o pedido leva "Venda rápida PDV" (o mesmo critério que
 * a nota usa para saber que a venda é de balcão).
 */
async function garantirVendaDoPdv(admin: Admin, empresaId: string, pedidoId: string) {
  const { data: pedido } = await admin
    .from("orders")
    .select("internal_notes,quotes(title,internal_notes)")
    .eq("id", pedidoId)
    .eq("user_id", empresaId)
    .maybeSingle();
  if (!pedido) throw new ErroFiscal("Pedido não encontrado.", 404);
  const linha = pedido as { internal_notes?: string | null; quotes?: unknown };
  const orcamentos = (Array.isArray(linha.quotes) ? linha.quotes : [linha.quotes]) as Array<{ title?: string | null; internal_notes?: string | null } | null>;
  const textos = [linha.internal_notes, ...orcamentos.flatMap((orcamento) => [orcamento?.title, orcamento?.internal_notes])]
    .map((texto) => String(texto ?? "").toLowerCase())
    .join(" ");
  if (!["origem:pdv", "venda pdv", "venda rápida pdv"].some((marca) => textos.includes(marca))) {
    throw new ErroFiscal("Este pedido não é uma venda do PDV.", 403);
  }
}

/** Como está o cupom fiscal da venda agora; se ainda estiver a caminho, espera um pouco pela SEFAZ antes de responder. */
export async function cupomAtualDaVenda(
  admin: Admin,
  input: { empresaId: string; ambiente: AmbienteFiscal; pedidoId: string; podeQualquerPedido: boolean; esperarMs?: number }
): Promise<NotaGuardada | null> {
  if (!input.podeQualquerPedido) await garantirVendaDoPdv(admin, input.empresaId, input.pedidoId);
  const notaId = await cupomDaVenda(admin, input);
  if (!notaId) return null;
  return input.esperarMs ? aguardarResposta(admin, notaId, { ateMs: input.esperarMs, intervaloMs: 900 }) : carregarNota(admin, notaId);
}

/** O PDF do cupom fiscal da venda, para imprimir no balcão. */
export async function pdfDoCupomDaVenda(
  admin: Admin,
  input: { empresaId: string; ambiente: AmbienteFiscal; pedidoId: string; podeQualquerPedido: boolean }
) {
  if (!input.podeQualquerPedido) await garantirVendaDoPdv(admin, input.empresaId, input.pedidoId);
  const notaId = await cupomDaVenda(admin, input);
  if (!notaId) throw new ErroFiscal("Esta venda ainda não tem cupom fiscal.", 404);
  return arquivoDaNota(admin, { empresaId: input.empresaId, notaId, tipo: "pdf" });
}

/** "Emitir cupom fiscal em toda venda", ligado pelo próprio PDV. */
export async function mudarCupomEmTodaVenda(admin: Admin, input: { empresaId: string; ambiente: AmbienteFiscal; emTodaVenda: boolean }) {
  const emissor = await carregarEmissor(admin, input.empresaId, input.ambiente);
  if (!emissor) throw new ErroFiscal("Configure as notas fiscais primeiro.", 409);
  const preferencias: PreferenciasFiscais = { ...lerPreferencias(emissor.preferencias), nfceNoPdv: input.emTodaVenda ? "sempre" : "perguntar" };
  const { error } = await admin
    .from("fiscal_emissores")
    .update({ preferencias, updated_at: new Date().toISOString() })
    .eq("id", emissor.id);
  if (error) throw new ErroFiscal(`Falha ao salvar: ${error.message}`, 500);
  return situacaoDaNotaNoPdv(input.empresaId, admin);
}
