// ── Analyse-engine conventie-nazicht — gedragstests ──────────────────────────
// Toetst het hele contract van de engine op een realistisch extern compromis:
// vereiste punten die aanwezig zijn → in orde; ontbrekende → invoegen;
// verdachte formuleringen → schrappen/vervangen mét alinea-aanwijzing.

import { describe, expect, it } from "vitest";
import {
  analyseerConventie,
  effectieveKennisbank,
  kennisbankAkteTypesGeldig,
  normaliseerVoorDetectie,
  splitsAlineas,
  suggereerAkteType,
} from "./analyse";
import { KENNISBANK_CONTROLEPUNTEN } from "@/data/conventie-nazicht/kennisbank";
import type { Controlepunt } from "@/data/conventie-nazicht/types";

/** Extern ontvangen ontwerpcompromis (fictief, bewust met gekende gebreken). */
const EXTERN_COMPROMIS = [
  "ONDERHANDSE VERKOOPOVEREENKOMST",
  "Tussen de heer Jan Peeters, geboren te Leuven, rijksregisternummer 70.01.01-123.45, verkoper,",
  "en mevrouw Marie Dubois, geboren te Namen, koper, wordt overeengekomen wat volgt.",
  "Het goed, aan de koper welbekend, wordt verkocht in de staat waarin het zich bevindt.",
  "De koper betaalt bij ondertekening een voorschot van 10.000 euro rechtstreeks aan de verkoper.",
  "De verkoper wordt ontslagen van elke vrijwaring: de koper aanvaardt het goed zonder enig verhaal.",
  "Het energieprestatiecertificaat met kengetal 210 kWh/m² werd aan de koper overhandigd.",
  "De verkoper legt een bodemattest van de OVAM voor, waarvan de inhoud werd meegedeeld.",
  "Opgemaakt te Tervuren in twee originele exemplaren.",
].join("\n");

describe("analyseerConventie — compromis", () => {
  const rapport = analyseerConventie(EXTERN_COMPROMIS, "verkoopovereenkomst (compromis)");

  it("keurt aanwezige vereiste punten goed (EPC, bodemattest, identiteit)", () => {
    const inOrdeIds = rapport.inOrde.map((b) => b.punt.id);
    expect(inOrdeIds).toContain("verkoop-epc");
    expect(inOrdeIds).toContain("verkoop-bodemattest");
    expect(inOrdeIds).toContain("alg-identiteit-partijen");
    expect(rapport.inOrde.every((b) => b.actie === "geen")).toBe(true);
  });

  it("meldt ontbrekende vereiste punten als in te voegen clausule", () => {
    const stedenbouw = rapport.actiepunten.find((b) => b.punt.id === "verkoop-stedenbouw");
    expect(stedenbouw?.status).toBe("ontbreekt");
    expect(stedenbouw?.actie).toBe("invoegen");
    expect(stedenbouw?.punt.voorstel).toBeTruthy();
  });

  it("wijst verdachte formuleringen aan met de concrete alinea", () => {
    const voorschot = rapport.actiepunten.find((b) => b.punt.id === "compromis-voorschot-aan-verkoper");
    expect(voorschot?.status).toBe("aangetroffen");
    expect(voorschot?.actie).toBe("vervangen");
    expect(voorschot?.paragraafIndex).toBe(4);
    expect(voorschot?.fragment).toContain("rechtstreeks aan de verkoper");

    const exoneratie = rapport.actiepunten.find((b) => b.punt.id === "verkoop-exoneratie-verborgen-gebreken");
    expect(exoneratie?.paragraafIndex).toBe(5);

    const welbekend = rapport.actiepunten.find((b) => b.punt.id === "verkoop-welbekend");
    expect(welbekend?.paragraafIndex).toBe(3);
  });

  it("sorteert de actiepunten op zwaarte (te-vermijden eerst)", () => {
    expect(rapport.actiepunten[0].punt.categorie).toBe("te-vermijden");
    const volgorde = ["te-vermijden", "essentieel", "belangrijk", "beter"];
    const rangen = rapport.actiepunten.map((b) => volgorde.indexOf(b.punt.categorie));
    expect([...rangen].sort((a, b) => a - b)).toEqual(rangen);
  });

  it("telt per categorie getoetste punten en actiepunten coherent op", () => {
    for (const categorie of ["te-vermijden", "essentieel", "belangrijk", "beter"] as const) {
      const { getoetst, actieNodig } = rapport.telling[categorie];
      const geteld =
        rapport.actiepunten.filter((b) => b.punt.categorie === categorie).length;
      expect(actieNodig).toBe(geteld);
      expect(getoetst).toBeGreaterThanOrEqual(actieNodig);
    }
  });
});

describe("analyseerConventie — detectiesemantiek", () => {
  it("eist voor verdachte punten alle trefwoorden in dezélfde alinea", () => {
    const gesplitst = "De koper betaalt een voorschot van 10.000 euro.\nDe sleutels worden overhandigd aan de verkoper.";
    const rapport = analyseerConventie(gesplitst, "verkoopovereenkomst (compromis)");
    expect(rapport.actiepunten.some((b) => b.punt.id === "compromis-voorschot-aan-verkoper")).toBe(false);
  });

  it("zoekt hoofdletter- en accentongevoelig", () => {
    expect(normaliseerVoorDetectie("GEBOREN  Té")).toBe("geboren te");
    const rapport = analyseerConventie("DE PARTIJ, GEBOREN TE LEUVEN.", "volstrekt-onbekend-type");
    expect(rapport.inOrde.some((b) => b.punt.id === "alg-identiteit-partijen")).toBe(true);
  });

  it("toetst bij een onbekend akteType enkel de algemene punten", () => {
    const rapport = analyseerConventie("Lege tekst.", "volstrekt-onbekend-type");
    const alle = [...rapport.actiepunten, ...rapport.inOrde];
    expect(alle.length).toBeGreaterThan(0);
    expect(alle.every((b) => b.punt.akteTypes === "alle")).toBe(true);
  });

  it("bewaart lege regels bij het splitsen (render-contract)", () => {
    expect(splitsAlineas("a\r\n\r\nb")).toEqual(["a", "", "b"]);
  });
});

describe("suggereerAkteType", () => {
  it("herkent de geseede rechtshandelingen aan hun trefwoorden", () => {
    expect(suggereerAkteType("Bevestiging van een bankgift van 50.000 euro")).toBe(
      "schenkingsakte-roerend-goed"
    );
    expect(suggereerAkteType("De lastgever geeft volmacht aan de lasthebber")).toBe(
      "onderhandse volmacht"
    );
    expect(suggereerAkteType(EXTERN_COMPROMIS)).toBe("verkoopovereenkomst (compromis)");
  });

  it("geeft undefined zonder herkenbaar signaal", () => {
    expect(suggereerAkteType("Verslag van de vergadering van gisteren.")).toBeUndefined();
  });
});

describe("kennisbank-integriteit vanuit de engine", () => {
  it("verwijst enkel naar geregistreerde akteTypes", () => {
    expect(kennisbankAkteTypesGeldig()).toBe(true);
  });
});

describe("analyse met een effectieve (aangepaste) kennisbank", () => {
  it("valt zonder aanpassingen samen met de seed", () => {
    expect(effectieveKennisbank([])).toEqual(KENNISBANK_CONTROLEPUNTEN);
  });

  it("laat een geschrapt seed-punt uit de analyse verdwijnen", () => {
    const kennisbank = effectieveKennisbank([
      { id: "verkoop-bodemattest", geschrapt: true, aangepast: "x" },
    ]);
    const rapport = analyseerConventie("Een lege verkooptekst.", "verkoopovereenkomst (compromis)", kennisbank);
    const alle = [...rapport.actiepunten, ...rapport.inOrde];
    expect(alle.some((b) => b.punt.id === "verkoop-bodemattest")).toBe(false);
  });

  it("toetst een kantooreigen punt mee", () => {
    const eigen: Controlepunt = {
      id: "kantoor-mondeling-verbod",
      categorie: "te-vermijden",
      doel: "verdacht",
      akteTypes: ["verkoopovereenkomst (compromis)"],
      titel: "Mondelinge nevenafspraak",
      uitleg: "Geen mondelinge nevenafspraken.",
      signalen: [["mondelinge afspraak"]],
      vervanging: "[NAKIJKEN: mondelinge nevenafspraak schriftelijk vastleggen.]",
    };
    const kennisbank = effectieveKennisbank([
      { id: eigen.id, punt: eigen, aangepast: "x" },
    ]);
    const rapport = analyseerConventie(
      "Er geldt een mondelinge afspraak tussen partijen.",
      "verkoopovereenkomst (compromis)",
      kennisbank
    );
    expect(rapport.actiepunten.some((b) => b.punt.id === eigen.id)).toBe(true);
  });
});
