/* eslint-disable @typescript-eslint/no-explicit-any -- os testes leem o payload da nota em profundidade */
import assert from "node:assert/strict";
import { describe, it } from "node:test";

import { montarNotaDeProduto, montarPagamentos, pendenciasDaNotaDeProduto, planejarNotas, type EmissorDaNota, type PerfilFiscal } from "./montar.ts";
import {
  ErroNaNotaAvulsa,
  formularioDaNota,
  itensDaNotaAvulsa,
  lerNotaAvulsa,
  pedidoDaNotaAvulsa,
  pendenciaNaLinha,
  type ProdutoDaNotaAvulsa,
} from "./nota-avulsa.ts";
import { MODELOS_DE_MERCADORIA, MODELOS_DE_SERVICO } from "./regras.ts";

const CLIENTE = "6f1f0a38-1f5e-4d6a-9d7e-2b1f5c3a9e10";
const PRODUTO = "0b7c3d52-8a41-4e8f-bf0d-6a2c9d1e7f33";
const CHAVE = "a1b2c3d4-e5f6-4789-8abc-def012345678";

const emissor: EmissorDaNota = {
  cnpj: "11222333000181",
  razaoSocial: "Gráfica Teste Ltda",
  regime: "simplesNacional",
  uf: "PE",
  cidadeIbge: "2611606",
  inscricaoEstadual: "123456789",
  inscricaoMunicipal: "998877",
};

const perfilProducao: PerfilFiscal = {
  id: "perfil-producao",
  nome: "Produção própria",
  tipo: "mercadoria",
  padrao: true,
  ativo: true,
  regras: MODELOS_DE_MERCADORIA.find((m) => m.chave === "producao-simples")!.regras,
};

const perfilServico: PerfilFiscal = {
  id: "perfil-servico",
  nome: "Serviço gráfico",
  tipo: "servico",
  padrao: true,
  ativo: true,
  regras: { ...MODELOS_DE_SERVICO[0].regras, aliquotaIss: 2.5 },
};

const perfis = [perfilProducao, perfilServico];

const catalogo = new Map<string, ProdutoDaNotaAvulsa>([
  [PRODUTO, { id: PRODUTO, sku: "CV-500", barcode: null, fiscal_tipo: "mercadoria", ncm: "4911.10.90", cest: null, fiscal_origem: 0, fiscal_perfil_id: null }],
]);

describe("lerNotaAvulsa", () => {
  it("sem tipo de nota não dá para ler", () => {
    assert.throws(() => lerNotaAvulsa({ itens: [] }), ErroNaNotaAvulsa);
  });

  it("linha incompleta vira pendência apontando a linha, sem derrubar a leitura", () => {
    const { dados, problemas } = lerNotaAvulsa({
      modelo: "nfe",
      itens: [
        { descricao: "Banner", quantidade: "2", valorUnitario: "35,5" },
        { descricao: "", quantidade: 1, valorUnitario: 10 },
        { descricao: "Adesivo", quantidade: 0, valorUnitario: 10 },
        { descricao: "Placa", quantidade: 1, valorUnitario: 0 },
      ],
    });
    assert.deepEqual(
      problemas.map((p) => [p.codigo, p.alvoId, p.titulo]),
      [
        ["avulsa.item.descricao:1", "1", "Linha 2: escreva o que foi vendido"],
        ["avulsa.item.quantidade:2", "2", "Linha 3: informe a quantidade"],
        ["avulsa.item.valor:3", "3", "Linha 4: informe o valor"],
      ]
    );
    assert.ok(problemas.every((p) => p.nivel === "bloqueio"));
    /* A linha sem quantidade sai da conta; as outras guardam a posição da tela. */
    assert.deepEqual(
      dados.itens.map((i) => [i.posicao, i.descricao, i.quantidade, i.valorUnitario]),
      [
        [0, "Banner", 2, 35.5],
        [1, "Item 2", 1, 10],
        [3, "Placa", 1, 0],
      ]
    );
  });

  it("limpa unidade, NCM, desconto, frete, vencimento e documento", () => {
    const { dados } = lerNotaAvulsa({
      chave: CHAVE,
      modelo: "nfce",
      clienteId: CLIENTE,
      documento: "111.444.777-35",
      itens: [{ descricao: "  Cartão   de visita ", quantidade: 1000, valorUnitario: 0.08, unidade: "m-il", ncm: "4911.10.90.99" }],
      desconto: -5,
      frete: 12,
      pagamento: "credito",
      vencimento: "2026-10-01",
      presenca: "qualquer",
    });
    assert.equal(dados.chave, CHAVE);
    assert.equal(dados.documento, null, "com cliente escolhido, o documento é o do cadastro");
    assert.deepEqual(dados.itens[0], { posicao: 0, produtoId: null, descricao: "Cartão de visita", ncm: "49111090", unidade: "MIL", quantidade: 1000, valorUnitario: 0.08 });
    assert.equal(dados.desconto, 0);
    assert.equal(dados.frete, 0, "frete só vai na nota de produto");
    assert.equal(dados.vencimento, null, "pago na hora não tem vencimento");
    assert.equal(dados.presenca, null);
  });

  it("forma de pagamento desconhecida vira Pix e chave inválida é ignorada", () => {
    const { dados } = lerNotaAvulsa({ modelo: "nfe", chave: "123", pagamento: "bitcoin", itens: [] });
    assert.equal(dados.pagamento, "pix");
    assert.equal(dados.chave, null);
  });

  it("o formulário guardado na nota volta igual", () => {
    const { dados } = lerNotaAvulsa({
      modelo: "nfe",
      clienteId: CLIENTE,
      itens: [
        { produtoId: PRODUTO, descricao: "Cartão de visita", quantidade: 500, valorUnitario: 0.12, unidade: "UN", ncm: "49111090" },
        { descricao: "Banner lona", quantidade: 3, valorUnitario: 45.5, unidade: "M2", ncm: "39199090" },
      ],
      desconto: 10,
      frete: 20,
      pagamento: "boleto",
      vencimento: "2026-10-17",
      observacao: "Entrega combinada",
      consumidorFinal: false,
      presenca: "telephone",
      enviarEmail: true,
    });
    const guardado = JSON.parse(JSON.stringify(formularioDaNota(dados)));
    const lido = lerNotaAvulsa({ ...guardado, modelo: "nfe", chave: CHAVE });
    assert.deepEqual(lido.problemas, []);
    assert.deepEqual({ ...lido.dados, chave: null }, dados);
  });
});

describe("nota de produto sem pedido", () => {
  const agora = new Date("2026-09-17T10:00:00-03:00");

  function montar(entrada: Record<string, unknown>) {
    const { dados, problemas } = lerNotaAvulsa(entrada);
    assert.deepEqual(problemas, []);
    const itens = itensDaNotaAvulsa(dados, catalogo);
    const pedido = pedidoDaNotaAvulsa(dados, itens, agora);
    const plano = planejarNotas(pedido, { perfis, servicosDoCatalogo: [] });
    return { dados, itens, pedido, plano };
  }

  it("NF-e a receber em boleto: unidade digitada, desconto, frete e o vencimento informado", () => {
    const { pedido, plano } = montar({
      modelo: "nfe",
      itens: [
        { produtoId: PRODUTO, descricao: "Cartão de visita 4x0", quantidade: 1000, valorUnitario: 0.09, unidade: "UN", ncm: "" },
        { descricao: "Banner lona 440g", quantidade: 3, valorUnitario: 45.5, unidade: "m2", ncm: "39199090" },
      ],
      desconto: 16.5,
      frete: 25,
      pagamento: "boleto",
      vencimento: "2026-10-17",
    });
    assert.equal(pedido.total, 235);
    assert.equal(pedido.numero, null);
    assert.deepEqual(pedido.pagamentos, []);
    assert.equal(plano.inconsistente, false);
    assert.equal(plano.desconto, 1650);
    assert.equal(plano.frete, 2500);

    const [cartao, banner] = plano.mercadorias;
    assert.equal(cartao.ncm, "49111090", "sem NCM na linha, vale o do cadastro do produto");
    assert.equal(cartao.codigo, "CV-500");
    assert.equal(cartao.unidade, "UN");
    assert.equal(cartao.quantidade, 1000);
    assert.equal(banner.unidade, "M2");
    assert.equal(banner.quantidade, 3);
    assert.equal(banner.valor, 13650);

    const nota = montarNotaDeProduto({
      emissor,
      destinatario: {
        tipo: "pj",
        nome: "Loja Cliente Ltda",
        documento: "11444777000161",
        email: null,
        telefone: null,
        inscricaoEstadual: null,
        contribuinte: "nao_contribuinte",
        endereco: { cep: "50000000", logradouro: "Rua A", numero: "1", complemento: null, bairro: "Centro", cidade: "Recife", uf: "PE", ibge: "2611606" },
      },
      pedido,
      linhas: plano.mercadorias,
      perfis,
      opcoes: {
        modelo: "nfe",
        integrationId: CHAVE,
        emitir: true,
        homologacao: false,
        consumidorFinal: true,
        presenca: "presence",
        enviarEmail: false,
        numeroDoPedido: pedido.numero,
        agora,
      },
    });
    assert.equal(nota.valor, 23500);
    const payload = nota.payload as Record<string, any>;
    assert.deepEqual(
      payload.items.map((item: any) => [item.unit, item.quantity]),
      [
        ["UN", 1000],
        ["M2", 3],
      ]
    );
    assert.deepEqual(payload.payments, [{ method: "billetBanking", amount: 235 }]);
    assert.deepEqual(
      payload.duplicates.map((d: any) => [d.number, String(d.dueDate).slice(0, 10), d.amount]),
      [["001", "2026-10-17", 235]]
    );
    assert.deepEqual(payload.billing, { number: "1", originalAmount: 235, discountAmount: 0, netAmount: 235 });
    assert.doesNotMatch(String(payload.additionalInformation ?? ""), /Pedido/);
  });

  /* Vencimento que já passou (ou que é hoje) não é venda a prazo: a SEFAZ
     recusa duplicata vencendo no dia da emissão (rejeição 853). A nota sai à
     vista, só com o meio de pagamento. */
  it("a prazo com vencimento no passado sai à vista, como duplicata comercial e sem duplicata", () => {
    const { pedido } = montar({
      modelo: "nfe",
      itens: [{ descricao: "Adesivo vinil", quantidade: 10, valorUnitario: 5, unidade: "UN", ncm: "39199090" }],
      pagamento: "a_prazo",
      vencimento: "2026-09-01",
    });
    const r = montarPagamentos(pedido, 5000, { modelo: "nfe", agora });
    assert.deepEqual(r.payments, [{ method: "commercialDuplicate", amount: 50 }]);
    assert.deepEqual(r.duplicates, []);
    assert.equal(r.billing, null);
  });

  it("cupom pago no cartão não deixa nada em aberto", () => {
    const { pedido } = montar({
      modelo: "nfce",
      itens: [{ descricao: "Caneca personalizada", quantidade: 2, valorUnitario: 39.9, unidade: "UN", ncm: "69120000" }],
      pagamento: "debito",
    });
    const r = montarPagamentos(pedido, 7980, { modelo: "nfce", agora });
    assert.deepEqual(r.payments, [{ method: "debitCard", amount: 79.8, card: { isIntegratedPayment: false } }]);
    assert.equal(r.emAberto, 0);
  });

  it("serviço sai numa nota só, na tributação escolhida", () => {
    const { plano } = montar({
      modelo: "nfse",
      itens: [
        { descricao: "Criação de arte", quantidade: 1, valorUnitario: 150 },
        { produtoId: PRODUTO, descricao: "Instalação", quantidade: 2, valorUnitario: 80, unidade: "M2", ncm: "49111090" },
      ],
      desconto: 10,
    });
    assert.equal(plano.mercadorias.length, 0);
    assert.equal(plano.servicos.length, 1);
    assert.equal(plano.servicos[0].perfilId, "perfil-servico");
    assert.deepEqual(
      plano.servicos[0].linhas.map((l) => [l.unidade, l.quantidade, l.ncm]),
      [
        ["UN", 1, null],
        ["UN", 2, null],
      ]
    );
  });

  it("a falta de NCM aponta a linha da tela", () => {
    const { dados, plano } = montar({
      modelo: "nfe",
      itens: [
        { descricao: "Banner", quantidade: 1, valorUnitario: 50, unidade: "UN", ncm: "39199090" },
        { descricao: "Faixa", quantidade: 1, valorUnitario: 60, unidade: "UN", ncm: "3919" },
      ],
    });
    const pendencias = pendenciasDaNotaDeProduto({
      modelo: "nfe",
      emissor,
      destinatario: null,
      linhas: plano.mercadorias,
      perfis,
      consumidorFinal: true,
      total: 11000,
    }).map(pendenciaNaLinha(dados));
    const ncm = pendencias.find((p) => p.codigo.startsWith("produto.ncm"));
    assert.ok(ncm);
    assert.equal(ncm.onde, "pedido");
    assert.equal(ncm.alvoId, "1");
    assert.equal(ncm.titulo, "Linha 2: o NCM tem 8 números");
  });
});
