import assert from "node:assert/strict";
import { test } from "node:test";
import {
  BASE_DO_ASAAS,
  billingTypeDoMeio,
  configuracaoPublicaDoAsaas,
  credenciaisAtivas,
  estadoDoPagamento,
  instanteDoPagamento,
  lerConfiguracaoDoAsaas,
  metodoDoPagamento,
  tokensIguais,
  vencimentoNoBrasil,
} from "./asaas.ts";

/*
  Este arquivo e a metade da loja. O gemeo do admin, em
  `apps/admin/src/lib/asaas.test.ts`, cobre o mesmo miolo — os dois apps nao
  compartilham pacote, entao as duas copias precisam concordar. Aqui ficam as
  provas do que so existe deste lado: o que a vitrine pode saber e a conferencia
  do segredo do webhook.
*/

/* ------------------------------------ o que a vitrine pode e nao pode saber */

const linha = (settings: Record<string, unknown>, active: boolean | null = true) => ({
  id: "int_1",
  user_id: "dono_1",
  provider: "asaas",
  token: null,
  refresh_token: null,
  settings,
  active,
});

test("a configuracao publica nunca carrega a chave da conta do lojista", () => {
  const publica = configuracaoPublicaDoAsaas(
    linha({ environment: "production", production_api_key: "$aact_segredo_do_lojista" })
  );

  assert.ok(publica, "deveria ter devolvido configuracao");
  const serializada = JSON.stringify(publica);
  assert.ok(!serializada.includes("aact_segredo_do_lojista"), "a chave vazou para o navegador");
  assert.ok(!serializada.includes("api_key"), "campo de chave vazou para o navegador");
});

test("integracao desligada, sem chave ou com checkout fechado nao habilita a loja", () => {
  assert.equal(configuracaoPublicaDoAsaas(linha({ sandbox_api_key: "x" }, false)), null);
  assert.equal(configuracaoPublicaDoAsaas(linha({ environment: "sandbox", sandbox_api_key: "" })), null);
  assert.equal(
    configuracaoPublicaDoAsaas(linha({ sandbox_api_key: "x", checkout_enabled: false })),
    null
  );
  assert.equal(configuracaoPublicaDoAsaas(null), null);
});

test("com chave e checkout aberto, a loja recebe o ambiente e os TRES meios", () => {
  const publica = configuracaoPublicaDoAsaas(
    linha({ environment: "sandbox", sandbox_api_key: "x", aceita_cartao: true })
  );

  assert.equal(publica?.ambiente, "sandbox");
  assert.equal(publica?.aceitaPix, true);
  assert.equal(publica?.aceitaBoleto, true);
  assert.equal(publica?.aceitaCartao, true);
});

test("integracao nova nasce com Pix e boleto; cartao fica desligado", () => {
  /* Cartao pelo Asaas leva o comprador para uma pagina deles e o traz de
     volta. Ligar isso sem o lojista pedir seria decidir no lugar dele. */
  const config = lerConfiguracaoDoAsaas({});
  assert.equal(config.aceita_pix, true);
  assert.equal(config.aceita_boleto, true);
  assert.equal(config.aceita_cartao, false);
});

test("o billingType de cada meio e o nome que o Asaas espera", () => {
  assert.equal(billingTypeDoMeio("pix"), "PIX");
  assert.equal(billingTypeDoMeio("boleto"), "BOLETO");
  /* CREDIT_CARD sem objeto `creditCard` na requisicao e exatamente o que faz
     o Asaas devolver a fatura hospedada em vez de cobrar direto. */
  assert.equal(billingTypeDoMeio("cartao"), "CREDIT_CARD");
});

/* ------------------------------------------------ o segredo do webhook */

test("token igual passa, token diferente nao passa", () => {
  assert.equal(tokensIguais("abc123", "abc123"), true);
  assert.equal(tokensIguais("abc123", "abc124"), false);
});

test("tamanho diferente nao passa, nem quando um e prefixo do outro", () => {
  /* O caso perigoso: quem manda "ab" nao pode ser aceito so porque bate com o
     comeco do segredo "abcdef". */
  assert.equal(tokensIguais("abcdef", "ab"), false);
  assert.equal(tokensIguais("ab", "abcdef"), false);
});

test("token vazio nunca passa, nem contra outro vazio", () => {
  /* Integracao recem-criada tem `webhook_token` vazio. Se vazio batesse com
     vazio, qualquer requisicao sem cabecalho entraria como se fosse o Asaas. */
  assert.equal(tokensIguais("", ""), false);
  assert.equal(tokensIguais("segredo", ""), false);
  assert.equal(tokensIguais("", "segredo"), false);
});

test("a comparacao percorre tudo, entao erro no comeco e no fim custam o mesmo", () => {
  /* Nao da para cronometrar isso de forma estavel num teste; o que da para
     provar e que as duas pontas sao olhadas. */
  assert.equal(tokensIguais("xbcdef", "abcdef"), false);
  assert.equal(tokensIguais("abcdex", "abcdef"), false);
});

/* --------------------------------- o miolo, que tem que bater com o gemeo */

test("as bases dos dois ambientes sao as mesmas do lado do admin", () => {
  assert.equal(BASE_DO_ASAAS.production, "https://api.asaas.com/v3");
  assert.equal(BASE_DO_ASAAS.sandbox, "https://api-sandbox.asaas.com/v3");
});

test("a traducao de estados e a mesma dos dois lados", () => {
  assert.equal(estadoDoPagamento("CONFIRMED"), "approved");
  assert.equal(estadoDoPagamento("RECEIVED"), "approved");
  assert.equal(estadoDoPagamento("OVERDUE"), "pending");
  assert.equal(estadoDoPagamento("REFUNDED"), "refunded");
  assert.equal(estadoDoPagamento("CHARGEBACK_REQUESTED"), "charged_back");
  assert.equal(estadoDoPagamento("DELETED"), "cancelled");
  assert.equal(estadoDoPagamento("ALGO_NOVO"), "pending");
});

test("boleto continua sendo ticket, que e o que o banco aceita", () => {
  assert.equal(metodoDoPagamento("BOLETO"), "ticket");
  assert.equal(metodoDoPagamento("PIX"), "pix");
  assert.equal(metodoDoPagamento("UNDEFINED"), "unknown");
});

test("em producao sem chave de producao nao ha credencial", () => {
  const config = lerConfiguracaoDoAsaas({ environment: "production", sandbox_api_key: "so_sandbox" });
  assert.equal(credenciaisAtivas(config), null);
});

test("o vencimento e o dia no Brasil, nao o do servidor em UTC", () => {
  assert.equal(vencimentoNoBrasil(0, new Date("2026-09-13T02:00:00.000Z")), "2026-09-12");
  assert.equal(vencimentoNoBrasil(0, new Date("2026-09-13T15:00:00.000Z")), "2026-09-13");
});


/* ------------------------------------------ o instante do pagamento */

test("data pura do Asaas vira meio-dia de Brasilia, nao meia-noite UTC", () => {
  /* O Asaas manda "2026-09-14" sem hora. Guardar assim numa coluna de instante
     vira meia-noite UTC, que no Brasil e 21h do DIA ANTERIOR: um Pix pago no
     dia 14 caia no fluxo de caixa como dia 13. */
  const instante = instanteDoPagamento("2026-09-14");
  assert.equal(instante, "2026-09-14T15:00:00.000Z");

  /* A prova do que importa: o dia continua 14 quando lido no Brasil. */
  const noBrasil = new Date(new Date(instante as string).getTime() - 3 * 60 * 60 * 1000);
  assert.equal(noBrasil.toISOString().slice(0, 10), "2026-09-14");
});

test("meia-noite UTC seria o dia errado no Brasil", () => {
  /* O comportamento antigo, guardado aqui para ninguem voltar a ele sem querer. */
  const errado = new Date(new Date("2026-09-14T00:00:00.000Z").getTime() - 3 * 60 * 60 * 1000);
  assert.equal(errado.toISOString().slice(0, 10), "2026-09-13");
});

test("instante completo passa inteiro, sem inventar hora", () => {
  assert.equal(instanteDoPagamento("2026-09-14T18:42:10.000Z"), "2026-09-14T18:42:10.000Z");
});

test("vazio e lixo viram nada, nunca uma data inventada", () => {
  assert.equal(instanteDoPagamento(""), null);
  assert.equal(instanteDoPagamento(null), null);
  assert.equal(instanteDoPagamento("ontem"), null);
});
