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

import {
  adicionarFotos,
  aplicarPedidosNosAtributos,
  assinaturaDaCombinacao,
  atributosDaFicha,
  camposDepoisDe,
  cobrancaDaOpcao,
  comoGuardarAFoto,
  custoDoProcesso,
  definirPrincipal,
  fotosDaFicha,
  limparFotosDasOpcoes,
  linhaDeInsumo,
  removerFotos,
  reordenarFotos,
  servicoLigado,
  variacoesParaSalvar,
  vinculosDepoisDe,
  type AtributoDaFicha,
  type FotoDaFicha,
} from "./ferramentas/produto-completo-regras.ts";
import { corDoTextoSobre, fundoValido, htmlDaImagemDoProduto } from "./ferramentas/imagem-do-produto-modelo.ts";
import { arquivoDaFoto } from "../produtos/servidor/arquivo-da-foto.ts";

let contador = 0;
const novoId = () => `00000000-0000-4000-8000-${String(++contador).padStart(12, "0")}`;

const foto = (id: string, ordem: number, principal = false): FotoDaFicha => ({ id, image_url: `https://x/${id}.webp`, sort_order: ordem, is_primary: principal });

test("ficha: atributos como a tela lê (saldo vivo e receita por cima do JSON, ordem pela posição)", () => {
  const atributos = atributosDaFicha(
    [
      { id: "a2", name: "Tamanho", position: 1, options: ["P"] },
      {
        id: "a1",
        name: "Cor",
        position: 0,
        options: [{ id: "o1", label: "Azul", price: 2, stock: 99, price_applies_per_quantity: false }],
        show_in_store: null,
      },
    ],
    [{ option_id: "o1", stock: 7, minimum_stock: 1, reorder_point: 2 }],
    [{ option_id: "o1", material_id: "m1", qty_per_piece: 3 }]
  );
  assert.deepEqual(atributos.map((a) => a.name), ["Cor", "Tamanho"]);
  const azul = atributos[0].options[0];
  assert.equal(azul.stock, 7);
  assert.equal(azul.minimum_stock, 1);
  assert.equal(azul.material_id, "m1");
  assert.equal(azul.material_qty, 3);
  assert.equal(azul.price_applies_per_quantity, false);
  assert.equal(atributos[0].show_in_store, true);
  /* Opção antiga em texto vira objeto sem id (a rota dá o id ao gravar). */
  assert.deepEqual(atributos[1].options[0], { label: "P", price: 0, is_required: false, price_applies_per_quantity: true });
});

test("ficha: galeria em ordem, com principal; sem galeria, a imagem antiga do produto", () => {
  const fotos = fotosDaFicha([{ id: "b", image_url: "u2", sort_order: 5 }, { id: "a", image_url: "u1", sort_order: 1 }, { id: "c", image_url: "" }], null, novoId);
  assert.deepEqual(fotos.map((f) => [f.id, f.sort_order, f.is_primary]), [["a", 0, true], ["b", 1, false]]);
  const antiga = fotosDaFicha([], "https://velha.png", () => "id-novo");
  assert.deepEqual(antiga, [{ id: "id-novo", image_url: "https://velha.png", sort_order: 0, is_primary: true }]);
});

test("galeria: adicionar, principal, reordenar e remover como o editor da tela", () => {
  let fotos: FotoDaFicha[] = [];
  fotos = adicionarFotos(fotos, [{ id: "f1", image_url: "u1" }, { id: "f2", image_url: "u2" }]);
  assert.deepEqual(fotos.map((f) => [f.id, f.sort_order, f.is_primary]), [["f1", 0, true], ["f2", 1, false]]);
  fotos = adicionarFotos(fotos, [{ id: "f3", image_url: "u3", principal: true }]);
  assert.equal(fotos.find((f) => f.is_primary)?.id, "f3");
  fotos = reordenarFotos(fotos, ["f3"]);
  assert.deepEqual(fotos.map((f) => f.id), ["f3", "f1", "f2"]);
  assert.deepEqual(fotos.map((f) => f.sort_order), [0, 1, 2]);
  fotos = definirPrincipal(fotos, "f2");
  assert.deepEqual(fotos.filter((f) => f.is_primary).map((f) => f.id), ["f2"]);
  fotos = removerFotos(fotos, ["f2"]);
  /* Saiu a principal: a primeira que fica vira a principal. */
  assert.deepEqual(fotos.map((f) => [f.id, f.sort_order, f.is_primary]), [["f3", 0, true], ["f1", 1, false]]);
  assert.deepEqual(removerFotos(fotos, ["f3", "f1"]), []);
});

test("galeria: opção que mostrava a foto que saiu perde o vínculo", () => {
  const atributos: AtributoDaFicha[] = [{ name: "Cor", options: [{ id: "o1", label: "Azul", price: 0, image_id: "f9" }, { id: "o2", label: "Verde", price: 0, image_id: "f1" }] }];
  const limpos = limparFotosDasOpcoes(atributos, [foto("f1", 0, true)]);
  assert.equal(limpos.mudou, true);
  assert.deepEqual(limpos.atributos[0].options.map((o) => o.image_id), [null, "f1"]);
  assert.equal(limparFotosDasOpcoes(limpos.atributos, [foto("f1", 0, true)]).mudou, false);
});

test("foto: a mesma decisão do navegador (lib/fotos/encolher.ts)", () => {
  assert.equal(comoGuardarAFoto({ formato: "gif", largura: 4000, altura: 4000, bytes: 9e6 }), "original");
  assert.equal(comoGuardarAFoto({ formato: "jpeg", largura: 1600, altura: 900, bytes: 300 * 1024 }), "original");
  assert.equal(comoGuardarAFoto({ formato: "jpeg", largura: 1600, altura: 900, bytes: 300 * 1024 + 1 }), "encolher");
  assert.equal(comoGuardarAFoto({ formato: "png", largura: 1601, altura: 10, bytes: 100 }), "encolher");
});

test("atributos: opção nova nasce com os campos do 'Adicionar opção' da tela, e ids antigos ficam", () => {
  const atuais: AtributoDaFicha[] = [{ id: "a1", name: "Cor", allow_multiple: false, is_required: false, position: 0, options: [{ id: "o1", label: "Azul", price: 0, position: 0 }] }];
  const r = aplicarPedidosNosAtributos(atuais, [{ nome: "cor", opcoes: [{ rotulo: "Verde", preco_adicional: 5, cor: "#1F9D55" }, { rotulo: "AZUL", novo_rotulo: "Azul royal" }] }], { unitType: "unit", fotos: [], novoId });
  assert.ok(r.ok);
  if (!r.ok) return;
  const [azul, verde] = r.atributos[0].options;
  assert.equal(azul.id, "o1");
  assert.equal(azul.label, "Azul royal");
  assert.equal(verde.label, "Verde");
  assert.equal(verde.price, 5);
  assert.equal(verde.swatch, "#1f9d55");
  assert.equal(verde.price_applies_per_quantity, true);
  assert.equal(verde.stock, 0);
  assert.equal(verde.position, 1);
  assert.equal(verde.image_id, null);
  assert.equal(r.opcoesNovas, 1);
  /* O original não é tocado. */
  assert.equal(atuais[0].options.length, 1);
  assert.equal(atuais[0].options[0].label, "Azul");
});

test("atributos: cobrança como a tela (m² usa price_unit; fora dele, só a chave da quantidade)", () => {
  const base: AtributoDaFicha[] = [{ name: "Acabamento", options: [{ id: "o1", label: "Ilhós", price: 3 }] }];
  const noM2 = aplicarPedidosNosAtributos(base, [{ nome: "Acabamento", opcoes: [{ rotulo: "Ilhós", cobrar_por: "pedido" }] }], { unitType: "square_meter", fotos: [], novoId });
  assert.ok(noM2.ok && noM2.atributos[0].options[0].price_unit === "order" && noM2.atributos[0].options[0].price_applies_per_quantity === false);
  const porPeca = aplicarPedidosNosAtributos(base, [{ nome: "Acabamento", opcoes: [{ rotulo: "Ilhós", cobrar_por: "pedido" }] }], { unitType: "unit", fotos: [], novoId });
  assert.ok(porPeca.ok && porPeca.atributos[0].options[0].price_unit === undefined && porPeca.atributos[0].options[0].price_applies_per_quantity === false);
  const medida = aplicarPedidosNosAtributos(base, [{ nome: "Acabamento", opcoes: [{ rotulo: "Ilhós", cobrar_por: "medida" }] }], { unitType: "unit", fotos: [], novoId });
  assert.equal(medida.ok, false);
  assert.equal(cobrancaDaOpcao({ price_unit: "measure" }, "unit"), "peça");
  assert.equal(cobrancaDaOpcao({ price_unit: null, price_applies_per_quantity: false }, "square_meter"), "pedido");
  assert.equal(cobrancaDaOpcao({}, "square_meter"), "medida");
});

test("atributos: remover, renomear, posição, foto e insumo", () => {
  const fotos = [foto("f1", 0, true), foto("f2", 1)];
  const atuais: AtributoDaFicha[] = [
    { name: "Cor", position: 0, options: [{ id: "o1", label: "Azul", price: 0 }, { id: "o2", label: "Verde", price: 0 }] },
    { name: "Tamanho", position: 1, options: [{ id: "o3", label: "P", price: 0 }] },
  ];
  const r = aplicarPedidosNosAtributos(
    atuais,
    [
      { nome: "Cor", opcoes: [{ rotulo: "Verde", remover: true }, { rotulo: "Azul", foto_numero: 2, insumo_id: "m1" }] },
      { nome: "Tamanho", novo_nome: "Medida", posicao: 1 },
    ],
    { unitType: "unit", fotos, novoId, insumos: new Set(["m1"]) }
  );
  assert.ok(r.ok);
  if (!r.ok) return;
  assert.deepEqual(r.atributos.map((a) => [a.name, a.position]), [["Medida", 0], ["Cor", 1]]);
  const azul = r.atributos[1].options[0];
  assert.equal(azul.image_id, "f2");
  assert.equal(azul.material_id, "m1");
  assert.equal(azul.material_qty, 1);
  assert.deepEqual(r.saem, [{ atributo: "Cor", opcao: "Verde", id: "o2" }]);
  const semInsumo = aplicarPedidosNosAtributos(r.atributos, [{ nome: "Cor", opcoes: [{ rotulo: "Azul", insumo_id: "nenhum" }] }], { unitType: "unit", fotos, novoId });
  assert.ok(semInsumo.ok && semInsumo.atributos[1].options[0].material_id === null && semInsumo.atributos[1].options[0].material_qty === 0);
  const tudo = aplicarPedidosNosAtributos(atuais, [{ nome: "Cor", remover: true }], { unitType: "unit", fotos, novoId });
  assert.ok(tudo.ok && tudo.atributos.length === 1 && tudo.saem.length === 2 && tudo.atributos[0].position === 0);
});

test("atributos: recusas (estoque de opção que existe, duplicado, foto inexistente, insumo de fora)", () => {
  const atuais: AtributoDaFicha[] = [{ name: "Cor", options: [{ id: "o1", label: "Azul", price: 0 }, { id: "o2", label: "Verde", price: 0 }] }];
  const ctx = { unitType: "unit", fotos: [foto("f1", 0, true)], novoId, insumos: new Set<string>(["m1"]) };
  assert.equal(aplicarPedidosNosAtributos(atuais, [{ nome: "Cor", opcoes: [{ rotulo: "Azul", estoque_inicial: 3 }] }], ctx).ok, false);
  assert.equal(aplicarPedidosNosAtributos(atuais, [{ nome: "Cor", opcoes: [{ rotulo: "Azul", novo_rotulo: "verde" }] }], ctx).ok, false);
  assert.equal(aplicarPedidosNosAtributos(atuais, [{ nome: "Cor", opcoes: [{ rotulo: "Azul", foto_numero: 4 }] }], ctx).ok, false);
  assert.equal(aplicarPedidosNosAtributos(atuais, [{ nome: "Cor", opcoes: [{ rotulo: "Azul", insumo_id: "m9" }] }], ctx).ok, false);
  assert.equal(aplicarPedidosNosAtributos(atuais, [{ nome: "Tamanho", remover: true }], ctx).ok, false);
  assert.equal(aplicarPedidosNosAtributos(atuais, [{ nome: "Cor", estilo: "carrossel" }], ctx).ok, false);
  const nova = aplicarPedidosNosAtributos(atuais, [{ nome: "Cor", opcoes: [{ rotulo: "Rosa", estoque_inicial: 4 }] }], ctx);
  assert.ok(nova.ok && nova.atributos[0].options[2].stock === 4);
});

test("combinações: refeitas no estoque por combinação, guardando código e saldo; nos outros casos, as gravadas", () => {
  const atributos: AtributoDaFicha[] = [
    { name: "Cor", options: [{ label: "Azul", price: 0 }, { label: "Verde", price: 0 }] },
    { name: "Tamanho", options: [{ label: "P", price: 0 }] },
  ];
  const atuais = [{ id: "v1", combination: { tamanho: "p", cor: "azul" }, sku: "AZ-P", price: 9, stock: 4, cost_adjustment: 1 }];
  const novas = variacoesParaSalvar(atributos, atuais, "variant");
  assert.deepEqual(novas, [
    { id: "v1", combination: { Cor: "Azul", Tamanho: "P" }, sku: "AZ-P", price: null, stock: 4, cost_adjustment: 0 },
    { id: undefined, combination: { Cor: "Verde", Tamanho: "P" }, sku: "", price: null, stock: 0, cost_adjustment: 0 },
  ]);
  assert.equal(variacoesParaSalvar(atributos, atuais, "option"), atuais);
});

test("serviços: o vínculo só existe quando o produto discorda da biblioteca", () => {
  const paraTodos = { id: "s1", titulo: "Arte", valeParaTodos: true, ativo: true };
  const soOnde = { id: "s2", titulo: "Montagem", valeParaTodos: false, ativo: true };
  assert.equal(servicoLigado(paraTodos, []), true);
  assert.deepEqual(vinculosDepoisDe([], paraTodos, "oferecer"), []);
  assert.deepEqual(vinculosDepoisDe([], paraTodos, "não oferecer"), [{ serviceId: "s1", ligado: false }]);
  assert.deepEqual(vinculosDepoisDe([], soOnde, "oferecer"), [{ serviceId: "s2", ligado: true }]);
  assert.deepEqual(vinculosDepoisDe([{ serviceId: "s2", ligado: true }], soOnde, "padrão"), []);
});

test("campos personalizados: criar, mudar, combinação e remover", () => {
  const r = camposDepoisDe([], [{ titulo: "Nome para gravar", tipo: "texto curto", obrigatorio: true, so_na_combinacao: "Tamanho: P, Cor: Azul" }], novoId);
  assert.ok(r.ok);
  if (!r.ok) return;
  assert.equal(r.campos[0].field_type, "text");
  assert.equal(r.campos[0].required, true);
  assert.equal(r.campos[0].variation_signature, "cor:azul|tamanho:p");
  assert.equal(assinaturaDaCombinacao("Cor: Azul"), "cor:azul");
  const mudado = camposDepoisDe(r.campos, [{ titulo: "nome para gravar", novo_titulo: "Nome", so_na_combinacao: "" }], novoId);
  assert.ok(mudado.ok && mudado.campos[0].title === "Nome" && mudado.campos[0].variation_signature === null && mudado.campos[0].id === r.campos[0].id);
  const fora = camposDepoisDe(r.campos, [{ titulo: "Nome para gravar", remover: true }], novoId);
  assert.ok(fora.ok && fora.campos.length === 0);
  assert.equal(camposDepoisDe([], [{ titulo: "X", tipo: "data" }], novoId).ok, false);
});

test("ficha técnica: as contas da aba Composição", () => {
  assert.deepEqual(linhaDeInsumo({ custoPorUnidade: 10, perdaPct: 10 }, { quantidade: 2 }), { consumption_qty: 2, waste_pct: 10, cost_total: 2 * 10 * (1 + 10 / 100), pieces_per_unit: null });
  const rendimento = linhaDeInsumo({ custoPorUnidade: 30, perdaPct: 0 }, { pecas_por_unidade: 3 });
  assert.equal(rendimento?.consumption_qty, 0.3333);
  assert.equal(rendimento?.pieces_per_unit, 3);
  assert.equal(linhaDeInsumo({ custoPorUnidade: 1, perdaPct: 0 }, {}), null);
  assert.equal(custoDoProcesso("MACHINE", 10, 5, 60), 15);
  assert.equal(custoDoProcesso("ROLE", 10, 5, 60), 10);
});

test("imagem do produto: HTML seguro, cor do texto e fundo conferido", () => {
  assert.equal(corDoTextoSobre("#FFFFFF"), "#141414");
  assert.equal(corDoTextoSobre("#6D28D9"), "#FFFFFF");
  assert.equal(fundoValido("red", "#FFFFFF"), "#FFFFFF");
  assert.equal(fundoValido("#abcdef", "#FFFFFF"), "#ABCDEF");
  const html = htmlDaImagemDoProduto({ modo: "cartão com nome", formato: "quadrado", fundo: "#6D28D9", imagem: null, titulo: "<b>Caneca</b>", subtitulo: "", fonte: "montserrat" });
  assert.ok(html.includes("&lt;b&gt;Caneca&lt;/b&gt;"));
  assert.ok(!html.includes("<b>Caneca"));
  const comFoto = htmlDaImagemDoProduto({
    modo: "foto em fundo",
    formato: "retrato",
    fundo: "#FFFFFF",
    imagem: { src: "javascript:alert(1)", tipo: "recorte", largura: 10, altura: 10 },
    titulo: "",
    subtitulo: "",
    fonte: "montserrat",
  });
  /* src fora de data: não entra: cai no cartão. */
  assert.ok(!comFoto.includes("javascript:"));
  assert.ok(comFoto.includes("height:1500px"));
});

test("exclusão: o arquivo da foto sai do endereço público ou do caminho guardado; foto de fora não é nossa", () => {
  assert.deepEqual(arquivoDaFoto({ image_url: "https://abc.supabase.co/storage/v1/object/public/products/products/x_1.webp" }), { bucket: "products", filePath: "products/x_1.webp" });
  assert.deepEqual(arquivoDaFoto({ path: "products/y.png", image_url: "https://outro" }), { bucket: "products", filePath: "products/y.png" });
  assert.equal(arquivoDaFoto({ image_url: "https://cdn.loja-antiga.com/foto.jpg" }), null);
  assert.equal(arquivoDaFoto({ image_url: null, url: null }), null);
});
