/*
 * Separar contorno e os cantos do contorno (rodar: npx tsx --test src/lib/banner-editor/separar-contorno.test.ts).
 */
import assert from "node:assert/strict";
import { test } from "node:test";
import { contornosQueSeparam, separarContorno, tracoDoContorno } from "./separar-contorno";
import { cantosDoContorno, conteudoDaPeca, documentoLimpo, solida, type Peca, type PecaDeImagem, type PecaDeTexto, type PontoDoTraco } from "./documento";

const texto = (extra: Partial<PecaDeTexto> = {}) =>
  ({ id: "t", tipo: "texto", nome: "Título", x: 10, y: 20, w: 300, h: 120, rot: 4, opacidade: 1, texto: "MEGA", fonte: "montserrat", tamanho: 80, peso: 900, pintura: solida("#FFD400"), alinhamento: "centro", vertical: "meio", italico: false, caixaAlta: false, riscado: false, sublinhado: true, espaco: 0, linha: 1, fundo: null, contorno: { cor: "#111111", largura: 18 }, sombra: { cor: "#000000", x: 4, y: 4, desfoque: 0, opacidade: 0.4 }, grupo: "g1", ...extra }) as PecaDeTexto;
const foto = (extra: Partial<PecaDeImagem> = {}) =>
  ({ id: "f", tipo: "imagem", x: 0, y: 0, w: 200, h: 200, rot: 0, opacidade: 1, src: "https://ptpstwltddugnnlewvbw.supabase.co/storage/v1/object/public/products/x.png", papel: "foto", ajuste: "conter", raio: 12, circulo: false, borda: { cor: "#FFFFFF", largura: 10 }, sombra: null, filtros: { brilho: 100, contraste: 100, saturacao: 100, desfoque: 0 }, adesivo: { cor: "#FF0000", largura: 8 }, ...extra }) as PecaDeImagem;

test("texto: só a borda vira texto sem tinta com o contorno; a original perde o contorno", () => {
  const r = separarContorno(texto(), "contorno", "borda", "novo")!;
  assert.equal((r.original as PecaDeTexto).contorno, null);
  const c = r.contorno as PecaDeTexto;
  assert.equal(c.id, "novo");
  assert.equal(c.pintura.tipo, "nenhuma");
  assert.deepEqual(c.contorno, { cor: "#111111", largura: 18 });
  assert.equal(c.sombra, null);
  assert.equal(c.sublinhado, false);
  assert.equal(c.grupo, "g1");
  assert.equal(c.texto, "MEGA");
  assert.equal(r.acima, false);
});

test("texto: cheio pinta a letra da cor do contorno; letra vazada não separa", () => {
  const c = separarContorno(texto(), "contorno", "cheio", "n")!.contorno as PecaDeTexto;
  assert.equal(c.pintura.tipo, "solida");
  assert.equal(c.pintura.cor, "#111111");
  assert.deepEqual(contornosQueSeparam(texto({ pintura: { tipo: "nenhuma", cor: "#000000", cor2: "#000000", angulo: 0 } })), []);
});

test("trechos com cor perdem a cor na peça do contorno", () => {
  const c = separarContorno(texto({ html: '<span style="color:#FF0000;-webkit-text-fill-color:#FF0000;font-weight:900">ME</span>GA' }), "contorno", "borda", "n")!.contorno as PecaDeTexto;
  assert.ok(!/color/i.test(c.html || ""), c.html || "");
  assert.ok(/font-weight:900/.test(c.html || ""));
});

test("foto: contorno de adesivo vira a silhueta (anel ou cheia) e a moldura vira um retângulo", () => {
  assert.deepEqual(contornosQueSeparam(foto()), ["adesivo", "moldura"]);
  const anel = separarContorno(foto(), "adesivo", "borda", "a")!;
  assert.equal((anel.contorno as PecaDeImagem).silhueta, "borda");
  assert.equal((anel.original as PecaDeImagem).adesivo, null);
  const moldura = separarContorno(foto(), "moldura", "borda", "m")!;
  assert.equal(moldura.contorno.tipo, "forma");
  assert.equal(moldura.acima, true);
  assert.equal((moldura.original as PecaDeImagem).borda, null);
  /* Foto dentro de uma forma não mostra a moldura. */
  assert.deepEqual(contornosQueSeparam(foto({ mascara: "estrela" })), ["adesivo"]);
});

test("desenho: a silhueta só desenha o contorno; o contorno redondo da letra sai pelo engorde (no servidor)", () => {
  const anel = separarContorno(foto(), "adesivo", "borda", "a")!.contorno;
  const html = conteudoDaPeca(anel);
  assert.match(html, /mix-blend-mode:lighten/, "o anel tira a foto do miolo");
  /* A foto entra só como molde (no engorde e no miolo que sai): 2 vezes; com a foto à vista seriam 3. */
  assert.equal((html.match(/<img /g) || []).length, 2, "a foto não aparece fora do anel");
  assert.equal((conteudoDaPeca({ ...anel, silhueta: null } as Peca).match(/<img /g) || []).length, 2, "o adesivo normal: o molde e a foto");
  const redondo = conteudoDaPeca(texto({ contorno: { cor: "#111111", largura: 18, cantos: "redondos" } }));
  assert.ok(!/-webkit-text-stroke/.test(redondo) && /url\(#cntt/.test(redondo) && !/feGaussianBlur/.test(redondo), "engorde exato no lugar do text-stroke e do desfoque");
  assert.match(conteudoDaPeca(texto()), /-webkit-text-stroke:18px/);
  assert.equal(cantosDoContorno(texto()), "vivos");
  assert.equal(cantosDoContorno(texto({ curva: 60 })), "redondos");
});

test("os cantos e a silhueta passam pela limpeza do documento", () => {
  const doc = documentoLimpo({ largura: 500, altura: 500, fundo: { tipo: "cor", cor: "#FFFFFF" }, pecas: [texto({ contorno: { cor: "#111111", largura: 18, cantos: "redondos" } }), { ...foto(), silhueta: "cheia" } as Peca] }, 500, 500);
  assert.equal((doc.pecas.find((p) => p.tipo === "texto") as PecaDeTexto).contorno?.cantos, "redondos");
  assert.equal((doc.pecas.find((p) => p.tipo === "imagem") as PecaDeImagem).silhueta, "cheia");
});

/* Um quadrado (px na caixa da peça) como as curvas que a silhueta devolve. */
const quadrado = (x0: number, y0: number, x1: number, y1: number): PontoDoTraco[] => [
  { x: x0, y: y0 },
  { x: x1, y: y0 },
  { x: x1, y: y1 },
  { x: x0, y: y1 },
];

test("em curvas: o desenho fica no lugar da peça (caixa dos contornos, centro levado pelo giro)", () => {
  const p = texto({ x: 100, y: 50, w: 200, h: 100, rot: 90 });
  const centrado = tracoDoContorno(p, [quadrado(-10, -10, 210, 110)], "#111111", "c")!;
  assert.equal(centrado.tipo, "traco");
  assert.deepEqual([centrado.x, centrado.y, centrado.w, centrado.h, centrado.rot], [90, 40, 220, 120, 90]);
  assert.equal(centrado.preenchimento.cor, "#111111");
  assert.equal(centrado.espessura, 0);
  assert.equal(centrado.grupo, "g1");
  /* Fora do centro: o meio da caixa nova (50, 50) fica 50 px à esquerda do meio da peça; girado 90°, sobe 50. */
  const torto = tracoDoContorno(p, [quadrado(0, 0, 100, 100)], "#111111", "c")!;
  assert.deepEqual([torto.x + torto.w / 2, torto.y + torto.h / 2], [200, 50]);
});

test("em curvas: o espelho da letra vai para as curvas; a foto já vem espelhada; furos viram contornos", () => {
  const p = texto({ x: 0, y: 0, w: 200, h: 100, rot: 0, espelharX: true });
  const t = tracoDoContorno(p, [quadrado(0, 0, 50, 100), quadrado(10, 10, 40, 40)], "#FF0000", "c")!;
  assert.deepEqual([t.x, t.w], [150, 50], "a letra espelhada: o pedaço da esquerda vai para a direita");
  assert.equal(t.contornos?.length, 1, "o furo vai em contornos");
  const f = tracoDoContorno(foto({ espelharX: true }), [quadrado(0, 0, 50, 100)], "#FF0000", "c", true)!;
  assert.deepEqual([f.x, f.w], [0, 50], "a foto não espelha de novo");
  assert.equal(tracoDoContorno(p, [], "#000000", "c"), null);
});
