import { describe, it } from "node:test";
import assert from "node:assert/strict";
import {
  cronograma,
  descreverCondicoes,
  dividirEmParcelas,
  erroDasCondicoes,
  normalizarCondicoes,
  padraoDasCondicoes,
  proximaParcelaEmAberto,
  resumirCondicoes,
  situacaoDasParcelas,
  valorDaEntrada,
  type CondicoesComerciais,
} from "./condicoes-comerciais.ts";

function condicoes(mudancas: Partial<CondicoesComerciais>): CondicoesComerciais {
  return { ...padraoDasCondicoes(), ...mudancas };
}

describe("normalizarCondicoes", () => {
  it("devolve null quando não há condições guardadas", () => {
    assert.equal(normalizarCondicoes(null), null);
    assert.equal(normalizarCondicoes(undefined), null);
    assert.equal(normalizarCondicoes("x"), null);
    assert.equal(normalizarCondicoes([]), null);
  });

  it("completa o que faltar com o padrão e descarta o que não conhece", () => {
    const lidas = normalizarCondicoes({ formas: ["pix", "cheque", "pix", "boleto"], esquema: "parcelado", parcelado: { parcelas: 99 } });
    assert.ok(lidas);
    assert.deepEqual(lidas.formas, ["pix", "boleto"]);
    assert.equal(lidas.esquema, "parcelado");
    assert.equal(lidas.parcelado.parcelas, 24);
    assert.equal(lidas.parcelado.intervalo_dias, 30);
    assert.equal(lidas.a_vista.quando, "aprovacao");
    assert.equal(lidas.cartao.sem_juros, true);
  });

  it("prende a entrada percentual entre 1 e 99", () => {
    const lidas = normalizarCondicoes({ entrada: { tipo: "percentual", valor: 250 } });
    assert.equal(lidas?.entrada.valor, 99);
    const zero = normalizarCondicoes({ entrada: { tipo: "percentual", valor: 0 } });
    assert.equal(zero?.entrada.valor, 1);
  });
});

describe("erroDasCondicoes", () => {
  it("exige uma forma de pagamento", () => {
    assert.equal(erroDasCondicoes(condicoes({ formas: [] }), 100), "Escolha ao menos uma forma de pagamento.");
    assert.equal(erroDasCondicoes(condicoes({}), 100), null);
  });

  it("não deixa a entrada em valor engolir o total", () => {
    const comEntrada = condicoes({ esquema: "entrada_e_saldo", entrada: { tipo: "valor", valor: 500 } });
    assert.equal(erroDasCondicoes(comEntrada, 400), "A entrada precisa ser menor que o total do orçamento.");
    assert.equal(erroDasCondicoes(comEntrada, 900), null);
    assert.equal(erroDasCondicoes(condicoes({ esquema: "entrada_e_saldo", entrada: { tipo: "valor", valor: 0 } }), 900), "Informe o valor da entrada.");
  });
});

describe("dividirEmParcelas", () => {
  it("fecha os centavos na última parcela", () => {
    assert.deepEqual(dividirEmParcelas(100, 3), [33.33, 33.33, 33.34]);
    assert.deepEqual(dividirEmParcelas(950.07, 2), [475.03, 475.04]);
    assert.equal(dividirEmParcelas(950.07, 3).reduce((soma, valor) => soma + valor, 0).toFixed(2), "950.07");
  });
});

describe("cronograma", () => {
  it("à vista na aprovação sem pedido não tem data ainda", () => {
    const parcelas = cronograma(condicoes({}), 950.07, {});
    assert.equal(parcelas.length, 1);
    assert.equal(parcelas[0].valor, 950.07);
    assert.equal(parcelas[0].quando, "na aprovação");
    assert.equal(parcelas[0].vencimento, null);
    assert.equal(parcelas[0].base, "aprovacao");
  });

  it("à vista na aprovação ganha a data do pedido", () => {
    const parcelas = cronograma(condicoes({}), 950.07, { aprovacao: "2026-09-16T18:20:00.000Z" });
    assert.equal(parcelas[0].vencimento, "2026-09-16");
  });

  it("faturado conta os dias a partir da entrega", () => {
    const faturado = condicoes({ a_vista: { quando: "faturado", dias: 28 } });
    const parcelas = cronograma(faturado, 1000, { entrega: "2026-09-30" });
    assert.equal(parcelas[0].vencimento, "2026-10-28");
    assert.equal(parcelas[0].quando, "28 dias após a entrega");
    assert.equal(cronograma(faturado, 1000, {})[0].vencimento, null);
  });

  it("entrada de 50% e saldo na entrega", () => {
    const parcelas = cronograma(condicoes({ esquema: "entrada_e_saldo" }), 950.07, { aprovacao: "2026-09-16", entrega: "2026-09-30" });
    assert.equal(parcelas.length, 2);
    assert.equal(parcelas[0].rotulo, "Entrada (50%)");
    assert.equal(parcelas[0].valor, 475.04);
    assert.equal(parcelas[0].vencimento, "2026-09-16");
    assert.equal(parcelas[1].rotulo, "Saldo");
    assert.equal(parcelas[1].valor, 475.03);
    assert.equal(parcelas[1].vencimento, "2026-09-30");
  });

  it("entrada em valor e saldo em duas parcelas de 30 em 30 dias", () => {
    const parcelas = cronograma(
      condicoes({
        esquema: "entrada_e_saldo",
        entrada: { tipo: "valor", valor: 200 },
        saldo: { quando: "parcelado", dias: 30, parcelas: 2, intervalo_dias: 30 },
      }),
      1000,
      { aprovacao: "2026-01-31" }
    );
    assert.deepEqual(
      parcelas.map((parcela) => [parcela.rotulo, parcela.valor, parcela.vencimento]),
      [
        ["Entrada", 200, "2026-01-31"],
        ["Saldo 1 de 2", 400, "2026-03-02"],
        ["Saldo 2 de 2", 400, "2026-04-01"],
      ]
    );
  });

  it("entrada maior que o total vira só a entrada", () => {
    const parcelas = cronograma(condicoes({ esquema: "entrada_e_saldo", entrada: { tipo: "valor", valor: 5000 } }), 900, {});
    assert.equal(parcelas.length, 1);
    assert.equal(parcelas[0].valor, 900);
  });

  it("parcelado com a primeira na aprovação", () => {
    const parcelas = cronograma(condicoes({ esquema: "parcelado" }), 300, { aprovacao: "2026-09-16" });
    assert.deepEqual(
      parcelas.map((parcela) => [parcela.quando, parcela.vencimento, parcela.valor]),
      [
        ["na aprovação", "2026-09-16", 100],
        ["30 dias após a aprovação", "2026-10-16", 100],
        ["60 dias após a aprovação", "2026-11-15", 100],
      ]
    );
  });

  it("parcelado 30/60/90 e parcelado a partir da entrega", () => {
    const trintaSessentaNoventa = condicoes({ esquema: "parcelado", parcelado: { parcelas: 3, intervalo_dias: 30, primeira: "dias", dias: 30 } });
    assert.deepEqual(
      cronograma(trintaSessentaNoventa, 300, { aprovacao: "2026-09-16" }).map((parcela) => parcela.vencimento),
      ["2026-10-16", "2026-11-15", "2026-12-15"]
    );
    const naEntrega = condicoes({ esquema: "parcelado", parcelado: { parcelas: 2, intervalo_dias: 15, primeira: "entrega", dias: 30 } });
    const parcelas = cronograma(naEntrega, 300, { entrega: "2026-09-30" });
    assert.deepEqual(
      parcelas.map((parcela) => [parcela.quando, parcela.vencimento, parcela.base]),
      [
        ["na entrega", "2026-09-30", "entrega"],
        ["15 dias após a entrega", "2026-10-15", "entrega"],
      ]
    );
  });
});

describe("situacaoDasParcelas", () => {
  it("quita as parcelas na ordem e marca a vencida", () => {
    const parcelas = cronograma(condicoes({ esquema: "parcelado" }), 300, { aprovacao: "2026-08-01" });
    const situacao = situacaoDasParcelas(parcelas, 150, "2026-09-16");
    assert.deepEqual(
      situacao.map((parcela) => [parcela.situacao, parcela.pago]),
      [
        ["paga", 100],
        ["vencida", 50],
        ["pendente", 0],
      ]
    );
    assert.equal(proximaParcelaEmAberto(situacao)?.numero, 2);
    assert.equal(proximaParcelaEmAberto(situacaoDasParcelas(parcelas, 300, "2026-09-16")), null);
  });
});

describe("descreverCondicoes", () => {
  it("escreve à vista e as formas com o parcelamento do cartão", () => {
    const escritas = descreverCondicoes(condicoes({ formas: ["pix", "cash", "credit_card"], cartao: { parcelas: 3, sem_juros: true } }), 950.07);
    assert.equal(escritas.pagamento, "À vista na aprovação.");
    assert.equal(escritas.formas, "Pix, dinheiro ou cartão de crédito (em até 3x sem juros).");
  });

  it("escreve entrada e saldo com os valores", () => {
    const escritas = descreverCondicoes(condicoes({ esquema: "entrada_e_saldo" }), 950.07);
    assert.equal(escritas.pagamento, "Entrada de 50% (R$ 475,04) na aprovação e saldo na entrega.");
    const parceladoNoSaldo = descreverCondicoes(
      condicoes({ esquema: "entrada_e_saldo", saldo: { quando: "parcelado", dias: 30, parcelas: 3, intervalo_dias: 30 } }),
      0
    );
    assert.equal(parceladoNoSaldo.pagamento, "Entrada de 50% na aprovação e saldo em 3 parcelas (30, 60 e 90 dias após a aprovação).");
  });

  it("escreve o parcelado e o faturado", () => {
    assert.equal(
      descreverCondicoes(condicoes({ esquema: "parcelado" }), 300).pagamento,
      "3 parcelas de R$ 100,00: a primeira na aprovação e as demais a cada 30 dias."
    );
    assert.equal(
      descreverCondicoes(condicoes({ a_vista: { quando: "faturado", dias: 28 } }), 300).pagamento,
      "Faturado: pagamento em 28 dias após a entrega."
    );
    assert.equal(resumirCondicoes(condicoes({ formas: ["boleto"] }), 100), "À vista na aprovação. Boleto.");
  });

  it("calcula a entrada em valor e em percentual", () => {
    assert.equal(valorDaEntrada(condicoes({ entrada: { tipo: "percentual", valor: 30 } }), 1000), 300);
    assert.equal(valorDaEntrada(condicoes({ entrada: { tipo: "valor", valor: 250.5 } }), 1000), 250.5);
    assert.equal(valorDaEntrada(condicoes({ entrada: { tipo: "valor", valor: 5000 } }), 1000), 1000);
  });
});
