import { describe, expect, it } from "vitest";
import {
  AKTE_CHECKLISTS_SEED,
  GEWICHTEN,
  TRACK_CHANGES_WERKWIJZE,
  combineerChecklists,
  gewichtLabels,
  groepeerPuntenPerGewicht,
  vindChecklistVoorAkteType,
  type AkteChecklist,
} from "./kennisbank-checklists";
import { AKTETYPES } from "./modeldocumenten/akte-types";
import { seedOnderdelen } from "./modeldocumenten";

describe("akte-checklists-seed", () => {
  it("heeft unieke checklist-ids en unieke punt-ids per checklist", () => {
    const ids = AKTE_CHECKLISTS_SEED.map((c) => c.id);
    expect(new Set(ids).size).toBe(ids.length);
    for (const c of AKTE_CHECKLISTS_SEED) {
      const puntIds = c.punten.map((p) => p.id);
      expect(new Set(puntIds).size, c.id).toBe(puntIds.length);
    }
  });

  it("koppelt elke checklist aan een geregistreerd akteType", () => {
    for (const c of AKTE_CHECKLISTS_SEED) {
      expect(AKTETYPES, `${c.id}: akteType "${c.akteType}"`).toContain(c.akteType);
    }
  });

  it("is volledig: elke checklist dekt alle vier de gewichten met voldoende punten", () => {
    for (const c of AKTE_CHECKLISTS_SEED) {
      expect(c.punten.length, c.id).toBeGreaterThanOrEqual(15);
      for (const gewicht of GEWICHTEN) {
        expect(
          c.punten.filter((p) => p.gewicht === gewicht).length,
          `${c.id}: gewicht ${gewicht}`
        ).toBeGreaterThan(0);
      }
      // Cruciaal is het zwaartepunt van elke lijst.
      expect(
        c.punten.filter((p) => p.gewicht === "cruciaal").length,
        `${c.id}: cruciaal`
      ).toBeGreaterThanOrEqual(8);
    }
  });

  it("bevat geen bedragen of tarieven (die leven in data/ereloon en data/belastingen)", () => {
    for (const c of AKTE_CHECKLISTS_SEED) {
      for (const punt of c.punten) {
        const tekst = `${punt.omschrijving} ${punt.toelichting ?? ""}`;
        expect(tekst, `${c.id}/${punt.id}`).not.toMatch(/€|\d+\s*%|\d+\s*euro/i);
      }
    }
  });

  it("gebruikt enkel geldige gewestcodes", () => {
    for (const c of AKTE_CHECKLISTS_SEED) {
      for (const punt of c.punten) {
        for (const g of punt.gewesten ?? []) {
          expect(["vl", "br", "wa"], `${c.id}/${punt.id}`).toContain(g);
        }
      }
    }
  });

  it("elk gekoppeld modelonderdeelId bestaat effectief in de bibliotheek (punt 46)", () => {
    const onderdeelIds = new Set(seedOnderdelen.map((o) => o.id));
    for (const c of AKTE_CHECKLISTS_SEED) {
      for (const punt of c.punten) {
        if (punt.modelonderdeelId) {
          expect(onderdeelIds, `${c.id}/${punt.id} → ${punt.modelonderdeelId}`).toContain(punt.modelonderdeelId);
        }
      }
    }
  });

  it("koppelt een substantieel deel van de cruciale punten aan een modelonderdeel (punt 46)", () => {
    const cruciaal = AKTE_CHECKLISTS_SEED.flatMap((c) => c.punten).filter((p) => p.gewicht === "cruciaal");
    const gekoppeld = cruciaal.filter((p) => p.modelonderdeelId);
    expect(gekoppeld.length).toBeGreaterThanOrEqual(35);
  });

  it("dekt de kern van de dossierketen", () => {
    const akteTypes = AKTE_CHECKLISTS_SEED.map((c) => c.akteType);
    for (const verwacht of [
      "verkoopovereenkomst (compromis)",
      "verkoopakte",
      "kredietakte",
      "basisakte",
      "schenkingsakte",
      "aangifte-nalatenschap",
    ]) {
      expect(akteTypes).toContain(verwacht);
    }
  });
});

describe("track-changes-werkwijze", () => {
  it("heeft doorlopend genummerde stappen", () => {
    const stappen = TRACK_CHANGES_WERKWIJZE.stappen;
    expect(stappen.length).toBeGreaterThanOrEqual(5);
    stappen.forEach((s, i) => {
      expect(s.stap).toBe(i + 1);
      expect(s.titel.trim().length).toBeGreaterThan(0);
      expect(s.omschrijving.trim().length).toBeGreaterThan(40);
    });
  });

  it("schrijft een handeling voor bij elk gewicht", () => {
    for (const gewicht of GEWICHTEN) {
      expect(TRACK_CHANGES_WERKWIJZE.gewichtHandeling[gewicht].length).toBeGreaterThan(40);
    }
    // De kernregels: overbodig nooit stilzwijgend schrappen, cruciaal nooit laten liggen.
    expect(TRACK_CHANGES_WERKWIJZE.gewichtHandeling.overbodig).toMatch(/[Nn]ooit stilzwijgend/);
    expect(TRACK_CHANGES_WERKWIJZE.gewichtHandeling.cruciaal).toMatch(/escaleren/);
  });

  it("verankert de vaste spelregels", () => {
    const regels = TRACK_CHANGES_WERKWIJZE.spelregels.join(" ");
    expect(regels).toMatch(/track changes/i);
    expect(regels).toMatch(/nooit een 'schone' versie/i);
    expect(regels).toMatch(/\[AAN TE VULLEN/);
    expect(regels).toMatch(/\[VRAAG NOTARIS/);
    expect(regels).toMatch(/[Aa]nonimiseer/);
  });
});

describe("helpers", () => {
  it("groepeert punten per gewicht in de vaste volgorde", () => {
    const groepen = groepeerPuntenPerGewicht(AKTE_CHECKLISTS_SEED[0]);
    expect(groepen.map((g) => g.gewicht)).toEqual(GEWICHTEN);
    const totaal = groepen.reduce((som, g) => som + g.punten.length, 0);
    expect(totaal).toBe(AKTE_CHECKLISTS_SEED[0].punten.length);
  });

  it("laat een opgeslagen checklist het seed-exemplaar vervangen", () => {
    const seed = AKTE_CHECKLISTS_SEED[0];
    const bewerkt: AkteChecklist = { ...seed, titel: "Bewerkt door de notaris" };
    const samen = combineerChecklists([bewerkt]);
    expect(samen.length).toBe(AKTE_CHECKLISTS_SEED.length);
    expect(samen.find((c) => c.id === seed.id)?.titel).toBe("Bewerkt door de notaris");
  });

  it("vindt de checklist voor een akteType, ook voor sub-varianten via prefix", () => {
    expect(vindChecklistVoorAkteType("verkoopakte")?.id).toBe("checklist-verkoopakte");
    expect(vindChecklistVoorAkteType("verkoopovereenkomst (compromis) - heldere taal")?.id).toBe(
      "checklist-compromis"
    );
    expect(vindChecklistVoorAkteType("verkoopakte - wet breyne")?.id).toBe("checklist-verkoopakte");
    expect(vindChecklistVoorAkteType("KREDIETAKTE")?.id).toBe("checklist-kredietakte");
    expect(vindChecklistVoorAkteType("onbestaand-type")).toBeUndefined();
  });

  it("heeft een label voor elk gewicht", () => {
    for (const gewicht of GEWICHTEN) expect(gewichtLabels[gewicht]).toBeTruthy();
  });
});
