import { test } from "node:test";
import assert from "node:assert/strict";
import { validar, type CorpoCompromisso } from "./validacao.ts";

/**
 * Regras do compromisso.
 *
 * Estas regras rodam nos dois lados — no formulario, para o aviso aparecer sem
 * ida a rede, e na rota, que e quem de fato protege o banco. Um erro aqui nao
 * aparece na tela: ele deixa passar dado ruim, ou recusa dado bom com uma
 * mensagem que a pessoa nao entende.
 */

const amanha = () => {
  const d = new Date();
  d.setDate(d.getDate() + 1);
  return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
};

const ontem = () => {
  const d = new Date();
  d.setDate(d.getDate() - 1);
  return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
};

const CATEGORIA = "8f14e45f-ceea-467a-9f4c-3b1e2c9a1234";

const corpo = (p: Partial<CorpoCompromisso> = {}): CorpoCompromisso => ({
  title: "Reunião",
  event_date: amanha(),
  category_id: CATEGORIA,
  ...p,
});

test("aceita um compromisso simples", () => {
  const r = validar(corpo());
  assert.equal(r.ok, true);
});

test("exige nome", () => {
  const r = validar(corpo({ title: "   " }));
  assert.equal(r.ok, false);
});

test("exige categoria válida", () => {
  assert.equal(validar(corpo({ category_id: "" })).ok, false);
  assert.equal(validar(corpo({ category_id: "nao-e-uuid" })).ok, false);
});

test("recusa agendar no passado, mas deixa registrar o que já aconteceu", () => {
  /* "Agendado" e o passado nao convivem. Concluido no passado e justamente
     como se registra algo que ja foi feito. */
  assert.equal(validar(corpo({ event_date: ontem() })).ok, false);
  assert.equal(validar(corpo({ event_date: ontem(), status: "concluido" })).ok, true);
  assert.equal(validar(corpo({ event_date: ontem(), status: "cancelado" })).ok, true);
});

test("fim precisa ser depois do início", () => {
  assert.equal(validar(corpo({ start_time: "14:00", end_time: "13:00" })).ok, false);
  assert.equal(validar(corpo({ start_time: "14:00", end_time: "14:00" })).ok, false);
  assert.equal(validar(corpo({ start_time: "14:00", end_time: "15:00" })).ok, true);
});

test("aceita HH:MM:SS que alguns navegadores mandam", () => {
  const r = validar(corpo({ start_time: "14:00:00" }));
  assert.equal(r.ok, true);
  if (r.ok) assert.equal(r.dados.start_time, "14:00");
});

test("repetição exige data final", () => {
  assert.equal(validar(corpo({ recurrence: "weekly" })).ok, false);
  assert.equal(validar(corpo({ recurrence: "weekly", recurrence_until: amanha() })).ok, true);
});

test("repetição não passa de 3 anos — pega ano digitado errado", () => {
  /* 2260 em vez de 2026 criaria uma serie que a expansao gera quase sem fim,
     em silencio. O teto e o que transforma isso em erro visivel. */
  const r = validar(corpo({ recurrence: "monthly", recurrence_until: "2260-01-01" }));
  assert.equal(r.ok, false);
  if (!r.ok) assert.match(r.erro, /3 anos/);
});

test("data final antes do início é recusada", () => {
  assert.equal(validar(corpo({ recurrence: "daily", recurrence_until: ontem() })).ok, false);
});

test("aceita só as antecedências que a tela oferece", () => {
  assert.equal(validar(corpo({ lembrete_minutos: 15 })).ok, true);
  assert.equal(validar(corpo({ lembrete_minutos: null })).ok, true);
  /* Numero livre viraria lembrete de tres anos antes, e o disparador varreria
     uma janela inutil a cada rodada. */
  assert.equal(validar(corpo({ lembrete_minutos: 7 })).ok, false);
  assert.equal(validar(corpo({ lembrete_minutos: 999999 })).ok, false);
});

test("cor precisa ser hexadecimal completo", () => {
  assert.equal(validar(corpo({ color: "#6C3CF0" })).ok, true);
  assert.equal(validar(corpo({ color: null })).ok, true);
  assert.equal(validar(corpo({ color: "roxo" })).ok, false);
  assert.equal(validar(corpo({ color: "#FFF" })).ok, false);
});

test("coordenada inválida é descartada, sem derrubar o compromisso", () => {
  /* Coordenada fora da faixa e dado ruim de fonte externa, nao erro de quem
     preencheu: o compromisso ainda precisa salvar. */
  const r = validar(corpo({ location_lat: 999, location_lng: -46.6 }));
  assert.equal(r.ok, true);
  if (r.ok) {
    assert.equal(r.dados.location_lat, null);
    assert.equal(r.dados.location_lng, -46.6);
  }
});

test("responsável só passa se for texto preenchido", () => {
  const comDono = validar(corpo({ responsible_user_id: "0ec4b4b6-e94b-4724-9dd2-4356ca0e574e" }));
  assert.equal(comDono.ok, true);
  if (comDono.ok) assert.equal(comDono.dados.responsible_user_id, "0ec4b4b6-e94b-4724-9dd2-4356ca0e574e");

  const semDono = validar(corpo({ responsible_user_id: "" }));
  assert.equal(semDono.ok, true);
  if (semDono.ok) assert.equal(semDono.dados.responsible_user_id, null);
});

test("não repete zera a data final", () => {
  const r = validar(corpo({ recurrence: "none", recurrence_until: amanha() }));
  assert.equal(r.ok, true);
  if (r.ok) assert.equal(r.dados.recurrence_until, null);
});
