import { describe, expect, it } from "vitest";
import {
  KENNISBANK_SEED,
  KENNIS_CATEGORIEEN,
  bouwKennisVoorstel,
  combineerKennisbank,
  keurVoorstelGoed,
  kennisCategorieLabels,
  kennisStatusLabels,
  sorteerKennis,
  valideerKennisOntwerp,
  zoekKennis,
  alleThemas,
  type KennisItem,
  type KennisOntwerp,
} from "./kennisbank";

function maakItem(deels: Partial<KennisItem>): KennisItem {
  return {
    id: "kennis-test",
    titel: "Testitem",
    inhoud: "Inhoud van het testitem.",
    categorie: "werkwijze",
    themas: ["test"],
    status: "gevalideerd",
    aangemaaktDoor: "notaris",
    versie: "1.0",
    datum: "2026-01-01",
    aangepast: "2026-01-01",
    ...deels,
  };
}

describe("kennisbank-seed", () => {
  it("heeft unieke, sprekende ids", () => {
    const ids = KENNISBANK_SEED.map((i) => i.id);
    expect(new Set(ids).size).toBe(ids.length);
    for (const id of ids) expect(id).toMatch(/^kennis-[a-z0-9-]+$/);
  });

  it("heeft volledige items met geldige categorie en status", () => {
    for (const item of KENNISBANK_SEED) {
      expect(item.titel.trim().length, item.id).toBeGreaterThan(0);
      expect(item.inhoud.trim().length, item.id).toBeGreaterThan(40);
      expect(KENNIS_CATEGORIEEN).toContain(item.categorie);
      expect(item.status).toBe("gevalideerd");
      expect(item.aangemaaktDoor).toBe("seed");
      expect(item.themas.length, item.id).toBeGreaterThan(0);
    }
  });

  it("bevat geen bedragen of tarieven (die leven in data/ereloon en data/belastingen)", () => {
    for (const item of KENNISBANK_SEED) {
      expect(item.inhoud, item.id).not.toMatch(/€|\d+\s*%|\d+\s*euro/i);
    }
  });

  it("dekt meerdere categorieën", () => {
    const categorieen = new Set(KENNISBANK_SEED.map((i) => i.categorie));
    expect(categorieen.size).toBeGreaterThanOrEqual(4);
  });
});

describe("zoekKennis", () => {
  it("zoekt accent- en hoofdletterongevoelig in titel, inhoud, themas en bronnen", () => {
    const items = [
      maakItem({ id: "a", titel: "Gewestelijke verschillen", themas: ["registratie"] }),
      maakItem({ id: "b", inhoud: "Ambtelijk voorbehoud én wilscontrole." }),
      maakItem({ id: "c", bronnen: ["art. 12 OWN"] }),
    ];
    expect(zoekKennis(items, "GEWESTELIJKE").map((i) => i.id)).toEqual(["a"]);
    expect(zoekKennis(items, "wilscontrole en").map((i) => i.id)).toEqual(["b"]);
    expect(zoekKennis(items, "own").map((i) => i.id)).toEqual(["c"]);
  });

  it("vereist dat élke zoekterm voorkomt en geeft alles terug bij een lege zoekterm", () => {
    const items = [maakItem({ id: "a", titel: "Opzoekingen in de afrekening" })];
    expect(zoekKennis(items, "opzoekingen afrekening")).toHaveLength(1);
    expect(zoekKennis(items, "opzoekingen onbestaand")).toHaveLength(0);
    expect(zoekKennis(items, "  ")).toHaveLength(1);
  });
});

describe("combineerKennisbank", () => {
  it("laat een opgeslagen item met hetzelfde id het seed-item vervangen", () => {
    const seedId = KENNISBANK_SEED[0].id;
    const bewerkt = maakItem({ id: seedId, titel: "Bewerkt door de notaris" });
    const samen = combineerKennisbank([bewerkt]);
    expect(samen.filter((i) => i.id === seedId)).toHaveLength(1);
    expect(samen.find((i) => i.id === seedId)?.titel).toBe("Bewerkt door de notaris");
    expect(samen.length).toBe(KENNISBANK_SEED.length);
  });

  it("voegt nieuwe opgeslagen items toe aan de seed", () => {
    const nieuw = maakItem({ id: "kennis-nieuw" });
    expect(combineerKennisbank([nieuw]).length).toBe(KENNISBANK_SEED.length + 1);
  });
});

describe("sorteerKennis", () => {
  it("zet te valideren eerst, dan gevalideerd, dan gearchiveerd; recentst aangepast bovenaan", () => {
    const items = [
      maakItem({ id: "oud", status: "gevalideerd", aangepast: "2026-01-01" }),
      maakItem({ id: "archief", status: "gearchiveerd", aangepast: "2026-06-01" }),
      maakItem({ id: "voorstel", status: "te_valideren", aangepast: "2026-02-01" }),
      maakItem({ id: "recent", status: "gevalideerd", aangepast: "2026-05-01" }),
    ];
    expect(sorteerKennis(items).map((i) => i.id)).toEqual([
      "voorstel",
      "recent",
      "oud",
      "archief",
    ]);
  });
});

describe("alleThemas", () => {
  it("geeft de unieke thema's alfabetisch terug", () => {
    const items = [
      maakItem({ id: "a", themas: ["gdpr", "akte"] }),
      maakItem({ id: "b", themas: ["akte", "bibliotheek"] }),
    ];
    expect(alleThemas(items)).toEqual(["akte", "bibliotheek", "gdpr"]);
  });
});

describe("agentvoorstellen", () => {
  const geldig: KennisOntwerp = {
    titel: "Nieuwe kantoorkennis",
    inhoud: "Een nuttige praktijkregel voor het kantoor.",
    categorie: "aandachtspunt",
    themas: ["Test", " akte "],
    motivering: "Kwam meermaals voor in dossiers.",
  };

  it("valideert verplichte velden en de categorie", () => {
    expect(valideerKennisOntwerp(geldig)).toEqual([]);
    expect(valideerKennisOntwerp({ ...geldig, titel: " " })).toContain("titel ontbreekt.");
    expect(valideerKennisOntwerp({ ...geldig, inhoud: "" })).toContain("inhoud ontbreekt.");
    expect(
      valideerKennisOntwerp({ ...geldig, categorie: "onzin" as KennisOntwerp["categorie"] })
    ).toEqual([
      `categorie "onzin" is ongeldig. Toegelaten: ${KENNIS_CATEGORIEEN.join(", ")}.`,
    ]);
    expect(valideerKennisOntwerp({ ...geldig, motivering: undefined })[0]).toMatch(
      /motivering ontbreekt/
    );
  });

  it("bouwt een voorstel dat altijd 'te_valideren' is en van een agent komt", () => {
    const item = bouwKennisVoorstel(geldig, "2026-07-06");
    expect(item.status).toBe("te_valideren");
    expect(item.aangemaaktDoor).toBe("agent");
    expect(item.id).toMatch(/^kennis-mcp-nieuwe-kantoorkennis-\d+$/);
    expect(item.themas).toEqual(["test", "akte"]);
    expect(item.datum).toBe("2026-07-06");
  });

  it("overschrijft een bestaand item nooit rechtstreeks: het voorstel leeft onder een eigen id", () => {
    const item = bouwKennisVoorstel({ ...geldig, itemId: "kennis-anonimisering" }, "2026-07-06");
    expect(item.id).not.toBe("kennis-anonimisering");
    expect(item.wijzigtItemId).toBe("kennis-anonimisering");
    expect(item.status).toBe("te_valideren");
  });

  it("past bij goedkeuring de inhoud toe op het bestaande id en ruimt het voorstelrecord op", () => {
    const voorstel = bouwKennisVoorstel({ ...geldig, itemId: "kennis-anonimisering" }, "2026-07-06");
    const { item, verwijderId } = keurVoorstelGoed(voorstel, "2026-07-07");
    expect(item.id).toBe("kennis-anonimisering");
    expect(item.status).toBe("gevalideerd");
    expect(item.wijzigtItemId).toBeUndefined();
    expect(item.aangepast).toBe("2026-07-07");
    expect(verwijderId).toBe(voorstel.id);
  });

  it("keurt een nieuw item goed onder zijn eigen id, zonder opruimactie", () => {
    const voorstel = bouwKennisVoorstel(geldig, "2026-07-06");
    const { item, verwijderId } = keurVoorstelGoed(voorstel, "2026-07-07");
    expect(item.id).toBe(voorstel.id);
    expect(item.status).toBe("gevalideerd");
    expect(verwijderId).toBeUndefined();
  });

  it("weigert een ongeldig ontwerp", () => {
    expect(() => bouwKennisVoorstel({ ...geldig, titel: "" }, "2026-07-06")).toThrow(
      /titel ontbreekt/
    );
  });
});

describe("labels", () => {
  it("heeft een label voor elke categorie en status", () => {
    for (const c of KENNIS_CATEGORIEEN) expect(kennisCategorieLabels[c]).toBeTruthy();
    for (const s of ["gevalideerd", "te_valideren", "gearchiveerd"] as const) {
      expect(kennisStatusLabels[s]).toBeTruthy();
    }
  });
});
