/*
 * O engorde exato e o registro das silhuetas (rodar: npx tsx --test src/lib/banner-editor/engorde.test.ts).
 */
import assert from "node:assert/strict";
import { test } from "node:test";
import { engordeExato, passosDoEngorde, tintaLisa } from "./engorde";
import { chaveDaSilhueta, registrarCalculoDaSilhueta, silhuetaAgora, silhuetaDe, type PedidoDeSilhueta } from "./silhuetas-prontas";

test("os passos somam o engorde e nenhum passa da largura já engordada (sem abrir buraco)", () => {
  for (const r of [0.6, 4, 18.5, 36, 100, 160]) {
    const passos = passosDoEngorde(r);
    let feito = 0;
    for (const p of passos) {
      assert.ok(p.raio <= 1.2 + 2 * feito + 1e-9, `r=${r}: passo ${p.raio} depois de ${feito}`);
      assert.ok(p.pontas >= 6 && p.pontas <= 72);
      feito += p.raio;
    }
    assert.ok(Math.abs(feito - r) < 0.06, `r=${r}: soma ${feito}`);
  }
  assert.deepEqual(passosDoEngorde(0), []);
});

test("um elemento por passo (o Chrome não guarda o resultado do meio do filtro), nada de desfoque", () => {
  const e = engordeExato({ id: "x", raio: 36, conteudo: "<b>A</b>", tinta: tintaLisa("#FF0000"), w: 300, h: 100 });
  const passos = passosDoEngorde(36).length;
  assert.equal((e.defs.match(/<filter /g) || []).length, passos + 1, "o começo (alfa em cor) e um por passo");
  assert.equal((e.html.match(/filter:url\(#x/g) || []).length, passos + 1);
  assert.doesNotMatch(e.defs, /feGaussianBlur|feMorphology/);
  assert.match(e.defs, /mode="lighten"/, "o maior valor (a borda suave não endurece)");
  assert.match(e.defs, /flood-color="#FF0000"/);
  /* A região do filtro cobre o engorde (a caixa mais o raio). */
  const x = Number(e.defs.match(/x="(-[\d.]+)"/)![1]);
  assert.ok(-x * 300 >= 36, `região ${x}`);
});

test("fechar os buracos afina de volta; o anel tira o desenho do miolo; o rápido é um passo só; sem raio, nada", () => {
  const fechado = engordeExato({ id: "f", raio: 10, conteudo: "<b>A</b>", tinta: tintaLisa("#000000"), w: 100, h: 100, fechar: 8 });
  assert.match(fechado.defs, /mode="darken"/);
  const anel = engordeExato({ id: "a", raio: 10, conteudo: "<b>A</b>", tinta: tintaLisa("#000000"), w: 100, h: 100, anel: true });
  assert.match(anel.html, /mix-blend-mode:lighten;filter:brightness\(0\) invert\(1\)/);
  assert.match(anel.defs, /values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 -1 0 0 0"/, "vermelho menos verde: só fora do desenho");
  const rapido = engordeExato({ id: "r", raio: 20, conteudo: "<b>A</b>", tinta: tintaLisa("#000000"), w: 100, h: 100, rapido: true, fechar: 5 });
  assert.equal((rapido.defs.match(/<filter /g) || []).length, 2);
  assert.doesNotMatch(rapido.defs, /darken/, "o rápido não fecha buracos");
  assert.deepEqual(engordeExato({ id: "z", raio: 0, conteudo: "<b>A</b>", tinta: tintaLisa("#000000"), w: 100, h: 100 }), { defs: "", html: "" });
});

test("silhuetas: a chave muda com o desenho e a caixa; sem quem calcule, o engorde; com, espera e chega", async () => {
  assert.equal(chaveDaSilhueta("<b>A</b>", 100, 50), chaveDaSilhueta("<b>A</b>", 100, 50));
  assert.notEqual(chaveDaSilhueta("<b>A</b>", 100, 50), chaveDaSilhueta("<b>B</b>", 100, 50));
  assert.notEqual(chaveDaSilhueta("<b>A</b>", 100, 50), chaveDaSilhueta("<b>A</b>", 101, 50));
  const pedido = (html: string): PedidoDeSilhueta => ({ dono: "p", chave: chaveDaSilhueta(html, 10, 10), html, w: 10, h: 10, fontes: [] });
  assert.equal(silhuetaDe(pedido("<i>1</i>")), "sem", "no servidor: o engorde exato");
  let chamadas = 0;
  registrarCalculoDaSilhueta(async (p) => {
    chamadas += 1;
    return p.html.includes("falha") ? null : { d: `M0 0L${p.w} 0L${p.w} ${p.h}Z`, dSemFuros: `M0 0L${p.w} 0L${p.w} ${p.h}Z` };
  });
  const p2 = pedido("<i>2</i>");
  assert.equal(silhuetaDe(p2), "esperando");
  assert.equal(silhuetaDe({ ...p2 }), "esperando", "o mesmo pedido não entra duas vezes");
  await new Promise((r) => setTimeout(r, 150));
  const pronta = silhuetaDe(p2);
  assert.ok(typeof pronta === "object" && pronta.d.startsWith("M0 0"));
  assert.equal(chamadas, 1);
  assert.equal(await silhuetaAgora(pedido("<i>falha</i>")), null);
  assert.equal(silhuetaDe(pedido("<i>falha</i>")), "sem", "deu errado: fica o engorde");
});
