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

import {
  GRUPO_PADRAO,
  alternarEscolha,
  escolhidosComGrupo,
  grupoPendente,
  lerCusto,
  lerGrupos,
  lerServico,
  lerServicos,
  servicoComoOpcao,
  servicosDoProduto,
  somaDosServicos,
  vitrineDeServicos,
} from "./servicos-opcionais.ts";

/*
  O serviço opcional entra na venda como opção com preço. Se este arquivo
  quebrar, o estrago é silencioso: a arte é cobrada quinhentas vezes num
  pedido de quinhentas canetas, o cliente soma duas entregas no mesmo pedido,
  ou um serviço desligado num produto reaparece na vitrine.
*/

const ARTE = "3e9f6c97-77f8-41e3-bcc8-2cbe8c92cc93";
const REVISAO = "75e08e0f-650f-4c6e-8b2a-94764f562b26";
const NORMAL = "1419fb70-05a0-4319-9e27-29d435130f7a";
const EXPRESSA = "aa9e807b-b396-42d9-abb3-0685b2e58930";
const G_SERVICOS = "94308d78-e430-40e7-bb9e-8930a6e8474d";
const G_PRAZO = "48245638-4f62-4241-8087-1dbdb5869137";

const grupos = lerGrupos([
  { id: G_SERVICOS, name: "Serviços", selection_mode: "varias", position: 0 },
  { id: G_PRAZO, name: "Prazo", selection_mode: "uma", required: true, position: 1 },
]);

const servicos = lerServicos([
  { id: ARTE, group_id: G_SERVICOS, title: "Criação de arte", price: 79, icon: "arte", position: 0 },
  { id: REVISAO, group_id: G_SERVICOS, title: "Revisão", price: 19.9, position: 1 },
  { id: NORMAL, group_id: G_PRAZO, title: "Normal", price: 0, position: 0 },
  { id: EXPRESSA, group_id: G_PRAZO, title: "Expressa", price: 40, icon: "urgencia", position: 1 },
]);

const vitrine = vitrineDeServicos(grupos, servicos);
const caixaServicos = vitrine[0];
const caixaPrazo = vitrine[1];

test("serviço sem 'charge_per_unit' é cobrado uma vez", () => {
  const servico = lerServico({ id: ARTE, title: "Criação de arte", price: 79 })!;
  assert.equal(servico.porPeca, false);
  assert.equal(servicoComoOpcao(servico).price_applies_per_quantity, false);
});

test("só o true explícito cobra por peça", () => {
  assert.equal(lerServico({ id: ARTE, title: "Arte", charge_per_unit: true })!.porPeca, true);
  assert.equal(lerServico({ id: ARTE, title: "Arte", charge_per_unit: "sim" })!.porPeca, false);
});

test("serviço sem título não entra", () => {
  assert.equal(lerServico({ id: ARTE, title: "   " }), null);
  assert.equal(lerServicos([{ price: 10 }]).length, 0);
});

test("o (i) só existe quando o lojista escreveu o texto", () => {
  assert.equal(lerServico({ id: ARTE, title: "Arte" })!.detalhes, null);
  assert.equal(lerServico({ id: ARTE, title: "Arte", details: "Até 3 revisões" })!.detalhes, "Até 3 revisões");
});

test("o vínculo do produto vence o 'vale para todos'", () => {
  const biblioteca = lerServicos([
    { id: ARTE, title: "Arte", apply_to_all: true },
    { id: REVISAO, title: "Revisão", apply_to_all: false },
  ]);
  assert.deepEqual(servicosDoProduto(biblioteca, null).map((s) => s.id), [ARTE]);
  assert.deepEqual(servicosDoProduto(biblioteca, [{ service_id: ARTE, enabled: false }]).map((s) => s.id), []);
  assert.deepEqual(
    servicosDoProduto(biblioteca, [{ service_id: REVISAO, enabled: true }]).map((s) => s.id),
    [ARTE, REVISAO]
  );
});

test("serviço desligado na biblioteca não aparece nem ligado no produto", () => {
  const biblioteca = lerServicos([{ id: ARTE, title: "Arte", active: false }]);
  assert.equal(servicosDoProduto(biblioteca, [{ service_id: ARTE, enabled: true }]).length, 0);
});

test("a vitrine agrupa na ordem e nunca fica sem título", () => {
  assert.deepEqual(vitrine.map((c) => c.grupo.nome), ["Serviços", "Prazo"]);
  const semGrupo = vitrineDeServicos([], lerServicos([{ id: ARTE, title: "Arte" }]));
  assert.equal(semGrupo[0].grupo.nome, GRUPO_PADRAO);
  assert.equal(semGrupo[0].grupo.modo, "varias");
});

test("grupo de múltipla escolha soma escolhas", () => {
  let marcados = alternarEscolha([], caixaServicos.servicos[0], caixaServicos);
  marcados = alternarEscolha(marcados, caixaServicos.servicos[1], caixaServicos);
  assert.deepEqual(marcados, [ARTE, REVISAO]);
  assert.deepEqual(alternarEscolha(marcados, caixaServicos.servicos[0], caixaServicos), [REVISAO]);
});

test("grupo de escolha única troca em vez de somar", () => {
  const marcados = alternarEscolha([ARTE, NORMAL], caixaPrazo.servicos[1], caixaPrazo);
  assert.deepEqual(marcados, [ARTE, EXPRESSA]);
});

test("grupo obrigatório não deixa desmarcar a única escolha", () => {
  assert.deepEqual(alternarEscolha([EXPRESSA], caixaPrazo.servicos[1], caixaPrazo), [EXPRESSA]);
});

test("grupo de escolha única não obrigatório deixa desmarcar", () => {
  const solto = vitrineDeServicos(
    lerGrupos([{ id: G_PRAZO, name: "Prazo", selection_mode: "uma", required: false }]),
    lerServicos([{ id: EXPRESSA, group_id: G_PRAZO, title: "Expressa", price: 40 }])
  )[0];
  assert.deepEqual(alternarEscolha([EXPRESSA], solto.servicos[0], solto), []);
});

test("o pendente é o grupo obrigatório sem resposta", () => {
  assert.equal(grupoPendente(vitrine, [])?.grupo.nome, "Prazo");
  assert.equal(grupoPendente(vitrine, [NORMAL]), null);
  assert.equal(grupoPendente(vitrine, [ARTE]),  grupoPendente(vitrine, []));
});

test("a soma separa o que é por peça do que é uma vez", () => {
  const porPeca = vitrineDeServicos(
    grupos,
    lerServicos([
      { id: ARTE, group_id: G_SERVICOS, title: "Arte", price: 79 },
      { id: REVISAO, group_id: G_SERVICOS, title: "Gravação", price: 5, charge_per_unit: true },
    ])
  );
  assert.deepEqual(somaDosServicos(porPeca, [ARTE, REVISAO]), { porQuantidade: 5, umaVez: 79 });
});

test("a opção que vai ao pedido leva o nome do grupo", () => {
  const escolhidos = escolhidosComGrupo(vitrine, [EXPRESSA]);
  assert.deepEqual(servicoComoOpcao(escolhidos[0].servico, escolhidos[0].grupo.nome), {
    option_id: EXPRESSA,
    attribute_name: "Prazo",
    label: "Expressa",
    price: 40,
    price_applies_per_quantity: false,
    price_unit: "order",
    affects_stock: false,
  });
});

test("sem nome de grupo a opção ainda sai com um atributo", () => {
  const servico = lerServico({ id: ARTE, title: "Arte", price: 79 })!;
  assert.equal(servicoComoOpcao(servico, "  ").attribute_name, GRUPO_PADRAO);
});

test("serviço nunca baixa estoque de peça", () => {
  const servico = lerServico({ id: ARTE, title: "Arte", charge_per_unit: true })!;
  assert.equal(servicoComoOpcao(servico).affects_stock, false);
});

test("preço negativo ou sujo vira zero", () => {
  assert.equal(lerServico({ id: ARTE, title: "Arte", price: -50 })!.preco, 0);
  assert.equal(lerServico({ id: ARTE, title: "Arte", price: "muito" })!.preco, 0);
});

test("custo fora do formato vira 'sem custo declarado'", () => {
  assert.equal(lerCusto(null).tipo, "nenhum");
  assert.equal(lerCusto({ cost_kind: "magica" }).tipo, "nenhum");
  assert.equal(lerCusto({ cost_kind: "mao_de_obra", minutes: 40 }).minutos, 40);
});
