import type { AmbienteFiscal } from "@/lib/fiscal/ambiente";
import {
  ambienteDaNota,
  emissorParaNota,
  exigenciasDaCidade,
  lerEndereco,
  lerNfce,
  lerNfse,
  modeloAtivo,
  nfseNoPadraoNacional,
  problemaNaIdentificacaoDoCsc,
  problemaNoCsc,
} from "@/lib/fiscal/emissor";
import {
  montarNotaDeProduto,
  montarNotaDeServico,
  pendenciasDaNotaDeProduto,
  pendenciasDaNotaDeServico,
  planejarNotas,
  soDigitos,
  temBloqueio,
  type DestinatarioDaNota,
  type PedidoDaNota,
  type PerfilFiscal,
} from "@/lib/fiscal/montar";
import type { ModeloFiscal } from "@/lib/spedy/api";

import { carregarEmissor, segredosDoEmissor, type Admin } from "./base";
import { garantirNumeracao } from "./emissor";
import { enviarNota, ErroFiscal, limiteDeTeste, type NotaGuardada } from "./notas";

/**
 * A nota de teste do assistente: R$ 1,00, sem valor fiscal, emitida em
 * homologação (ou simulação, na NFS-e) para provar que empresa, certificado e
 * tributação conversam com a SEFAZ e a prefeitura. Nunca gasta nota do pacote e
 * nunca sai em produção.
 */
export async function emitirNotaDeTeste(
  admin: Admin,
  input: { empresaId: string; ambiente: AmbienteFiscal; modelo: ModeloFiscal; usuarioId: string | null }
): Promise<NotaGuardada> {
  const emissor = await carregarEmissor(admin, input.empresaId, input.ambiente);
  if (!emissor?.spedy_empresa_id || !emissor.chave_cifrada) throw new ErroFiscal("Cadastre a empresa antes do teste.", 409);
  if (emissor.status === "suspenso") throw new ErroFiscal("A emissão desta empresa está suspensa. Fale com o suporte da Ktalogu.", 403);
  if (!emissor.certificado_validade) throw new ErroFiscal("Envie o certificado digital antes do teste.", 409);
  if (!modeloAtivo(emissor, input.modelo)) throw new ErroFiscal("Ligue este tipo de nota antes do teste.", 409);
  const ambienteNota = ambienteDaNota(emissor, input.modelo);
  if (ambienteNota === "producao") {
    throw new ErroFiscal("A nota de teste só sai enquanto este tipo de nota está em modo de teste.", 409);
  }

  if (input.modelo === "nfce") {
    const nfce = lerNfce(emissor.nfce);
    const segredos = segredosDoEmissor(emissor);
    const problema =
      !nfce.tokenIdHomologacao || !segredos.cscHomologacao
        ? "Preencha a identificação e o CSC de teste."
        : problemaNaIdentificacaoDoCsc(nfce.tokenIdHomologacao, "identificação do CSC de teste") ?? problemaNoCsc(segredos.cscHomologacao, "CSC de teste guardado");
    if (problema) throw new ErroFiscal(`${problema} Corrija no passo Notas e impostos e teste de novo.`, 409);
  }

  /* O mesmo teto das notas em homologação que saem por pedido: teste também
     custa (na Spedy), e sem teto o assistente virava uma torneira aberta. */
  const { usadas, limite } = await limiteDeTeste(admin, input.empresaId, input.ambiente);
  if (usadas + 1 > limite) {
    throw new ErroFiscal(`Este mês já saíram ${usadas} notas de teste (limite ${limite}). O teste volta no mês que vem — ou ligue a emissão de verdade.`, 429);
  }

  try {
    await garantirNumeracao(emissor, input.modelo);
  } catch (erro) {
    console.error("[fiscal] numeração não conferida antes do teste", emissor.id, erro instanceof Error ? erro.message : erro);
    throw new ErroFiscal("Não foi possível preparar a numeração da nota agora. Tente de novo em instantes.", 503);
  }

  const tipo = input.modelo === "nfse" ? "servico" : "mercadoria";
  const [perfisR, produtoR] = await Promise.all([
    admin.from("fiscal_perfis").select("id,nome,tipo,padrao,ativo,regras").eq("empresa_id", input.empresaId).eq("ativo", true),
    admin.from("products").select("ncm").eq("user_id", input.empresaId).not("ncm", "is", null).limit(20),
  ]);
  const perfis = ((perfisR.data ?? []) as PerfilFiscal[]).map((p) => ({ ...p, ativo: true }));
  const padrao = perfis.find((p) => p.tipo === tipo && p.padrao) ?? perfis.find((p) => p.tipo === tipo);
  if (!padrao) {
    throw new ErroFiscal(tipo === "servico" ? "Crie a tributação de serviço antes do teste." : "Crie a tributação de produto antes do teste.", 409);
  }
  const ncm = ((produtoR.data ?? []) as Array<{ ncm: string | null }>).map((p) => soDigitos(p.ncm)).find((n) => n.length === 8) ?? "49119900";

  const pedido: PedidoDaNota = {
    id: "teste",
    numero: null,
    total: 1,
    criadoEm: new Date().toISOString(),
    entregaEm: null,
    itens: [
      {
        id: "teste",
        titulo: "Nota de teste do Ktalogu",
        produtoId: null,
        sku: "TESTE",
        codigoDeBarras: null,
        unitType: "unit",
        quantidade: 1,
        larguraM: null,
        alturaM: null,
        comprimentoM: null,
        valorTotal: 1,
        opcoes: [],
        fiscalTipo: tipo === "servico" ? "servico" : "mercadoria",
        ncm,
        cest: null,
        origem: null,
        perfilId: padrao.id,
      },
    ],
    freteDoOrcamento: 0,
    descontoDoOrcamento: 0,
    temEntrega: false,
    pagamentos: [{ metodo: "cash", valor: 1 }],
    condicoes: null,
  };

  const emissorDaNota = emissorParaNota(emissor);
  const plano = planejarNotas(pedido, { perfis, servicosDoCatalogo: [] });
  const endereco = lerEndereco(emissor.endereco);
  const destinatario: DestinatarioDaNota | null =
    input.modelo === "nfe"
      ? {
          tipo: "pj",
          nome: emissor.razao_social,
          documento: soDigitos(emissor.cnpj),
          email: null,
          telefone: null,
          /* A nota de teste vai para a própria empresa. CNPJ com inscrição estadual ativa
             precisa dela na nota (a SEFAZ recusa com "IE do destinatário não informada"). */
          inscricaoEstadual: soDigitos(emissor.inscricao_estadual) || null,
          contribuinte: soDigitos(emissor.inscricao_estadual) ? "contribuinte" : "nao_contribuinte",
          endereco,
        }
      : input.modelo === "nfse"
        ? { tipo: "pf", nome: "Tomador de teste", documento: null, email: null, telefone: null, inscricaoEstadual: null, contribuinte: null, endereco: null }
        : null;

  const id = crypto.randomUUID();
  let montada;
  let grupo = "";
  if (input.modelo === "nfse") {
    const grupoDeServico = plano.servicos[0];
    const nfse = lerNfse(emissor.nfse);
    const exige = exigenciasDaCidade(nfse.cidade?.opcoes);
    const pendencias = pendenciasDaNotaDeServico({ emissor: emissorDaNota, destinatario, grupo: grupoDeServico, perfis, exige });
    if (temBloqueio(pendencias)) {
      throw new ErroFiscal(pendencias.filter((p) => p.nivel === "bloqueio").map((p) => [p.titulo, p.detalhe].filter(Boolean).join(": ")).join(" · "), 422, { pendencias });
    }
    grupo = padrao.id;
    montada = montarNotaDeServico({
      emissor: emissorDaNota,
      destinatario,
      grupo: grupoDeServico,
      perfil: padrao,
      opcoes: { integrationId: id, emitir: true, enviarEmail: false, numeroDoPedido: null, observacao: "Nota de teste sem valor fiscal", exigeAnexo: exige.anexoSimples, padraoNacional: nfseNoPadraoNacional(nfse) },
    });
  } else {
    const modelo = input.modelo;
    const pendencias = pendenciasDaNotaDeProduto({
      modelo,
      emissor: emissorDaNota,
      destinatario,
      linhas: plano.mercadorias,
      perfis,
      consumidorFinal: true,
      total: 100,
    });
    if (temBloqueio(pendencias)) {
      throw new ErroFiscal(pendencias.filter((p) => p.nivel === "bloqueio").map((p) => [p.titulo, p.detalhe].filter(Boolean).join(": ")).join(" · "), 422, { pendencias });
    }
    montada = montarNotaDeProduto({
      emissor: emissorDaNota,
      destinatario,
      pedido,
      linhas: plano.mercadorias,
      perfis,
      opcoes: {
        modelo,
        integrationId: id,
        emitir: true,
        homologacao: true,
        consumidorFinal: true,
        presenca: "presence",
        enviarEmail: false,
        numeroDoPedido: null,
        observacao: "Nota de teste sem valor fiscal",
      },
    });
  }

  const { error } = await admin.from("fiscal_notas").insert({
    id,
    empresa_id: input.empresaId,
    ambiente: input.ambiente,
    emissor_id: emissor.id,
    modelo: input.modelo,
    ambiente_nota: ambienteNota,
    origem: "teste",
    pedido_id: null,
    grupo,
    status: "preparando",
    valor: montada.valor / 100,
    destinatario: destinatario ? { nome: destinatario.nome, documento: destinatario.documento, email: null } : null,
    payload: montada.payload,
    conta_credito: false,
    emitida_por: input.usuarioId,
  });
  if (error) throw new ErroFiscal(`Falha ao registrar a nota de teste: ${error.message}`, 500);
  return enviarNota(admin, id, { emitir: true, ator: input.usuarioId });
}
