import { test } from "node:test";
import assert from "node:assert/strict";
import {
  normalizar,
  celulaDaEscolha,
  desenharGrade,
  combinacoesParaAGrade,
  selecaoDaCombinacao,
  quantoDaParaFazer,
  custoBaseDaComposicao,
  origensDoMovimento,
  nomeDoMovimento,
  qtdComSinal,
  textoQuandoSai,
  mensagemDoErro,
  resumoDaGrade,
} from "./regras.ts";
import { aparecemNoV2, linhaDaComposicaoParaGravar } from "./gravar-composicao.ts";
import type { Celula } from "./tipos.ts";

function celula(v1: string | null, v2: string | null, saldo = 0, id?: string): Celula {
  return {
    celula_id: id ?? `${v1 ?? ""}-${v2 ?? ""}`,
    item_id: "item",
    nome: "Camiseta lisa",
    unidade: "un",
    papel: "insumo",
    valor1: v1,
    valor2: v2,
    rotulo: "",
    saldo,
    minimo: null,
    permite_negativo: true,
    controla: true,
  };
}

const GRADE = [
  { nome: "Tamanho", valores: ["P", "M", "G"] },
  { nome: "Cor", valores: ["Branca", "Preta"] },
];
const CELULAS = ["P", "M", "G"].flatMap((t) => ["Branca", "Preta"].map((c) => celula(t, c, 5)));

test("normalizar faz o mesmo que o banco", () => {
  assert.equal(normalizar("  Tamanho: "), "tamanho");
  assert.equal(normalizar("Côr  Azul"), "cor azul");
  assert.equal(normalizar(""), null);
  assert.equal(normalizar(null), null);
});

test("célula pelo nome do atributo", () => {
  const r = celulaDaEscolha(GRADE, CELULAS, [
    { atributo: "Cor", valor: "preta" },
    { atributo: "Tamanho:", valor: "g" },
    { atributo: "Personalização", valor: "Frente" },
  ]);
  assert.equal(r.como, "nome");
  assert.equal(r.celula?.celula_id, "G-Preta");
});

test("eixo sem atributo de mesmo nome usa a única escolha que existe no eixo", () => {
  const r = celulaDaEscolha(GRADE, CELULAS, [
    { atributo: "Tamanho", valor: "M" },
    { atributo: "Cor da peça", valor: "Branca" },
  ]);
  assert.equal(r.celula?.celula_id, "M-Branca");
});

test("falta escolher quando o valor não existe", () => {
  const r = celulaDaEscolha(GRADE, CELULAS, [{ atributo: "Tamanho", valor: "GG" }]);
  assert.equal(r.celula, null);
  assert.equal(r.como, "falta");
  assert.equal(r.falta, "tamanho e cor");
});

test("troca manual mais específica vence o nome", () => {
  const r = celulaDaEscolha(
    GRADE,
    CELULAS,
    [{ atributo: "Tamanho", valor: "P" }, { atributo: "Cor", valor: "Branca" }],
    [
      { combinacao: { Tamanho: "P" }, celula_id: "M-Preta" },
      { combinacao: { Tamanho: "P", Cor: "Branca" }, celula_id: "G-Branca" },
    ]
  );
  assert.equal(r.como, "manual");
  assert.equal(r.celula?.celula_id, "G-Branca");
});

test("item sem grade é a célula única", () => {
  const r = celulaDaEscolha([], [celula(null, null, 3, "unica")], []);
  assert.equal(r.celula?.celula_id, "unica");
  assert.equal(r.como, "unica");
});

test("grade desenhada em linhas e colunas", () => {
  const g = desenharGrade(GRADE, CELULAS);
  assert.equal(g.eixoDasLinhas, "Tamanho");
  assert.equal(g.eixoDasColunas, "Cor");
  assert.deepEqual(g.colunas, ["Branca", "Preta"]);
  assert.equal(g.linhas.length, 3);
  assert.equal(g.linhas[2].celulas[1].celula?.celula_id, "G-Preta");
  assert.equal(resumoDaGrade(GRADE), "Tamanho × Cor (6)");
});

test("célula com saldo fora da grade aparece à parte", () => {
  const g = desenharGrade([{ nome: "Tamanho", valores: ["P"] }], [celula("P", null, 1), celula("GG", null, 2)]);
  assert.equal(g.foraDaGrade.length, 1);
  assert.equal(g.foraDaGrade[0].valor1, "GG");
});

test("combinações só dos atributos da grade", () => {
  const combs = combinacoesParaAGrade(
    [
      { name: "Tamanho", options: [{ label: "P" }, { label: "M" }] },
      { name: "Cor", options: [{ label: "Branca" }] },
      { name: "Embrulho", options: [{ label: "Sim" }, { label: "Não" }] },
      { name: "Extras", allow_multiple: true, options: [{ label: "Laço" }] },
    ],
    GRADE
  );
  assert.deepEqual(combs, [
    { Tamanho: "P", Cor: "Branca" },
    { Tamanho: "M", Cor: "Branca" },
  ]);
});

test("seleção leva o id da opção", () => {
  const sel = selecaoDaCombinacao({ Cor: "Azul" }, [{ name: "Cor", options: [{ id: "o1", label: "azul" }] }]);
  assert.deepEqual(sel, { options: [{ attribute_name: "Cor", label: "Azul", option_id: "o1" }] });
});

test("quanto dá para fazer: usa o banco quando limita, senão saldo ÷ consumo", () => {
  assert.equal(quantoDaParaFazer({ disponivel: 7, itens: [], pedido: 1, motivo: null }), 7);
  assert.equal(
    quantoDaParaFazer({
      disponivel: null,
      pedido: 1,
      motivo: null,
      itens: [
        { saldo: 10, precisa: 1 } as never,
        { saldo: 0.5, precisa: 0.06 } as never,
      ],
    }),
    8
  );
  assert.equal(quantoDaParaFazer({ disponivel: 0, itens: [], pedido: 1, motivo: "falta_escolher" }), null);
});

test("custo base deixa de fora as linhas só-com-opção", () => {
  assert.equal(
    custoBaseDaComposicao([
      { quando_sai: "sempre", cost_total: 10 },
      { quando_sai: "pela_variacao", cost_total: 5 },
      { quando_sai: "se_opcao", cost_total: 3 },
      { cost_total: 1 },
    ]),
    16
  );
  assert.equal(textoQuandoSai({ quando_sai: "se_opcao", condicao_atributo: "Entrega", condicao_valor: "Caixa" }), "Só se Entrega = Caixa");
});

test("origem vira link", () => {
  const links = origensDoMovimento({ pedido_id: "p1", pedido_numero: 42, compra_id: "c1", compra_numero: 3, fornecedor_id: "f1" });
  assert.deepEqual(links.map((l) => [l.texto, l.href]), [
    ["Pedido #42", "/dashboard/orders/p1"],
    ["Compra #3", "/dashboard/fornecedores/meus-fornecedores/f1/compras"],
  ]);
  assert.equal(nomeDoMovimento("producao_saida", -2), "Produção: usado");
  assert.equal(nomeDoMovimento("producao_entrada", -2), "Produção desfeita");
  assert.equal(qtdComSinal(-3.5), "−3,5");
});

test("erro do banco chega em português", () => {
  assert.equal(mensagemDoErro({ message: "Estoque insuficiente.", hint: "ESTOQUE_INSUFICIENTE" }), "Estoque insuficiente.");
  assert.equal(mensagemDoErro(null, "x"), "x");
});

test("linha da composição: colunas novas só vão quando a empresa é v2", () => {
  const entrada = {
    quando_sai: "se_opcao",
    condicao_atributo: "Entrega",
    condicao_valor: "Caixa",
    qtd_por_variacao: [{ combinacao: { Tamanho: "P" }, quantidade: "0,06" }],
    celulas: [],
  };
  assert.deepEqual(linhaDaComposicaoParaGravar(entrada, false), {});
  const v2 = linhaDaComposicaoParaGravar(entrada, true);
  assert.equal(v2.quando_sai, "se_opcao");
  assert.equal(v2.condicao_valor, "Caixa");
  assert.deepEqual(v2.qtd_por_variacao, [{ combinacao: { Tamanho: "P" }, quantidade: 0.06 }]);
  assert.equal(linhaDaComposicaoParaGravar({ quando_sai: "qualquer" }, true).quando_sai, "sempre");
  assert.equal(aparecemNoV2.includes("quando_sai"), true);
});
