import { test } from "node:test";
import assert from "node:assert/strict";
import {
  ORCAMENTO_DA_VIA,
  montarFolhas,
  montarHtmlDoProtocolo,
  paginarItens,
  type ProtocoloItem,
  type ProtocoloPedido,
} from "./protocolo-de-entrega.ts";

/*
  O papel que o cliente assina.

  O erro caro deste arquivo nao quebra tela nenhuma: ele SOME COM UM ITEM de uma
  folha que alguem ja assinou, ou faz o cliente assinar a folha 1 de 2 achando
  que recebeu tudo. Por isso os testes aqui insistem em duas coisas chatas —
  nenhum item se perde, e assinatura so na ultima folha.
*/

const item = (titulo: string, extras: Partial<ProtocoloItem> = {}): ProtocoloItem => ({
  titulo,
  quantity: 1,
  unit_type: "unit",
  ...extras,
});

const muitos = (quantos: number, extras: Partial<ProtocoloItem> = {}) =>
  Array.from({ length: quantos }, (_, i) => item(`Item ${i + 1}`, extras));

const pedido = (numero: number, itens: ProtocoloItem[]): ProtocoloPedido => ({
  numero,
  id: `id-${numero}`,
  empresa: { nome: "Metra", cnpj: "35742257000101" },
  cliente: { nome: "Cliente", documento: "12445212452" },
  itens,
});

/* ----------------------------------------------------- repartir em folhas */

test("pedido curto cabe numa folha so", () => {
  const paginas = paginarItens(muitos(5));
  assert.equal(paginas.length, 1);
  assert.equal(paginas[0].folha, 1);
  assert.equal(paginas[0].folhas, 1);
});

test("pedido sem item nenhum ainda gera uma folha", () => {
  /* Um protocolo sem item continua precisando existir: alguem assina o
     recebimento do que foi combinado fora da lista. Devolver zero folha
     deixaria o botao de imprimir sem fazer nada. */
  const paginas = paginarItens([]);
  assert.equal(paginas.length, 1);
  assert.deepEqual(paginas[0].itens, []);
});

test("item com variacao pesa mais, porque ocupa uma linha a mais", () => {
  const simples = paginarItens(muitos(ORCAMENTO_DA_VIA));
  assert.equal(simples.length, 1, "doze itens simples cabem numa folha");

  const comVariacao = paginarItens(muitos(ORCAMENTO_DA_VIA, { variacao: "Cor: preta" }));
  assert.ok(comVariacao.length > 1, "os mesmos doze com variacao nao cabem");
});

test("item por m² pesa mais, porque leva medida embaixo", () => {
  const porArea = muitos(ORCAMENTO_DA_VIA, {
    unit_type: "square_meter",
    quantity: 2,
    width_m: 1,
    height_m: 0.7,
  });
  assert.ok(paginarItens(porArea).length > 1);
});

test("NENHUM item se perde nem aparece duas vezes ao repartir", () => {
  /* A propriedade que importa mais que qualquer layout. */
  for (const total of [1, 7, 12, 13, 40, 99]) {
    const entrada = muitos(total, { variacao: "Cor: preta" });
    const paginas = paginarItens(entrada);
    const saida = paginas.flatMap((p) => p.itens);

    assert.equal(saida.length, total, `${total} itens entraram, ${saida.length} sairam`);
    assert.deepEqual(
      saida.map((i) => i.titulo),
      entrada.map((i) => i.titulo),
      "a ordem tambem tem que sobreviver"
    );
    for (const p of paginas) assert.equal(p.folhas, paginas.length);
  }
});

test("um item sozinho maior que o orcamento nao entra em loop nem some", () => {
  const paginas = paginarItens([item("Gigante", { variacao: "x" })], 0.5);
  assert.equal(paginas.length, 1);
  assert.equal(paginas[0].itens.length, 1);
});

/* --------------------------------------------------------- duas vias */

test("duas vias: a mesma pagina dos dois lados, uma de cada dono", () => {
  const folhas = montarFolhas([pedido(57, muitos(3))], "duas-vias");
  assert.equal(folhas.length, 1);
  assert.equal(folhas[0].esquerda?.via, "empresa");
  assert.equal(folhas[0].direita?.via, "cliente");
  assert.deepEqual(
    folhas[0].esquerda?.pagina.itens.map((i) => i.titulo),
    folhas[0].direita?.pagina.itens.map((i) => i.titulo)
  );
});

test("duas vias de um pedido longo: cada parte sai com as folhas dela", () => {
  const folhas = montarFolhas([pedido(57, muitos(20, { variacao: "Cor: preta" }))], "duas-vias");
  assert.ok(folhas.length > 1);

  for (const folha of folhas) {
    /* Os dois lados da MESMA folha mostram a MESMA pagina. Cortar ao meio
       entrega a folha 1 para cada um, nao a 1 para um e a 2 para o outro. */
    assert.equal(folha.esquerda?.pagina.folha, folha.direita?.pagina.folha);
    assert.equal(folha.esquerda?.via, "empresa");
    assert.equal(folha.direita?.via, "cliente");
  }
});

/* ----------------------------------------------------- varios pedidos */

test("uma via so: pedidos diferentes emendam, dois por folha", () => {
  const folhas = montarFolhas([pedido(1, muitos(2)), pedido(2, muitos(2))], "so-cliente");
  assert.equal(folhas.length, 1, "dois pedidos cabem numa folha");
  assert.equal(folhas[0].esquerda?.pedido.numero, 1);
  assert.equal(folhas[0].direita?.pedido.numero, 2);
});

test("numero impar de paginas deixa meia folha em branco", () => {
  /* Melhor meia folha branca que o comeco de outro pedido no mesmo papel que
     alguem vai cortar ao meio. */
  const folhas = montarFolhas([pedido(1, muitos(2)), pedido(2, muitos(2)), pedido(3, muitos(2))], "so-cliente");
  assert.equal(folhas.length, 2);
  assert.equal(folhas[1].esquerda?.pedido.numero, 3);
  assert.equal(folhas[1].direita, null);
});

test("cada modo carimba a via certa, porque o rotulo diz quem fica com o papel", () => {
  const doCliente = montarFolhas([pedido(1, muitos(1)), pedido(2, muitos(1))], "so-cliente");
  assert.equal(doCliente[0].esquerda?.via, "cliente");
  assert.equal(doCliente[0].direita?.via, "cliente");

  const daEmpresa = montarFolhas([pedido(1, muitos(1)), pedido(2, muitos(1))], "so-empresa");
  assert.equal(daEmpresa[0].esquerda?.via, "empresa");
  assert.equal(daEmpresa[0].direita?.via, "empresa");
});

test("uma via so gasta metade do papel de duas vias", () => {
  /* E a razao de o seletor existir: trinta entregas por dia sao quinze folhas
     em vez de trinta quando so interessa o canhoto que vai com a mercadoria. */
  const pedidos = [pedido(1, muitos(2)), pedido(2, muitos(2)), pedido(3, muitos(2)), pedido(4, muitos(2))];
  assert.equal(montarFolhas(pedidos, "duas-vias").length, 4);
  assert.equal(montarFolhas(pedidos, "so-cliente").length, 2);
});

/* ------------------------------------------------------------ o desenho */

test("as assinaturas aparecem so na ultima folha da via", () => {
  const html = montarHtmlDoProtocolo([pedido(57, muitos(20, { variacao: "Cor: preta" }))], "duas-vias");
  const assinaturas = html.split("Recebido por").length - 1;
  const continua = html.split("continua na folha seguinte").length - 1;

  assert.ok(continua > 0, "a folha do meio avisa que continua");
  /* Duas vias por folha: so a ultima folha traz as duas assinaturas. */
  assert.equal(assinaturas, 2);
});

test("o m² imprime a area de uma peca E o total", () => {
  const html = montarHtmlDoProtocolo(
    [pedido(57, [item("Banner", { unit_type: "square_meter", quantity: 2, width_m: 1, height_m: 0.7 })])],
    "duas-vias"
  );
  assert.ok(html.includes("1 m × 0,7 m"), "as dimensoes");
  assert.ok(html.includes("0,7 m²/cada"), "a area de uma peca");
  assert.ok(html.includes("1,4 m² total"), "o total entregue");
  assert.ok(html.includes("2 peças"), "peças, nunca 'un', em item medido");
});

test("NENHUM VALOR vai para o papel", () => {
  /* Quem recebe a mercadoria muitas vezes nao e quem comprou. Se algum dia
     alguem colar preco aqui, este teste cai. */
  const html = montarHtmlDoProtocolo(
    [pedido(57, [item("Chaveiro", { quantity: 3 }), item("Banner", { unit_type: "square_meter", quantity: 1, width_m: 2, height_m: 1 })])],
    "duas-vias"
  );
  assert.ok(!html.includes("R$"), "nenhum simbolo de dinheiro");
  assert.ok(!/\bTotal a pagar\b/i.test(html));
  assert.ok(!/\bunit_price\b/.test(html));
});

test("o documento escapa o que o cliente digitou", () => {
  const html = montarHtmlDoProtocolo(
    [
      {
        numero: 9,
        empresa: { nome: "Metra" },
        cliente: { nome: '<script>alert("x")</script>' },
        itens: [item("Item")],
      },
    ],
    "duas-vias"
  );
  assert.ok(!html.includes("<script>alert"), "nome de cliente nao vira script");
  assert.ok(html.includes("&lt;script&gt;"));
});

test("a folha sai deitada, com as duas metades em pe", () => {
  const html = montarHtmlDoProtocolo([pedido(1, muitos(2))], "duas-vias");
  assert.ok(html.includes("size: A4 landscape"));
  assert.ok(html.includes("grid-template-columns: 148.5mm 148.5mm"));
});
