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

import {
  acharCategoria,
  conferirOrdemPedida,
  corHex,
  descendentes,
  encaixar,
  larguraEfetiva,
  lerSecao,
  linkDoBanner,
  metadadosAntigos,
  modoPeloNome,
  montarRegraDePreco,
  novaListaDaSecao,
  novaOrdemDosProdutos,
  ordemDaPagina,
  ordenarComoALoja,
  placementNaOrdem,
  posicoesDasSecoes,
  posicoesDosProdutos,
  posicoesParaAFrente,
  PRODUTOS_INVENTADOS,
  slugDaCategoria,
  templateMostraSecoes,
  tipoGravado,
  TIPOS_DE_SECAO,
  type CategoriaBruta,
} from "./ferramentas/loja-estrutura-regras.ts";

/* ------------------------------------------------------------- tipos */

test("cada nome de tipo volta para o modo da tela", () => {
  assert.equal(TIPOS_DE_SECAO.length, 24); // 10 de antes + os 14 blocos novos (lib/widgets-da-home.ts)
  assert.equal(modoPeloNome("imagens lado a lado"), "image_grid");
  assert.equal(modoPeloNome("Carrossel de banners"), "banner_carousel");
  assert.equal(modoPeloNome("primeiros produtos"), "featured_products");
  assert.equal(modoPeloNome("mais vendidos"), "featured_products");
  assert.equal(modoPeloNome("categorias em círculos"), "category_circles");
  assert.equal(modoPeloNome("imagem"), "image_banner");
  assert.equal(modoPeloNome("produtos escolhidos"), "manual_products");
  assert.equal(modoPeloNome("Todos os Produtos"), "all_products");
  assert.equal(modoPeloNome("produtos de uma categoria"), "category_products");
  assert.equal(modoPeloNome("Produtos de uma SUBcategoria"), "subcategory_products");
  assert.equal(modoPeloNome("banner de imagem"), "image_banner");
  assert.equal(modoPeloNome("atalhos de categorias"), "category_navigator");
  assert.equal(modoPeloNome("image_banner"), "image_banner");
  assert.equal(modoPeloNome("navegação por categoria"), "category_navigator");
  assert.equal(modoPeloNome("qualquer coisa"), null);
});

test("section_type guarda só os dois valores antigos, como a tela", () => {
  assert.equal(tipoGravado("all_products"), "all_products");
  for (const modo of ["manual_products", "category_products", "subcategory_products", "image_banner", "category_navigator"] as const) {
    assert.equal(tipoGravado(modo), "manual_products");
  }
});

test("banner e atalhos ficam sempre na largura caixa", () => {
  assert.equal(larguraEfetiva("image_banner", "full"), "contained");
  assert.equal(larguraEfetiva("category_navigator", "full"), "contained");
  assert.equal(larguraEfetiva("manual_products", "full"), "full");
  /* O banner com formato (editor da página inicial) pode ir de ponta a ponta. */
  assert.equal(larguraEfetiva("image_banner", "full", { formato: "cinema" }), "full");
  assert.equal(larguraEfetiva("image_banner", "full", {}), "contained");
});

/* ------------------------------------------------- leitura da seção */

test("configuração antiga no título é lida e a coluna vale mais", () => {
  const cfg = encodeURIComponent(JSON.stringify({ mode: "category_products", target_category_id: "a", show_view_more: false }));
  const antigo = metadadosAntigos(`Ofertas[[KTCFG:${cfg}]]`);
  assert.equal(antigo.titulo, "Ofertas");
  assert.equal(antigo.config.target_category_id, "a");

  const lida = lerSecao({ id: "1", title: `Ofertas[[KTCFG:${cfg}]]`, config: { show_view_more: true }, section_type: "manual_products" });
  assert.equal(lida.titulo, "Ofertas");
  assert.equal(lida.modo, "category_products");
  assert.equal(lida.config.show_view_more, true);
  assert.equal(lida.legado, true);

  assert.equal(lerSecao({ id: "2", title: "x", config: null, section_type: "all_products" }).modo, "all_products");
  assert.equal(lerSecao({ id: "3", title: "x", config: "{\"mode\":\"image_banner\"}" }).modo, "image_banner");
  assert.equal(lerSecao({ id: "4", title: "x[[KTCFG:%%%]]" }).titulo, "x");
  assert.equal(lerSecao({ id: "5", title: "x", type: "manual" }).modo, "manual_products");
});

test("cor em hex vira minúscula com seis dígitos", () => {
  assert.equal(corHex("#ABC"), "#aabbcc");
  assert.equal(corHex("FF0000"), "#ff0000");
  assert.equal(corHex("vermelho"), null);
  assert.equal(corHex("#12345"), null);
});

/* ---------------------------------------------------- link do banner */

test("o banner só leva para a própria loja", () => {
  const lojas = ["ktalogu.com.br", "loja.aurora.com.br"];
  assert.deepEqual(linkDoBanner("/aurora/produto/caneca", lojas), { ok: true, link: "/aurora/produto/caneca" });
  assert.deepEqual(linkDoBanner("#produtos", lojas), { ok: true, link: "#produtos" });
  assert.equal(linkDoBanner("https://www.ktalogu.com.br/aurora", lojas).ok, true);
  assert.equal(linkDoBanner("https://loja.aurora.com.br/x", lojas).ok, true);
  assert.equal(linkDoBanner("https://golpe.com/pix", lojas).ok, false);
  assert.equal(linkDoBanner("//golpe.com/pix", lojas).ok, false);
  assert.equal(linkDoBanner("javascript:alert(1)", lojas).ok, false);
  assert.equal(linkDoBanner("https://antigo.com/x", lojas, "https://antigo.com/x").ok, true);
  assert.deepEqual(linkDoBanner("", lojas), { ok: true, link: "" });
});

/* ----------------------------------------------------- lugar na página */

test("sem a seção Todos os produtos a grade fica no fim", () => {
  const ordem = ordemDaPagina([
    { id: "a", modo: "manual_products", ativa: true },
    { id: "b", modo: "image_banner", ativa: false },
  ]);
  assert.deepEqual(ordem, ["a", PRODUTOS_INVENTADOS]);
});

test("encaixar acima, abaixo, no topo e por número", () => {
  const ordem = ["a", "p", "b"];
  assert.deepEqual(encaixar(ordem, "n", { lugar: "acima dos produtos" }, "p").ordem, ["a", "n", "p", "b"]);
  assert.deepEqual(encaixar(ordem, "n", { lugar: "abaixo dos produtos" }, "p").ordem, ["a", "p", "n", "b"]);
  assert.deepEqual(encaixar(ordem, "n", { lugar: "no topo" }, "p").ordem, ["n", "a", "p", "b"]);
  assert.deepEqual(encaixar(ordem, "n", { lugar: "no fim" }, "p").ordem, ["a", "p", "b", "n"]);
  assert.deepEqual(encaixar(ordem, "n", { posicao: 2, lugar: "no fim" }, "p").ordem, ["a", "n", "p", "b"]);
  assert.deepEqual(encaixar(ordem, "n", { posicao: 99 }, "p").ordem, ["a", "p", "b", "n"]);
  /* mover uma que já existe */
  assert.deepEqual(encaixar(ordem, "b", { lugar: "no topo" }, "p").ordem, ["b", "a", "p"]);
  assert.equal(placementNaOrdem(["a", "p", "n"], "n", "p"), "below");
  assert.equal(placementNaOrdem(["n", "a", "p"], "n", "p"), "above");
});

test("abaixo da grade inventada pede a grade de verdade", () => {
  const ordem = ["a", PRODUTOS_INVENTADOS];
  const acima = encaixar(ordem, "n", { lugar: "acima dos produtos" }, null);
  assert.deepEqual(acima.ordem, ["a", "n", PRODUTOS_INVENTADOS]);
  assert.equal(acima.precisaDaGradeDeVerdade, false);
  const abaixo = encaixar(ordem, "n", { lugar: "abaixo dos produtos" }, null);
  assert.deepEqual(abaixo.ordem, ["a", PRODUTOS_INVENTADOS, "n"]);
  assert.equal(abaixo.precisaDaGradeDeVerdade, true);
});

test("reorganizar exige todas as ativas uma vez", () => {
  assert.deepEqual(conferirOrdemPedida(["B", "produtos", "a"], ["a", "b"], null), { ok: true, ordem: ["b", PRODUTOS_INVENTADOS, "a"] });
  assert.deepEqual(conferirOrdemPedida(["b", "p", "a"], ["a", "b", "p"], "p"), { ok: true, ordem: ["b", "p", "a"] });
  assert.equal(conferirOrdemPedida(["a"], ["a", "b"], "a").ok, false);
  assert.equal(conferirOrdemPedida(["a", "a", "b"], ["a", "b"], "a").ok, false);
  assert.equal(conferirOrdemPedida(["a", "x"], ["a"], "a").ok, false);
});

test("posições das seções: página primeiro, desligadas depois", () => {
  const r = posicoesDasSecoes(["b", PRODUTOS_INVENTADOS, "a"], [
    { id: "a", position: 0 },
    { id: "b", position: 1 },
    { id: "off", position: 2 },
  ]);
  assert.deepEqual(r, [
    { id: "b", posicao: 0, mudou: true },
    { id: "a", posicao: 1, mudou: true },
    { id: "off", posicao: 2, mudou: false },
  ]);
});

/* ------------------------------------------------- produtos da seção */

test("produtos da seção: trocar, adicionar, remover e o limite", () => {
  assert.deepEqual(novaListaDaSecao(["a", "b"], { acao: "adicionar", ids: ["c", "a"] }), { ok: true, lista: ["a", "b", "c"], ignorados: ["a"] });
  assert.deepEqual(novaListaDaSecao(["a", "b"], { acao: "adicionar", ids: ["c"], posicao: 1 }), { ok: true, lista: ["c", "a", "b"], ignorados: [] });
  assert.deepEqual(novaListaDaSecao(["a", "b"], { acao: "remover", ids: ["a", "z"] }), { ok: true, lista: ["b"], ignorados: ["z"] });
  assert.deepEqual(novaListaDaSecao(["a"], { acao: "trocar", ids: ["X", "x", "y"] }), { ok: true, lista: ["x", "y"], ignorados: [] });
  const muitos = Array.from({ length: 61 }, (_, i) => `p${i}`);
  assert.equal(novaListaDaSecao([], { acao: "trocar", ids: muitos }).ok, false);
});

/* ------------------------------------------------- ordem dos produtos */

test("ordem dos produtos: para a frente e mover um", () => {
  assert.deepEqual(novaOrdemDosProdutos(["a", "b", "c", "d"], { inicio: ["c", "a"] }), { ok: true, ordem: ["c", "a", "b", "d"] });
  assert.deepEqual(novaOrdemDosProdutos(["a", "b", "c"], { mover: "c", para: 1 }), { ok: true, ordem: ["c", "a", "b"] });
  assert.deepEqual(novaOrdemDosProdutos(["a", "b", "c"], { mover: "a", para: 50 }), { ok: true, ordem: ["b", "c", "a"] });
  assert.equal(novaOrdemDosProdutos(["a"], { mover: "z" }).ok, false);
  assert.equal(novaOrdemDosProdutos(["a"], {}).ok, false);
});

test("posições dos produtos mexem no mínimo", () => {
  const atual = [
    { id: "a", posicao: 0 },
    { id: "b", posicao: 1 },
    { id: "c", posicao: 2 },
  ];
  /* para o topo: só ele, com um número antes do primeiro */
  assert.deepEqual(posicoesDosProdutos(atual, ["c", "a", "b"]), [{ id: "c", posicao: -1 }]);
  /* para o fim */
  assert.deepEqual(posicoesDosProdutos(atual, ["b", "c", "a"]), [{ id: "a", posicao: 3 }]);
  /* sem número livre no meio: renumera só os que mudaram */
  const meio = posicoesDosProdutos(
    [
      { id: "a", posicao: 0 },
      { id: "b", posicao: 1 },
      { id: "c", posicao: 2 },
      { id: "d", posicao: 3 },
    ],
    ["a", "d", "b", "c"]
  );
  assert.deepEqual(meio, [
    { id: "d", posicao: 1 },
    { id: "b", posicao: 2 },
    { id: "c", posicao: 3 },
  ]);
  /* com folga entre os vizinhos */
  assert.deepEqual(
    posicoesDosProdutos(
      [
        { id: "a", posicao: 0 },
        { id: "b", posicao: 10 },
        { id: "c", posicao: 20 },
      ],
      ["a", "c", "b"]
    ),
    [{ id: "c", posicao: 5 }]
  );
  /* sem posição gravada: numera tudo */
  assert.deepEqual(
    posicoesDosProdutos(
      [
        { id: "a", posicao: null },
        { id: "b", posicao: null },
      ],
      ["b", "a"]
    ),
    [
      { id: "b", posicao: 0 },
      { id: "a", posicao: 1 },
    ]
  );
  assert.deepEqual(posicoesDosProdutos(atual, ["a", "b", "c"]), []);
});

test("levar para a frente mexe só nos escolhidos", () => {
  const atual = [
    { id: "a", posicao: 0 },
    { id: "b", posicao: 0 },
    { id: "c", posicao: 3 },
  ];
  assert.deepEqual(posicoesParaAFrente(atual, ["c", "b"]), [
    { id: "c", posicao: -2 },
    { id: "b", posicao: -1 },
  ]);
  assert.equal(posicoesParaAFrente([{ id: "a", posicao: null }], ["a"]), null);
});

test("a ordem da loja: posição, sem posição no fim, mais novos primeiro", () => {
  const ordenados = ordenarComoALoja([
    { id: "x", catalog_position: null, created_at: "2026-01-02" },
    { id: "y", catalog_position: 1, created_at: "2026-01-01" },
    { id: "z", catalog_position: null, created_at: "2026-01-03" },
    { id: "w", catalog_position: 0, created_at: "2026-01-01" },
  ]);
  assert.deepEqual(ordenados.map((p) => p.id), ["w", "y", "z", "x"]);
});

/* ------------------------------------------------------- categorias */

const CATEGORIAS: CategoriaBruta[] = [
  { id: "r", name: "Roupas", slug: "roupas", parent_id: null, is_visible: true },
  { id: "c", name: "Camisetas", slug: "camisetas", parent_id: "r", is_visible: true },
  { id: "c2", name: "Camisetas", slug: "camisetas-2", parent_id: null, is_visible: false },
  { id: "g", name: "Gola V", slug: "gola-v", parent_id: "c", is_visible: true },
  { id: "b1", name: "Bonés", slug: "bones-1", parent_id: null, is_visible: true },
  { id: "b2", name: "Bonés", slug: "bones-2", parent_id: "r", is_visible: true },
];

test("acha categoria por id, slug, caminho e nome; nome repetido pede caminho", () => {
  assert.equal((acharCategoria("r", CATEGORIAS) as { categoria: CategoriaBruta }).categoria.id, "r");
  assert.equal((acharCategoria("gola-v", CATEGORIAS) as { categoria: CategoriaBruta }).categoria.id, "g");
  assert.equal((acharCategoria("Roupas / Camisetas", CATEGORIAS) as { categoria: CategoriaBruta }).categoria.id, "c");
  assert.equal((acharCategoria("roupas", CATEGORIAS) as { categoria: CategoriaBruta }).categoria.id, "r");
  /* o endereço é único: "camisetas" acha a primeira */
  assert.equal((acharCategoria("Camisetas", CATEGORIAS) as { categoria: CategoriaBruta }).categoria.id, "c");
  const repetida = acharCategoria("bones", CATEGORIAS);
  assert.equal(repetida.ok, false);
  assert.match((repetida as { erro: string }).erro, /2 categorias/);
  assert.equal(acharCategoria("Sapatos", CATEGORIAS).ok, false);
});

test("descendentes incluem a própria categoria e os netos", () => {
  assert.deepEqual([...descendentes("r", CATEGORIAS)].sort(), ["b2", "c", "g", "r"]);
});

test("slug da categoria igual ao do serviço", () => {
  assert.equal(slugDaCategoria("Canecas & Copos Térmicos"), "canecas-copos-termicos");
  assert.equal(slugDaCategoria("  Ação  "), "acao");
});

test("regra de preço junta o que mudou e confere as margens", () => {
  const r = montarRegraDePreco(null, { margem_alvo: 60 });
  assert.equal(r.ok && r.regra.marginTargetPct, 60);
  assert.equal(r.ok && r.regra.marginMinPct, 35);
  assert.equal(montarRegraDePreco(null, { margem_minima: 70 }).ok, false);
  assert.equal(montarRegraDePreco({ marginTargetPct: 50, marginMinPct: 30, rounding: { type: "NONE" } }, { centavos: 99 }).ok, false);
  const centavos = montarRegraDePreco({ marginTargetPct: 50, marginMinPct: 30, rounding: { type: "NONE" }, qtyTiers: [1] }, { arredondamento: "centavos", centavos: 49 });
  assert.equal(centavos.ok && centavos.regra.rounding.cents, 49);
  assert.deepEqual(centavos.ok && centavos.regra.qtyTiers, [1]);
});

test("só a família Vendor (Vendor e Boutique) esconde as seções", () => {
  assert.equal(templateMostraSecoes("vendor"), false);
  assert.equal(templateMostraSecoes("boutique"), false);
  /* O Vendor que publicou a página pelo editor mostra as seções. */
  assert.equal(templateMostraSecoes("vendor", true), true);
  assert.equal(templateMostraSecoes("boutique", true), true);
  for (const tema of ["default", "neo", "vitrine", null]) assert.equal(templateMostraSecoes(tema), true);
});
