import { describe, it, expect } from "vitest";
import {
  DOSSIERFASEN,
  FASE_OVERGANGEN,
  volgendeFasen,
  magOvergaan,
  isEindfase,
  isAmbtelijkeFase,
  faseVanStatus,
  termijnStatus,
  vervaldatumNaMaanden,
  type Dossierfase,
} from "./levenscyclus";
import type { DossierStatus } from "./types";

// ── AUT-O4 · Dossier-levenscyclus-toestandsmachine ───────────────────────────

describe("toestandsmachine — structuur", () => {
  it("elke fase heeft een overgangsregel; alle doelen zijn geldige fasen", () => {
    for (const fase of DOSSIERFASEN) {
      expect(FASE_OVERGANGEN[fase]).toBeDefined();
      for (const doel of FASE_OVERGANGEN[fase]) expect(DOSSIERFASEN).toContain(doel);
    }
  });

  it("de keten is bereikbaar van intake tot afgesloten", () => {
    const bezocht = new Set<Dossierfase>();
    let grens: Dossierfase[] = ["intake"];
    while (grens.length) {
      const volgende: Dossierfase[] = [];
      for (const f of grens) {
        if (bezocht.has(f)) continue;
        bezocht.add(f);
        volgende.push(...volgendeFasen(f));
      }
      grens = volgende;
    }
    expect(bezocht.has("afgesloten")).toBe(true);
    // Alle fasen zijn bereikbaar vanaf intake.
    expect(bezocht.size).toBe(DOSSIERFASEN.length);
  });

  it("enkel 'afgesloten' is een eindfase", () => {
    expect(isEindfase("afgesloten")).toBe(true);
    expect(DOSSIERFASEN.filter(isEindfase)).toEqual(["afgesloten"]);
  });
});

describe("magOvergaan — bewaakte overgangen", () => {
  it("staat de lineaire hoofdweg toe", () => {
    expect(magOvergaan("intake", "compliance")).toBe(true);
    expect(magOvergaan("ontwerp", "nazicht-validatie")).toBe(true);
    expect(magOvergaan("nazicht-validatie", "ondertekening")).toBe(true);
  });

  it("laat terug-naar-ontwerp toe bij correcties tijdens het nazicht", () => {
    expect(magOvergaan("nazicht-validatie", "ontwerp")).toBe(true);
  });

  it("verbiedt een sprong die compliance of ondertekening overslaat", () => {
    expect(magOvergaan("intake", "ontwerp")).toBe(false); // compliance overgeslagen
    expect(magOvergaan("ontwerp", "formaliteiten")).toBe(false); // nazicht + ondertekening overgeslagen
  });
});

describe("ambtelijk voorbehoud", () => {
  it("markeert de ondertekening (het verlijden) als ambtelijke fase", () => {
    expect(isAmbtelijkeFase("ondertekening")).toBe(true);
    expect(isAmbtelijkeFase("ontwerp")).toBe(false);
  });
});

describe("faseVanStatus — coëxistentie met de bestaande DossierStatus", () => {
  it("mapt elke bestaande status op een geldige fase", () => {
    const statussen: DossierStatus[] = [
      "compromis-ontvangen",
      "opzoekingen-lopend",
      "ontwerp-in-opmaak",
      "ontwerp-verstuurd",
      "klaar-voor-akte",
      "verleden",
    ];
    for (const s of statussen) expect(DOSSIERFASEN).toContain(faseVanStatus(s));
    expect(faseVanStatus("klaar-voor-akte")).toBe("ondertekening");
    expect(faseVanStatus("verleden")).toBe("formaliteiten");
  });
});

describe("termijnbewaking — puur, tegen een referentiedatum", () => {
  it("verstreken / nabij / ok", () => {
    const t = { omschrijving: "compromis → akte", vervaldatum: "2026-09-12" };
    expect(termijnStatus(t, "2026-09-13")).toBe("verstreken");
    expect(termijnStatus(t, "2026-09-01")).toBe("nabij"); // binnen 14 dagen
    expect(termijnStatus(t, "2026-07-01")).toBe("ok");
  });

  it("vervaldatumNaMaanden: vier maanden na het compromis", () => {
    expect(vervaldatumNaMaanden("2026-05-12", 4)).toBe("2026-09-12");
  });

  it("vervaldatumNaMaanden klemt de dag binnen de doelmaand (31 jan + 1 → 28 feb)", () => {
    expect(vervaldatumNaMaanden("2026-01-31", 1)).toBe("2026-02-28");
  });
});
