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

import {
  avisosDoPreco,
  descricaoParaLer,
  emReais,
  lerFaixas,
  NOME_DA_UNIDADE,
  OPCOES_DE_VENDA,
  OPCOES_DO_PRAZO,
  quantidade,
  resumoDasMudancas,
  soOQueMuda,
  textoDaMargem,
  textoDasFaixas,
  textoDoPrazo,
  textoDoPreco,
  UNIDADES_DO_PRAZO,
  VALOR_MAXIMO,
  VENDIDO_POR,
} from "./ferramentas/regras-do-produto.ts";

/* Os espaços do formato brasileiro de moeda variam (espaço comum ou fino). */
const semEspaco = (texto: string) => texto.replace(/\s/g, " ");

test("unidade de venda: cada opção vira o valor que o banco aceita, e volta", () => {
  assert.deepEqual(OPCOES_DE_VENDA, ["unidade", "metro quadrado", "metro linear", "metro", "caixa", "resma"]);
  /* O banco só aceita estes seis (check de unit_type). */
  assert.deepEqual(Object.values(VENDIDO_POR).sort(), ["box", "linear_meter", "meter", "ream", "square_meter", "unit"]);
  for (const [nome, unidade] of Object.entries(VENDIDO_POR)) assert.equal(NOME_DA_UNIDADE[unidade], nome);
});

test("prazo de produção: as unidades que o banco aceita, no singular e no plural", () => {
  assert.deepEqual(OPCOES_DO_PRAZO, ["horas", "dias", "dias úteis", "semanas"]);
  assert.deepEqual(Object.values(UNIDADES_DO_PRAZO).sort(), ["business_days", "days", "hours", "weeks"]);
  assert.equal(textoDoPrazo(5, "business_days"), "5 dias úteis");
  assert.equal(textoDoPrazo(1, "business_days"), "1 dia útil");
  assert.equal(textoDoPrazo(1, "weeks"), "1 semana");
  assert.equal(textoDoPrazo(48, "hours"), "48 horas");
  assert.equal(textoDoPrazo(2.5, "days"), "2,5 dias");
  assert.equal(textoDoPrazo(0, "days"), null, "sem prazo");
  assert.equal(textoDoPrazo(null, "days"), null);
  assert.equal(textoDoPrazo(3, null), null);
  assert.equal(textoDoPrazo(3, "meses"), null, "unidade que o banco não aceita");
});

test("preço e margem como a pessoa lê", () => {
  assert.equal(semEspaco(textoDoPreco(35, "unit", false)), "R$ 35,00 por unidade");
  assert.equal(semEspaco(textoDoPreco(80.5, "square_meter", false)), "R$ 80,50 por metro quadrado");
  assert.equal(textoDoPreco(0, "unit", true), "sob consulta");
  assert.equal(semEspaco(textoDoPreco(10, null, false)), "R$ 10,00 por unidade", "sem unidade, é por unidade");
  assert.equal(semEspaco(emReais(1234.567)), "R$ 1.234,57");

  assert.equal(textoDaMargem(40, 10), "75%");
  assert.equal(textoDaMargem(30, 20), "33,3%");
  assert.equal(textoDaMargem(10, 15), "-50%", "margem negativa aparece como tal");
  assert.equal(textoDaMargem(0, 10), null, "sem preço não há margem");
  assert.equal(textoDaMargem(10, 0), null, "sem custo não há margem");
  assert.equal(quantidade(1000), "1000");
  assert.equal(quantidade(2.5), "2,5");
});

test("faixas de preço: o formato do assistente vira o do banco, em ordem", () => {
  const lidas = lerFaixas(
    [
      { a_partir_de: 500, preco_unitario: 0.9 },
      { a_partir_de: 100, ate: 499, preco_unitario: "1,20" },
    ],
    {}
  );
  assert.ok(lidas.ok);
  assert.deepEqual(
    lidas.faixas.map((faixa) => [faixa.min_qty, faixa.max_qty, faixa.unit_price]),
    [
      [100, 499, 1.2],
      [500, null, 0.9],
    ]
  );
  assert.deepEqual(textoDasFaixas(lidas.faixas).map(semEspaco), ["de 100 a 499: R$ 1,20 cada", "a partir de 500: R$ 0,90 cada"]);

  const vazia = lerFaixas([], {});
  assert.ok(vazia.ok && vazia.faixas.length === 0, "lista vazia tira todas as faixas");
});

test("faixas de preço: as mesmas recusas do painel", () => {
  const recusa = (faixas: Parameters<typeof lerFaixas>[0], limites = {}) => {
    const lidas = lerFaixas(faixas, limites);
    assert.equal(lidas.ok, false, JSON.stringify(faixas));
    return lidas.ok ? "" : lidas.erro;
  };
  assert.match(
    recusa([
      { a_partir_de: 100, ate: 600, preco_unitario: 1 },
      { a_partir_de: 500, preco_unitario: 0.9 },
    ]),
    /sobrepõem/
  );
  assert.match(recusa([{ a_partir_de: 0, preco_unitario: 1 }]), /maior que zero/);
  assert.match(recusa([{ a_partir_de: 10, preco_unitario: 0 }]), /preço unitário maior que zero/);
  assert.match(recusa([{ a_partir_de: 10, ate: 5, preco_unitario: 1 }]), /máximo menor que o mínimo/);
  assert.match(recusa([{ a_partir_de: 10, preco_unitario: VALOR_MAXIMO + 1 }]), /máximo/);
  /* A faixa não pode começar abaixo da quantidade mínima de venda. */
  assert.match(recusa([{ a_partir_de: 10, preco_unitario: 1 }], { min_sale_qty_enabled: true, min_sale_qty: 50 }), /abaixo da quantidade mínima/);
  assert.match(recusa([{ a_partir_de: 10, preco_unitario: 1 }], { max_sale_qty_enabled: true, max_sale_qty: 5 }), /acima da quantidade máxima/);
});

test("avisos do preço: faixa que começa em 1 e quantidades fixas mandam no preço", () => {
  const base = { preco: 39.9, sobConsulta: false, faixas: [] as Array<{ min_qty: number; active?: boolean }>, quantidadesFixas: false, mudouOPreco: true };
  assert.deepEqual(avisosDoPreco(base), []);
  assert.match(avisosDoPreco({ ...base, faixas: [{ min_qty: 1 }] })[0], /começa em 1 unidade/);
  assert.match(avisosDoPreco({ ...base, faixas: [{ min_qty: 100 }] })[0], /abaixo da primeira faixa/);
  assert.deepEqual(avisosDoPreco({ ...base, faixas: [{ min_qty: 1, active: false }] }), [], "faixa desligada não conta");
  assert.match(avisosDoPreco({ ...base, quantidadesFixas: true }).join(" "), /quantidades fixas/);
  assert.deepEqual(avisosDoPreco({ ...base, faixas: [{ min_qty: 1 }], mudouOPreco: false }), [], "sem mudar o preço, nada a avisar");
  assert.deepEqual(avisosDoPreco({ ...base, faixas: [{ min_qty: 1 }], sobConsulta: true }), []);
});

test("mudanças: só entra o que muda, e o resumo cabe numa linha", () => {
  const mudancas = soOQueMuda([
    { campo: "nome", antes: "Caneca", depois: "Caneca" },
    { campo: "preço", antes: "R$ 35,00 por unidade", depois: "R$ 39,90 por unidade" },
    { campo: "código", antes: null, depois: "" },
    { campo: "na loja virtual", antes: "não", depois: "sim" },
    { campo: "categoria", antes: null, depois: "Canecas" },
    { campo: "prazo de produção", antes: "3 dias úteis", depois: "5 dias úteis" },
  ]);
  assert.deepEqual(
    mudancas.map((mudanca) => mudanca.campo),
    ["preço", "na loja virtual", "categoria", "prazo de produção"],
    "igual não entra, e vazio é igual a nulo"
  );
  assert.equal(
    resumoDasMudancas(mudancas),
    "preço: R$ 35,00 por unidade → R$ 39,90 por unidade; na loja virtual: não → sim; categoria: vazio → Canecas e mais 1"
  );
  assert.equal(resumoDasMudancas(mudancas.slice(0, 1)), "preço: R$ 35,00 por unidade → R$ 39,90 por unidade");
});

test("descrição na prévia: sem marcação, com as quebras e com limite", () => {
  assert.equal(descricaoParaLer("<p>Caneca de <strong>porcelana</strong></p><p>325 ml &amp; alça</p>"), "Caneca de porcelana\n325 ml & alça");
  assert.equal(descricaoParaLer(""), null);
  assert.equal(descricaoParaLer(null), null);
  assert.equal(descricaoParaLer("<p> </p>"), null);
  const longa = descricaoParaLer(`<p>${"a".repeat(400)}</p>`, 300) ?? "";
  assert.equal(longa.length, 301);
  assert.ok(longa.endsWith("…"));
  assert.equal(descricaoParaLer("<script>alert(1)</script>texto"), "alert(1) texto", "nada de marcação chega ao assistente");
});
