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

import {
  BRANCO,
  contraste,
  coresDoBanner,
  escaparHtml,
  familiaDaFonte,
  FORMATOS,
  htmlDoBanner,
  lerFormato,
  lerModelo,
  linkDasFontes,
  MODELOS,
  NOMES_DOS_FORMATOS,
  pesoDoTitulo,
  planoDoBanner,
  QUASE_PRETO,
  srcSeguro,
  tamanhoDoSelo,
  tamanhoQueCabe,
  textoSobre,
} from "./ferramentas/gerador-de-banner-modelos.ts";

test("formato: nomes, apelidos e padrão", () => {
  assert.equal(lerFormato(undefined), "carrossel");
  assert.equal(lerFormato(""), "carrossel");
  assert.equal(lerFormato("Carrossel"), "carrossel");
  assert.equal(lerFormato("faixa de secao"), "faixa de seção");
  assert.equal(lerFormato("FAIXA DE SEÇÃO"), "faixa de seção");
  assert.equal(lerFormato("carrossel vendor"), "carrossel vendor");
  assert.equal(lerFormato("16:5"), "carrossel vendor");
  assert.equal(lerFormato("banner duplo"), "banner duplo");
  assert.equal(lerFormato("Stories"), "story");
  assert.equal(lerFormato("post do instagram"), "quadrado");
  assert.equal(lerFormato("1080x1080"), "quadrado");
  assert.equal(lerFormato("hero"), "carrossel");
  assert.equal(lerFormato("outdoor"), null);
});

test("formato: tamanhos da loja", () => {
  assert.deepEqual([FORMATOS.carrossel.largura, FORMATOS.carrossel.altura], [1920, 540]);
  assert.deepEqual([FORMATOS["carrossel vendor"].largura, FORMATOS["carrossel vendor"].altura], [1920, 600]);
  assert.deepEqual([FORMATOS["banner duplo"].largura, FORMATOS["banner duplo"].altura], [1600, 1000]);
  assert.deepEqual([FORMATOS["faixa de seção"].largura, FORMATOS["faixa de seção"].altura], [1920, 240]);
  assert.deepEqual([FORMATOS.story.largura, FORMATOS.story.altura], [1080, 1920]);
  assert.deepEqual([FORMATOS.quadrado.largura, FORMATOS.quadrado.altura], [1080, 1080]);
});

test("modelo: nomes, apelidos e padrão", () => {
  assert.equal(lerModelo(undefined), "imagem à direita");
  assert.equal(lerModelo("imagem a direita"), "imagem à direita");
  assert.equal(lerModelo("Imagem de fundo com degrade"), "imagem de fundo com degradê");
  assert.equal(lerModelo("centralizado"), "centralizado");
  assert.equal(lerModelo("promoção"), "oferta com selo de desconto");
  assert.equal(lerModelo("minimalista"), "minimalista");
  assert.equal(lerModelo("barroco"), null);
  assert.equal(MODELOS.length, 5);
});

test("contraste: valores conhecidos da WCAG", () => {
  assert.equal(Math.round(contraste("#000000", "#FFFFFF")), 21);
  assert.equal(contraste("#777777", "#777777"), 1);
  assert.ok(contraste("#FFFFFF", "#767676") >= 4.5);
});

test("texto sobre o fundo: branco no forte, preto no claro", () => {
  assert.equal(textoSobre("#E11D48"), BRANCO);
  assert.equal(textoSobre("#1D4ED8"), BRANCO);
  assert.equal(textoSobre("#111827"), BRANCO);
  assert.equal(textoSobre("#FFD400"), QUASE_PRETO);
  assert.equal(textoSobre("#FFFFFF"), QUASE_PRETO);
  assert.equal(textoSobre("#A3E635"), QUASE_PRETO);
});

test("cores do banner: padrão pela cor da loja, sempre legível", () => {
  const forte = coresDoBanner({ modelo: "imagem à direita", corDaLoja: "#E11D48" });
  assert.equal(forte.fundo, "#E11D48");
  assert.equal(forte.texto, BRANCO);
  /* Botão branco com a letra legível (na cor da loja quando ela dá leitura); o amarelo de cartaz fica para a oferta. */
  assert.equal(forte.destaque, BRANCO);
  assert.ok(contraste(forte.destaque, forte.textoDoDestaque) >= 4.5);
  const azul = coresDoBanner({ modelo: "imagem à direita", corDaLoja: "#1D4ED8" });
  assert.equal(azul.textoDoDestaque, "#1D4ED8");
  const oferta = coresDoBanner({ modelo: "oferta com selo de desconto", corDaLoja: "#E11D48" });
  assert.equal(oferta.destaque, "#FFD400");
  assert.ok(contraste(oferta.destaque, oferta.textoDoDestaque) >= 4.5);

  /* Loja amarela: o destaque não pode ser amarelo de novo. */
  const amarela = coresDoBanner({ modelo: "oferta com selo de desconto", corDaLoja: "#FACC15" });
  assert.equal(amarela.texto, QUASE_PRETO);
  assert.ok(contraste(amarela.destaque, amarela.fundo) >= 3);

  /* Minimalista: fundo branco e a cor da loja escurecida até dar contraste. */
  const clara = coresDoBanner({ modelo: "minimalista", corDaLoja: "#FDE68A" });
  assert.equal(clara.fundo, BRANCO);
  assert.ok(contraste(clara.destaque, BRANCO) >= 3);

  /* Sem cor na loja: o roxo da Ktalogu. Cores escolhidas valem. */
  assert.equal(coresDoBanner({ modelo: "centralizado", corDaLoja: null }).fundo, "#6D28D9");
  const escolhida = coresDoBanner({ modelo: "centralizado", corDaLoja: "#E11D48", fundo: "#000", texto: "ff0", destaque: "#00FF00" });
  assert.deepEqual([escolhida.fundo, escolhida.texto, escolhida.destaque], ["#000000", "#FFFF00", "#00FF00"]);
});

test("fontes: nome do Google, peso e link sem pedir peso que não existe", () => {
  assert.equal(familiaDaFonte("source-serif-4"), "Source Serif 4");
  assert.equal(familiaDaFonte("dm-sans"), "DM Sans");
  assert.equal(familiaDaFonte("bebas-neue"), "Bebas Neue");
  assert.equal(pesoDoTitulo("anton"), 400);
  assert.equal(pesoDoTitulo("montserrat"), 700);
  const link = linkDasFontes(["anton", "inter", "inter"]);
  assert.ok(link.startsWith("https://fonts.googleapis.com/css2?"));
  assert.ok(link.includes("family=Anton&"));
  assert.ok(link.includes("family=Inter:wght@400;700"));
  assert.equal(link.match(/family=Inter/g)?.length, 1);
});

test("tamanho que cabe: texto longo diminui, curto respeita o máximo", () => {
  const curto = tamanhoQueCabe({ texto: "Oi", largura: 1000, maximo: 90, minimo: 30, linhas: 2, fator: 0.56 });
  assert.equal(curto, 90);
  const longo = tamanhoQueCabe({ texto: "Camisetas personalizadas para toda a sua equipe de vendas", largura: 1000, maximo: 90, minimo: 30, linhas: 2, fator: 0.56 });
  assert.ok(longo < 90 && longo >= 30);
  /* Duas linhas cabem: a estimativa (letras × fator × tamanho) não passa de duas larguras. */
  assert.ok(57 * 0.56 * longo <= 2 * 1000);
  /* Uma palavra enorme precisa caber numa linha só. */
  const palavra = tamanhoQueCabe({ texto: "Supercalifragilisticexpialidocious", largura: 600, maximo: 90, minimo: 10, linhas: 2, fator: 0.56 });
  assert.ok(34 * 0.56 * palavra <= 600);
  /* Nunca abaixo do mínimo (o resto fica com as reticências da página). */
  assert.equal(tamanhoQueCabe({ texto: "x".repeat(400), largura: 300, maximo: 90, minimo: 30, linhas: 2, fator: 0.56 }), 30);
});

test("selo: cabe no miolo, uma palavra numa linha", () => {
  const curto = tamanhoDoSelo("-30%", 300, "montserrat");
  const longo = tamanhoDoSelo("SUPERPROMOÇÃO!!", 300, "montserrat");
  assert.ok(longo < curto);
  assert.ok(4 * 0.56 * 1.16 * curto <= 300 * 0.62 + 1);
});

test("escapar: título com HTML vira texto", () => {
  assert.equal(escaparHtml(`<img src=x onerror="alert(1)">&'`), "&lt;img src=x onerror=&quot;alert(1)&quot;&gt;&amp;&#39;");
});

test("só imagem embutida (data:) entra no banner", () => {
  assert.equal(srcSeguro("https://exemplo.com/a.png"), null);
  assert.equal(srcSeguro("javascript:alert(1)"), null);
  assert.equal(srcSeguro('data:image/png;base64,AAA" onerror="x'), null);
  assert.equal(srcSeguro("data:image/svg+xml;base64,AAAA"), null);
  assert.equal(srcSeguro("data:image/webp;base64,UklGRg=="), "data:image/webp;base64,UklGRg==");
});

test("plano: nada sai do banner, em todo formato e modelo", () => {
  for (const nome of NOMES_DOS_FORMATOS) {
    const formato = FORMATOS[nome];
    for (const modelo of MODELOS) {
      for (const tipo of [null, "recorte", "transparente", "foto"] as const) {
        const plano = planoDoBanner(formato, modelo, tipo);
        const t = plano.texto;
        const onde = `${nome} / ${modelo} / ${tipo}`;
        assert.ok(t.x >= 0 && t.y >= 0 && t.w > 100 && t.h > 60, `caixa de texto ${onde}`);
        assert.ok(t.x + t.w <= formato.largura && t.y + t.h <= formato.altura, `texto dentro ${onde}`);
        if (tipo) {
          assert.ok(plano.imagem, `imagem ${onde}`);
          const i = plano.imagem!;
          assert.ok(i.x >= 0 && i.y >= 0 && i.x + i.w <= formato.largura && i.y + i.h <= formato.altura, `imagem dentro ${onde}`);
        }
      }
    }
  }
});

test("html: textos escapados, imagem só data:, sem endereço de fora além das fontes", () => {
  const html = htmlDoBanner({
    formato: "carrossel",
    modelo: "oferta com selo de desconto",
    titulo: `<script>alert("x")</script>`,
    subtitulo: "Tudo & mais",
    botao: "Comprar <já>",
    selo: "-30%",
    imagem: { src: "https://malicioso.example/a.png", tipo: "foto", largura: 10, altura: 10 },
    logo: { src: "data:image/webp;base64,UklGRg==", largura: 200, altura: 50 },
    cores: coresDoBanner({ modelo: "oferta com selo de desconto", corDaLoja: "#E11D48" }),
    fonteDoTitulo: "anton",
    fonteDoTexto: "inter",
  });
  assert.ok(!html.includes(`<script>alert("x")</script>`));
  assert.ok(html.includes("&lt;script&gt;alert(&quot;x&quot;)&lt;/script&gt;"));
  assert.ok(html.includes("Tudo &amp; mais"));
  assert.ok(html.includes("Comprar &lt;já&gt;"));
  assert.ok(!html.includes("malicioso.example"));
  const enderecos = html.match(/https?:\/\/[^\s"')]+/g) ?? [];
  assert.ok(enderecos.every((endereco) => endereco.startsWith("https://fonts.googleapis.com/")), enderecos.join(" "));
  assert.ok(html.includes("width:1920px") || html.includes("width:1920px;height:540px") || html.includes("html,body{width:1920px;height:540px"));
});

test("html: fonte desconhecida cai no padrão, sem entrar texto solto no CSS", () => {
  const html = htmlDoBanner({
    formato: "story",
    modelo: "centralizado",
    titulo: "Oi",
    cores: coresDoBanner({ modelo: "centralizado", corDaLoja: "#000000" }),
    fonteDoTitulo: "x'; } body { display:none",
  });
  assert.ok(html.includes("'Montserrat'"));
  assert.ok(!html.includes("display:none"));
});
