import { test } from "node:test";
import assert from "node:assert/strict";
import {
  camposParaLer,
  chaveDoRotulo,
  chaveUnica,
  conferirDefinicao,
  conferirFiltro,
  conferirValores,
  dataEscrita,
  formatarValor,
  lerValor,
  normalizarDefinicoes,
  numeroEscrito,
  valorCombina,
  valoresDaLinha,
  valoresQuePassamParaOPedido,
  type DefinicaoDeCampo,
} from "./campos-proprios.ts";

function campo(parcial: Partial<DefinicaoDeCampo> & Pick<DefinicaoDeCampo, "chave" | "tipo">): DefinicaoDeCampo {
  return {
    id: parcial.chave,
    entidade: "cliente",
    rotulo: parcial.chave,
    opcoes: [],
    obrigatorio: false,
    posicao: 0,
    ativo: true,
    ajuda: null,
    ...parcial,
  };
}

const aniversario = campo({ chave: "aniversario", rotulo: "Aniversário", tipo: "data" });
const indicou = campo({ chave: "quem_indicou", rotulo: "Quem indicou", tipo: "texto" });
const segmento = campo({ chave: "segmento", rotulo: "Segmento", tipo: "opcoes", opcoes: ["Gráfica", "Agência", "Varejo"] });
const tecnica = campo({ chave: "entrega_tecnica", rotulo: "Entrega técnica?", tipo: "sim_nao" });
const verba = campo({ chave: "verba", rotulo: "Verba", tipo: "moeda" });
const pessoas = campo({ chave: "pessoas", rotulo: "Pessoas", tipo: "numero" });

test("chave sai do nome, sem acento e começando por letra", () => {
  assert.equal(chaveDoRotulo("Quem indicou?"), "quem_indicou");
  assert.equal(chaveDoRotulo("  Aniversário  "), "aniversario");
  assert.equal(chaveDoRotulo("Entrega técnica?"), "entrega_tecnica");
  assert.equal(chaveDoRotulo("2ª via"), "campo_2_via");
  assert.equal(chaveDoRotulo("!!!"), "campo");
  assert.ok(chaveDoRotulo("x".repeat(90)).length <= 50);
});

test("chave repetida ganha número", () => {
  assert.equal(chaveUnica("Segmento", ["segmento"]), "segmento_2");
  assert.equal(chaveUnica("Segmento", ["segmento", "segmento_2"]), "segmento_3");
  assert.equal(chaveUnica("Novo", []), "novo");
});

test("definições vêm limpas e em ordem", () => {
  const lista = normalizarDefinicoes([
    { id: "b", entidade: "cliente", chave: "b", rotulo: "B", tipo: "texto", posicao: 2 },
    { id: "a", entidade: "cliente", chave: "a", rotulo: "A", tipo: "opcoes", opcoes: ["x", "X", " ", "y"], posicao: 1 },
    { id: "c", entidade: "pedido", chave: "c", rotulo: "C", tipo: "texto", posicao: 0 },
    { id: "d", entidade: "cliente", chave: "D-ruim", rotulo: "D", tipo: "texto" },
    { id: "e", entidade: "cliente", chave: "e", rotulo: "E", tipo: "inventado" },
  ], "cliente");
  assert.deepEqual(lista.map((d) => d.chave), ["a", "b"]);
  assert.deepEqual(lista[0].opcoes, ["x", "y"]);
});

test("conferir definição", () => {
  assert.equal(conferirDefinicao({ rotulo: "", tipo: "texto" }).ok, false);
  assert.equal(conferirDefinicao({ rotulo: "Segmento", tipo: "opcoes", opcoes: ["Só uma"] }).ok, false);
  assert.equal(conferirDefinicao({ rotulo: "X", tipo: "cor" }).ok, false);
  const certo = conferirDefinicao({ rotulo: " Segmento ", tipo: "opcoes", opcoes: "Gráfica\nAgência" });
  assert.ok(certo.ok && certo.definicao.rotulo === "Segmento" && certo.definicao.opcoes.length === 2);
});

test("número escrito por gente", () => {
  assert.equal(numeroEscrito("10,5"), 10.5);
  assert.equal(numeroEscrito("1.234,56"), 1234.56);
  assert.equal(numeroEscrito("R$ 12,50"), 12.5);
  assert.equal(numeroEscrito(7), 7);
  assert.equal(numeroEscrito("doze"), null);
  assert.equal(numeroEscrito("1,2,3"), null);
});

test("data em ISO ou no jeito brasileiro", () => {
  assert.equal(dataEscrita("2026-10-14"), "2026-10-14");
  assert.equal(dataEscrita("14/10/2026"), "2026-10-14");
  assert.equal(dataEscrita("5/3/1990"), "1990-03-05");
  assert.equal(dataEscrita("31/02/2026"), null);
  assert.equal(dataEscrita("ontem"), null);
});

test("cada tipo lê o seu valor", () => {
  assert.deepEqual(lerValor(indicou, "  Maria   Souza "), { ok: true, valor: "Maria Souza" });
  assert.deepEqual(lerValor(aniversario, "12/05/1990"), { ok: true, valor: "1990-05-12" });
  assert.deepEqual(lerValor(segmento, "grafica"), { ok: true, valor: "Gráfica" });
  assert.equal(lerValor(segmento, "Padaria").ok, false);
  assert.deepEqual(lerValor(tecnica, "Não"), { ok: true, valor: false });
  assert.deepEqual(lerValor(tecnica, true), { ok: true, valor: true });
  assert.equal(lerValor(tecnica, "talvez").ok, false);
  assert.deepEqual(lerValor(verba, "1.500,555"), { ok: true, valor: 1500.56 });
  assert.deepEqual(lerValor(pessoas, ""), { ok: true, valor: null });
  assert.equal(lerValor(indicou, "x".repeat(501)).ok, false);
});

test("conferir valores junta com os antigos e respeita campo desativado", () => {
  const desligado = campo({ chave: "antigo", tipo: "texto", ativo: false });
  const defs = [aniversario, indicou, { ...segmento, obrigatorio: true }, desligado];
  const r = conferirValores(defs, { aniversario: "12/05/1990", quem_indicou: "" }, { atuais: { quem_indicou: "Ana", antigo: "fica" }, exigirObrigatorios: true });
  assert.equal(r.ok, false);
  assert.deepEqual(r.valores, { antigo: "fica", aniversario: "1990-05-12" });
  assert.match(r.erros.segmento, /Preencha/);

  const r2 = conferirValores(defs, { antigo: "muda", inventado: 1 });
  assert.ok(r2.erros.antigo && r2.erros.inventado);
});

test("formatar para ler", () => {
  assert.equal(formatarValor(aniversario, "1990-05-12"), "12/05/1990");
  assert.equal(formatarValor(tecnica, true), "Sim");
  assert.equal(formatarValor(tecnica, false), "Não");
  assert.match(formatarValor(verba, 1500.5), /1\.500,50/);
  assert.equal(formatarValor(pessoas, null), "");
  const lidos = camposParaLer([aniversario, indicou], { aniversario: "1990-05-12", lixo: { a: 1 } });
  assert.deepEqual(lidos.map((c) => c.texto), ["12/05/1990"]);
  assert.deepEqual(valoresDaLinha({ ok: "x", "Ruim": 1, n: Infinity, b: false }), { ok: "x", b: false });
});

test("do orçamento para o pedido só o que tem campo igual", () => {
  const pedidoTecnica = { ...tecnica, entidade: "pedido" as const };
  const pedidoEvento = campo({ entidade: "pedido", chave: "evento", tipo: "texto" });
  const passa = valoresQuePassamParaOPedido([pedidoTecnica, pedidoEvento], { entrega_tecnica: true, evento: "Feira", outro: "x" }, { evento: "Já tinha" });
  assert.deepEqual(passa, { evento: "Já tinha", entrega_tecnica: true });
});

test("busca: aniversariantes do mês, faixa e texto", () => {
  const mes = conferirFiltro(aniversario, { mes: 5 });
  assert.ok(mes.ok);
  if (mes.ok) {
    assert.equal(valorCombina(aniversario, "1990-05-12", mes.filtro), true);
    assert.equal(valorCombina(aniversario, "1990-06-12", mes.filtro), false);
    assert.equal(valorCombina(aniversario, null, mes.filtro), false);
  }
  const faixa = conferirFiltro(verba, { de: "100", ate: "1.000" });
  assert.ok(faixa.ok);
  if (faixa.ok) {
    assert.equal(valorCombina(verba, 500, faixa.filtro), true);
    assert.equal(valorCombina(verba, 1500, faixa.filtro), false);
  }
  const contem = conferirFiltro(indicou, { contem: "joao" });
  assert.ok(contem.ok && valorCombina(indicou, "Seu João da esquina", contem.filtro));
  assert.equal(conferirFiltro(tecnica, { contem: "x" }).ok, false);
  assert.equal(conferirFiltro(indicou, { mes: 3 }).ok, false);
  const igual = conferirFiltro(segmento, { igual: "AGENCIA" });
  assert.ok(igual.ok && valorCombina(segmento, "Agência", igual.filtro));
  assert.equal(valorCombina(indicou, "", { preenchido: false }), true);
  assert.equal(valorCombina(indicou, "x", {}), true);
});
