import { test } from "node:test";
import assert from "node:assert/strict";

import {
  aplicarMudancasNaEmpresa,
  aplicarRegras,
  cnpjDoTitular,
  completarComCadastro,
  faltasDaEmpresa,
  montarProximosPassos,
  regrasParaLer,
  semSegredos,
  sugerirRegime,
  vazioDeEmpresa,
  type EmpresaFiscal,
  type EstadoParaPassos,
} from "./ferramentas/configuracao-fiscal-regras.ts";
import { lerRegrasDeMercadoria, lerRegrasDeServico, MODELOS_DE_MERCADORIA, SEM_REGRAS_DE_SERVICO } from "../fiscal/regras.ts";

function empresa(): EmpresaFiscal {
  return {
    cnpj: "11222333000181",
    razaoSocial: "Gráfica Aurora LTDA",
    nomeFantasia: "Aurora",
    inscricaoEstadual: "110042490114",
    inscricaoMunicipal: "12345",
    regime: "simplesNacional",
    regimeEspecial: null,
    regimeSimplesNfse: null,
    cnaes: [{ codigo: "1813001", principal: true }],
    email: "contato@aurora.com.br",
    telefone: "11999990000",
    endereco: { cep: "01310100", logradouro: "Av. Paulista", numero: "1000", complemento: null, bairro: "Bela Vista", cidade: "São Paulo", uf: "SP", ibge: "3550308" },
  };
}

function estado(parcial: Partial<EstadoParaPassos> = {}): EstadoParaPassos {
  return {
    servidor: "producao",
    emissaoDisponivel: true,
    suspensa: false,
    temEmissor: true,
    empresaCadastrada: true,
    faltasNaEmpresa: [],
    ultimoErro: null,
    inscricaoEstadual: true,
    inscricaoMunicipal: true,
    certificado: { enviado: true, dias: 200 },
    tipos: {
      nfe: { ativo: true, ambiente: "homologacao", testeAprovado: true },
      nfce: { ativo: false, ambiente: "homologacao", testeAprovado: false, cscDoAmbiente: false },
      nfse: { ativo: false, ambiente: "simulacao", testeAprovado: false, faltaLoginDaPrefeitura: false },
    },
    perfis: { mercadoria: { existe: true, faltas: [] }, servico: { existe: false, faltas: [] } },
    produtosSemNcm: 0,
    algumTesteAprovado: true,
    saldoDisponivel: 50,
    ...parcial,
  };
}

/* ---------------- Regime ---------------- */

test("regime: MEI e Simples com certeza; fora do Simples pergunta Presumido ou Real; sem dado, pergunta tudo", () => {
  assert.equal(sugerirRegime({ simples: true, mei: true }).regime_sugerido, "mei");
  const simples = sugerirRegime({ simples: true, mei: false });
  assert.equal(simples.regime_sugerido, "simples_nacional");
  assert.equal(simples.certeza, "alta");
  assert.match(simples.explicacao, /sublimite/);
  const normal = sugerirRegime({ simples: false, mei: false });
  assert.equal(normal.regime_sugerido, "regime_normal");
  assert.equal(normal.certeza, "media");
  assert.match(normal.pergunta_para_a_pessoa, /Presumido ou Lucro Real/);
  const nada = sugerirRegime({ simples: null, mei: null });
  assert.equal(nada.regime_sugerido, null);
  assert.equal(nada.certeza, "nenhuma");
  assert.equal(nada.opcoes.length, 4);
});

/* ---------------- Segredos ---------------- */

test("segredos: chave, CSC, senha e token saem; booleano de 'configurado' e chave de acesso da nota ficam", () => {
  const limpo = semSegredos({
    chave_cifrada: "abc",
    segredos_cifrados: "xyz",
    cscHomologacao: "123456789012345678",
    tokenIdHomologacao: "000001",
    senha: "s3nh4",
    nfse: { usuario: "joao", password: "p", senhaGuardada: true },
    apiKey: "k",
    csc_de_teste_configurado: true,
    chave_de_acesso: "35260111222333000181550010000000011000000010",
    mensagem: "Recusada pela Spedy",
    texto: "a1b2c3d4-0000-1111-2222-333344445555-e5f6a7b8-0000-1111-2222-333344445555",
    lista: [{ token: "t", ok: 1 }],
  });
  assert.equal("chave_cifrada" in limpo, false);
  assert.equal("segredos_cifrados" in limpo, false);
  assert.equal("cscHomologacao" in limpo, false);
  assert.equal("tokenIdHomologacao" in limpo, false);
  assert.equal("senha" in limpo, false);
  assert.equal("apiKey" in limpo, false);
  assert.deepEqual(limpo.nfse, { usuario: "joao", senhaGuardada: true });
  assert.equal(limpo.csc_de_teste_configurado, true);
  assert.equal(limpo.chave_de_acesso.length, 44);
  assert.doesNotMatch(limpo.mensagem, /spedy/i);
  assert.equal(limpo.texto, "[oculto]");
  assert.deepEqual(limpo.lista, [{ ok: 1 }]);
});

/* ---------------- Empresa ---------------- */

test("empresa: só muda o que veio, mostra antes e depois, limpa os opcionais e confere os formatos", () => {
  const { dados, mudancas, enderecoMudou, erros } = aplicarMudancasNaEmpresa(empresa(), {
    regime: "regime_normal",
    inscricao_estadual: "isento",
    cnaes: ["1813-0/01", "7319-0/02"],
    cnae_principal: "7319002",
    limpar: ["nome_fantasia"],
  });
  assert.deepEqual(erros, []);
  assert.equal(dados.regime, "regimeNormal");
  assert.equal(dados.inscricaoEstadual, "ISENTO");
  assert.equal(dados.nomeFantasia, null);
  assert.deepEqual(dados.cnaes, [
    { codigo: "1813001", principal: false },
    { codigo: "7319002", principal: true },
  ]);
  assert.equal(enderecoMudou, false);
  assert.equal(dados.razaoSocial, "Gráfica Aurora LTDA");
  const campos = mudancas.map((m) => m.campo).sort();
  assert.deepEqual(campos, ["cnaes", "inscricao_estadual", "nome_fantasia", "regime"]);
  assert.equal(mudancas.find((m) => m.campo === "regime")?.antes, "simples_nacional");

  const ruim = aplicarMudancasNaEmpresa(empresa(), { cnpj: "123", uf: "XX", cep: "1", regime: "lucro", email: "sem-arroba", limpar: ["cnpj"] });
  assert.equal(ruim.erros.length, 6);

  const cep = aplicarMudancasNaEmpresa(empresa(), { cep: "13010-000" });
  assert.equal(cep.enderecoMudou, true);
  assert.equal(cep.dados.endereco.cep, "13010000");
});

test("empresa: o que falta para salvar e o completar pelo cadastro público só preenche o vazio", () => {
  assert.deepEqual(faltasDaEmpresa(empresa()), []);
  assert.deepEqual(faltasDaEmpresa(vazioDeEmpresa()), ["CNPJ", "razão social", "regime tributário", "CEP", "rua", "bairro", "cidade", "estado (UF)"]);

  const cadastro = {
    cnpj: "00000000000191",
    razaoSocial: "BANCO DO BRASIL SA",
    nomeFantasia: "Direção Geral",
    email: "",
    telefone: "6134939002",
    inscricaoEstadual: null,
    cnaes: [{ codigo: "6422100", principal: true }],
    endereco: { cep: "70040912", logradouro: "Quadra Saun 5", numero: "S/N", complemento: "", bairro: "Asa Norte", cidade: "Brasília", uf: "DF", ibge: "5300108" },
  };
  const daLoja = { ...vazioDeEmpresa(), razaoSocial: "Nome da loja" };
  const completada = completarComCadastro(daLoja, cadastro);
  assert.equal(completada.razaoSocial, "Nome da loja");
  assert.equal(completada.cnpj, "00000000000191");
  assert.equal(completada.endereco.ibge, "5300108");
  assert.equal(completada.regime, "");
  assert.deepEqual(completada.cnaes, [{ codigo: "6422100", principal: true }]);
  assert.equal(completarComCadastro(empresa(), cadastro).endereco.cidade, "São Paulo");
});

test("certificado: o CNPJ sai do titular", () => {
  assert.equal(cnpjDoTitular("GRAFICA AURORA LTDA:11222333000181"), "11222333000181");
  assert.equal(cnpjDoTitular("sem cnpj"), null);
  assert.equal(cnpjDoTitular(null), null);
});

/* ---------------- Próximos passos ---------------- */

test("passos: empresa nova começa pelos dados, pergunta o regime e os tipos; o certificado é da pessoa no painel", () => {
  const { passos, perguntas, pronta } = montarProximosPassos(
    estado({
      temEmissor: false,
      empresaCadastrada: false,
      certificado: { enviado: false, dias: null },
      tipos: {
        nfe: { ativo: false, ambiente: "homologacao", testeAprovado: false },
        nfce: { ativo: false, ambiente: "homologacao", testeAprovado: false },
        nfse: { ativo: false, ambiente: "simulacao", testeAprovado: false },
      },
      perfis: { mercadoria: { existe: false, faltas: [] }, servico: { existe: false, faltas: [] } },
      algumTesteAprovado: false,
    })
  );
  assert.equal(pronta, false);
  assert.equal(passos[0].ferramenta, "alterar_dados_fiscais_da_empresa");
  assert.equal(passos[0].feito, false);
  const certificado = passos.find((p) => p.passo.startsWith("Certificado"));
  assert.equal(certificado?.quem_faz, "pessoa_no_painel");
  assert.match(certificado?.onde_no_painel ?? "", /Certificado digital/);
  assert.ok(passos.every((p, i) => p.ordem === i + 1));
  assert.ok(perguntas.some((p) => p.usar_em.startsWith("alterar_dados_fiscais_da_empresa.regime")));
  assert.ok(perguntas.some((p) => p.usar_em.startsWith("configurar_tipo_de_nota")));
  assert.doesNotMatch(JSON.stringify({ passos, perguntas }), /spedy/i);
});

test("passos: CSC e login da prefeitura vão para o painel; tributação e NCM pelo assistente; produção em produção", () => {
  const { passos, pronta } = montarProximosPassos(
    estado({
      tipos: {
        nfe: { ativo: true, ambiente: "homologacao", testeAprovado: true },
        nfce: { ativo: true, ambiente: "homologacao", testeAprovado: false, cscDoAmbiente: false },
        nfse: { ativo: true, ambiente: "homologacao", testeAprovado: false, faltaLoginDaPrefeitura: true },
      },
      perfis: { mercadoria: { existe: true, faltas: ["CSOSN"] }, servico: { existe: false, faltas: [] } },
      produtosSemNcm: 3,
      inscricaoMunicipal: false,
    })
  );
  assert.equal(pronta, false);
  const por = (inicio: string) => passos.find((p) => p.passo.startsWith(inicio));
  assert.equal(por("CSC")?.quem_faz, "pessoa_no_painel");
  assert.equal(por("Acesso ao sistema da prefeitura")?.quem_faz, "pessoa_no_painel");
  assert.equal(por("Inscrição municipal")?.ferramenta, "alterar_dados_fiscais_da_empresa");
  assert.equal(por("Tributação")?.ferramenta, "salvar_tributacao_fiscal");
  assert.match(por("Tributação")?.o_que_fazer ?? "", /CSOSN/);
  assert.equal(por("NCM")?.ferramenta, "corrigir_ncm_dos_produtos");
  assert.match(por("Nota de teste")?.o_que_fazer ?? "", /NFC-e.*NFS-e/);
  assert.equal(por("Emitir de verdade")?.ferramenta, "configurar_tipo_de_nota");
});

test("passos: no ambiente de testes do Ktalogu a produção não é com ninguém; tudo pronto vira pronta", () => {
  const sandbox = montarProximosPassos(estado({ servidor: "sandbox" }));
  assert.equal(sandbox.pronta, true);
  assert.equal(sandbox.passos.find((p) => p.passo === "Emitir de verdade")?.quem_faz, "ninguem");
  const semSaldo = montarProximosPassos(
    estado({ saldoDisponivel: 0, tipos: { ...estado().tipos, nfe: { ativo: true, ambiente: "producao", testeAprovado: true } } })
  );
  assert.equal(semSaldo.passos.find((p) => p.passo.startsWith("Pacote"))?.quem_faz, "pessoa_no_painel");
  const fora = montarProximosPassos(estado({ emissaoDisponivel: false }));
  assert.equal(fora.pronta, false);
  assert.equal(fora.passos[0].quem_faz, "suporte");
});

/* ---------------- Tributação ---------------- */

test("tributação: os campos da conversa viram as regras da tela, e voltam com os mesmos nomes", () => {
  const base = { ...MODELOS_DE_MERCADORIA[0].regras };
  const { regras, erros, mudou } = aplicarRegras("mercadoria", base, {
    cfop_dentro_do_estado: 5102,
    aliquota_pis: "0,65",
    aliquota_interna_por_uf: [
      { uf: "rj", aliquota: 20 },
      { uf: "SP", aliquota: 18 },
    ],
    limpar: ["informacoes_adicionais"],
  });
  assert.deepEqual(erros, []);
  assert.deepEqual(mudou.sort(), ["aliquota_interna_por_uf", "aliquota_pis", "cfop_dentro_do_estado", "informacoes_adicionais"]);
  const lidas = lerRegrasDeMercadoria(regras);
  assert.equal(lidas.cfopInterno, 5102);
  assert.equal(lidas.pisAliquota, 0.65);
  assert.deepEqual(lidas.aliquotaInternaPorUf, { RJ: 20, SP: 18 });
  assert.equal(lidas.informacoesAdicionais, null);
  const deVolta = regrasParaLer("mercadoria", lidas);
  assert.equal(deVolta.cfop_dentro_do_estado, 5102);
  assert.deepEqual(deVolta.aliquota_interna_por_uf, [
    { uf: "RJ", aliquota: 20 },
    { uf: "SP", aliquota: 18 },
  ]);

  const servico = aplicarRegras("servico", { ...SEM_REGRAS_DE_SERVICO }, { aliquota_iss: 2, tipo_de_tributacao_iss: "isento", iss_retido: "cliente_pj" });
  const lidoServico = lerRegrasDeServico(servico.regras);
  assert.equal(lidoServico.tipoTributacao, "exemption");
  assert.equal(lidoServico.issRetido, "cliente_pj");
  assert.equal(regrasParaLer("servico", lidoServico).tipo_de_tributacao_iss, "isento");

  const errado = aplicarRegras("mercadoria", base, { aliquota_iss: 2, tipo_de_tributacao_iss: "x", aliquota_interna_por_uf: [{ uf: "ZZ", aliquota: 1 }] });
  assert.equal(errado.erros.length, 3);
  assert.match(errado.erros[0], /serviço/);
});
