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

import { criarLiberacaoLembrada, digitaisLiberados, idsDaLiberacao, liberadoNasLinhas, type LinhaDeLiberacao } from "./liberacao.ts";

const AURORA = "609a0082-1f55-4a79-a76a-cc0017a4ea23";
const DONO = "11111111-2222-3333-4444-555555555555";
const OUTRA = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee";

test("a variável continua valendo sozinha", () => {
  assert.equal(digitaisLiberados([AURORA], ""), false);
  assert.equal(digitaisLiberados([AURORA], AURORA.toUpperCase()), true);
  assert.equal(digitaisLiberados([null, DONO], `${AURORA}, ${DONO}`), true);
  assert.equal(digitaisLiberados([OUTRA], "todas"), true);
});

test("ids da liberação: sem vazio, sem repetir, minúsculos", () => {
  assert.deepEqual(idsDaLiberacao([AURORA, null, undefined, "", AURORA.toUpperCase(), DONO]), [AURORA, DONO]);
});

test("a linha do banco libera pela empresa ou pelo dono, e só com liberado = true", () => {
  assert.equal(liberadoNasLinhas([AURORA, DONO], [{ company_id: DONO, liberado: true }]), true);
  assert.equal(liberadoNasLinhas([AURORA], [{ company_id: AURORA.toUpperCase(), liberado: true }]), true);
  assert.equal(liberadoNasLinhas([AURORA], [{ company_id: AURORA, liberado: false }]), false);
  assert.equal(liberadoNasLinhas([AURORA], [{ company_id: AURORA, liberado: null }]), false);
  /* Linha de outra empresa não abre esta. */
  assert.equal(liberadoNasLinhas([AURORA], [{ company_id: OUTRA, liberado: true }]), false);
  assert.equal(liberadoNasLinhas([AURORA], null), false);
  assert.equal(liberadoNasLinhas([], [{ company_id: AURORA, liberado: true }]), false);
});

function montar(linhas: LinhaDeLiberacao[], configuracao = "") {
  let relogio = 1_000;
  const leituras: string[][] = [];
  const falhas: unknown[] = [];
  let falhar = false;
  const liberacao = criarLiberacaoLembrada({
    configuracao: () => configuracao,
    agora: () => relogio,
    ler: async (ids) => {
      leituras.push(ids);
      if (falhar) throw new Error("banco fora");
      return linhas.filter((l) => ids.includes(l.company_id));
    },
    aoFalhar: (erro) => falhas.push(erro),
  });
  return {
    liberacao,
    leituras,
    falhas,
    andar: (ms: number) => (relogio += ms),
    quebrar: (sim: boolean) => (falhar = sim),
  };
}

test("variável OU banco: a variável nem precisa ler o banco", async () => {
  const t = montar([], AURORA);
  assert.equal(await t.liberacao.consultar([AURORA]), true);
  assert.equal(t.leituras.length, 0);
});

test("sem variável, a linha do banco libera", async () => {
  const t = montar([{ company_id: AURORA, liberado: true }]);
  assert.equal(await t.liberacao.consultar([AURORA, null]), true);
  assert.equal(await t.liberacao.consultar([OUTRA]), false);
  assert.equal(await t.liberacao.consultar([null, undefined]), false);
  /* Sem id nenhum, nem pergunta ao banco. */
  assert.equal(t.leituras.length, 2);
});

test("a resposta fica lembrada por um minuto e esquecer apaga na hora", async () => {
  const linhas: LinhaDeLiberacao[] = [];
  const t = montar(linhas);
  assert.equal(await t.liberacao.consultar([AURORA]), false);
  linhas.push({ company_id: AURORA, liberado: true });
  t.andar(30_000);
  assert.equal(await t.liberacao.consultar([AURORA]), false, "ainda lembrado");
  assert.equal(t.leituras.length, 1);
  t.andar(31_000);
  assert.equal(await t.liberacao.consultar([AURORA]), true, "passou o minuto, leu de novo");
  assert.equal(t.leituras.length, 2);

  linhas.length = 0;
  t.liberacao.esquecer([OUTRA]);
  assert.equal(await t.liberacao.consultar([AURORA]), true, "esquecer outra empresa não mexe nesta");
  t.liberacao.esquecer([AURORA.toUpperCase()]);
  assert.equal(await t.liberacao.consultar([AURORA]), false);
  t.liberacao.esquecer();
  assert.equal(await t.liberacao.consultar([AURORA]), false);
  assert.equal(t.leituras.length, 4);
});

test("banco fora: nunca lança; vale a última lembrança ou só a variável", async () => {
  const t = montar([{ company_id: AURORA, liberado: true }]);
  t.quebrar(true);
  assert.equal(await t.liberacao.consultar([AURORA]), false);
  assert.equal(t.falhas.length, 1);

  t.quebrar(false);
  assert.equal(await t.liberacao.consultar([AURORA]), true);
  t.andar(61_000);
  t.quebrar(true);
  assert.equal(await t.liberacao.consultar([AURORA]), true, "lembrança vencida ainda vale quando o banco cai");
  /* A falha não é lembrada: volta a perguntar na próxima. */
  t.quebrar(false);
  assert.equal(await t.liberacao.consultar([AURORA]), true);
  assert.equal(t.falhas.length, 2);
});
