// ── Deterministische hypothese-engine ────────────────────────────────────────
// Vertaalt gestructureerde, NIET-persoonsgebonden feiten over de rechtshandeling
// (het type goed, het gewest, krediet, het gebruik van het goed, …) naar de
// hypothesekeuzes (OntwerpKeuze[]) die bepalen welke varianten en facultatieve
// onderdelen in het modeldocument worden opgenomen.
//
// Kernprincipe: deze afleiding is DETERMINISTISCH en leeft in Notary.AI, niet in
// de AI-agent. De agent levert enkel de feiten aan (lokaal afgeleid uit de
// brondocumenten, via MCP); Notary.AI beslist daar zelf, reproduceerbaar, de
// passende hypotheses mee. Dezelfde engine voedt zowel de dossier-gestuurde
// flow (dossierNaarKeuzes) als de MCP-endpoint.
//
// Privacy: kenmerken zijn algemene feiten (booleans/enums), geen persoons-
// gegevens — ze mogen dus wél naar de Notary.AI-API/MCP.

import type { OntwerpKeuze } from "@/lib/handoff";
import type { Gewest } from "@/lib/context/gewest";
import { pasKenmerkRegelsToe, SCHENKING_REGELS, AKTE_REGELS, HELDERE_TAAL_REGELS } from "./kenmerken-regels";

/**
 * Sentinel-variantId die geen enkele variant selecteert (enkel de basistekst van
 * het onderdeel). Geen enkel modelonderdeel mag een variant met deze id hebben.
 */
export const GEEN_VARIANT = "__geen-variant__";

/** Type van het onroerend goed (bepaalt beschrijvings- en verzekeringsvariant). */
export type GoedKenmerkType = "woning" | "appartement" | "grond" | "ander";

/** Gebruik/genot van het goed bij overdracht. */
export type GebruikKenmerk = "vrij" | "koper-is-huurder" | "verhuurd";

/** Gestructureerde feiten die de hypothesekeuzes deterministisch bepalen. */
export interface Ontwerpkenmerken {
  /** Gewest van het goed (gewest-variant van de administratieve attesten). */
  gewest?: Gewest;
  /** Type onroerend goed. */
  goedType?: GoedKenmerkType;
  /** Wordt de aankoop (deels) met een hypothecair krediet gefinancierd? */
  metKrediet?: boolean;
  /** Gebruik/genot van het goed bij de overdracht. */
  gebruik?: GebruikKenmerk;
  /** Zijn er zonnepanelen op het goed? (clausule roerende goederen / zonnepanelen). */
  zonnepanelen?: boolean;
  /** Wordt het goed verpacht overgedragen? (pacht-clausule). */
  verpacht?: boolean;
  /**
   * Is de clausule "gezinswoning" van toepassing? (enkel wanneer een verkoper
   * in het goed gedomicilieerd is en zijn echtgeno(o)t(e)/wettelijk samen-
   * wonende partner zelf geen verkoper is). False → de volledige clausule,
   * titel inbegrepen, wordt weggelaten in plaats van als facultatief onderdeel
   * te blijven staan.
   */
  gezinswoningVanToepassing?: boolean;
  /**
   * Toestand van de keuring van de elektrische installatie (heldere-taal-PID-
   * clausule). Deterministisch af te leiden uit het keuringsverslag/attest.
   */
  elektrischeKeuring?: "geen-verslag" | "conform" | "niet-conform" | "vrijstelling" | "niet-van-toepassing";
  /**
   * Stand van de syndicus-informatie bij een kavel in mede-eigendom (heldere-taal-
   * clausule statuten van mede-eigendom). Enkel relevant bij een appartement.
   */
  syndicusInfo?: "geantwoord" | "geen-antwoord" | "geen-syndicus";
  /**
   * Overdracht van groenestroomcertificaten bij zonnepanelen (heldere-taal-
   * clausule reclamepanelen/zonnepanelen).
   */
  groenestroomcertificaten?: "met" | "zonder-zonder-subsidie" | "zonder-met-subsidie";
  /** Rust er een conventioneel voorkooprecht op het goed? */
  conventioneelVoorkooprecht?: boolean;
  /**
   * Is er al een EPC (Vlaanderen) opgesteld voor het goed? Deterministisch af
   * te leiden uit de aanwezigheid van EPC-attestgegevens in het dossier.
   * Stuurt, samen met goedType, welke hypothese van de heldere-taal-clausule
   * energieprestatie (ht-energieprestatie, Vlaamse variant) gekozen wordt.
   * Enkel voor woning/grond/ander — bij een appartement blijft de volledige
   * multi-hypothese open (de EPC-gemene-delen-hypothese vraagt een aparte,
   * niet zomaar automatiseerbare beoordeling).
   */
  epcAanwezig?: boolean;
  /**
   * Is er al een geldig asbestinventarisattest opgesteld voor het goed
   * (Vlaanderen)? Deterministisch af te leiden uit de aanwezigheid van
   * asbestattestgegevens in het dossier. Stuurt de hoofdhypothese van
   * ht-asbestinventarisattest-vlaanderen (voorrang op asbestBouwjaarRecent);
   * zonder deze waarde blijft de volledige keuze open, tenzij het bouwjaar
   * gekend is (zie asbestBouwjaarRecent).
   */
  asbestAanwezig?: boolean;
  /**
   * Is het bouwjaar van de (toegankelijke) constructies 2001 of recenter
   * (Vlaanderen)? Deterministisch af te leiden uit `goed.bouwjaar`. Kiest —
   * enkel wanneer er geen attest aanwezig is (asbestAanwezig) — de "niet
   * vereist"-hypothese van ht-asbestinventarisattest-vlaanderen. Onbekend of
   * false (bouwjaar vóór 2001 of ongekend) laat de volledige keuze open: een
   * ouder bouwjaar bewijst op zich niet dat een attest vereist is (de
   * 20 m²-drempel kan nog spelen), dus daar wordt niet automatisch "aanwezig
   * vereist" van afgeleid.
   */
  asbestBouwjaarRecent?: boolean;
  /**
   * Vlaanderen: P(erceel)- én G(ebouw)-score zijn beide "A" (geen overstroming
   * gemodelleerd) → de hypothese "het goed ligt niet in een overstromings-
   * gevoelig gebied" ligt deterministisch vast (variant
   * "vlaanderen-pg-score-erfgoed-geen-overstroming"). Elke andere of
   * onvolledige score laat dit kenmerk weg: dan blijft de generieke variant
   * met de volledige keuze staan (de aard van het gebied volgt niet uit de
   * score alleen).
   */
  overstromingVeilig?: boolean;
  /**
   * Rust er een WETTELIJK voorkooprecht, voorkeurrecht of recht van
   * wederinkoop op het goed? (te onderscheiden van het conventionele
   * voorkooprecht — zie conventioneelVoorkooprecht.) Stuurt de hoofdhypothese
   * (A/B) van ht-voorkooprecht-algemeen; zonder deze waarde blijft de
   * volledige A/B-keuze open.
   */
  wettelijkVoorkooprecht?: boolean;
  /**
   * Fiscaal regime van de verkoop (registratiebelasting/btw/gemengd). Stuurt
   * de hoofdhypothese (A/B/C) van ht-registratiebelasting-btw; onbekend laat
   * de volledige keuze open.
   */
  fiscaalRegime?: "registratiebelasting" | "btw" | "gemengd";
  /**
   * Bij mede-eigendom: betwist de verkoper het bedrag van de lasten/
   * achterstallen dat de syndicus meedeelt? Stuurt de hoofdhypothese (A/B) van
   * het voorrecht-onderdeel in ht-mede-eigendom-lasten-voorrecht; onbekend
   * laat de volledige keuze open.
   */
  medeEigendomLastenBetwist?: boolean;
  /** Schenking: behoudt de schenker het vruchtgebruik voor? (opnemen van de vruchtgebruik-clausule; false → weglaten). */
  voorbehoudVruchtgebruik?: boolean;
  /** Schenking: is (minstens) één begiftigde minderjarig? (aanvaarding namens de minderjarige; false → weglaten). */
  minderjarigeBegiftigde?: boolean;
  /** Schenking: schenkt een gehuwde schenker een eigen goed? (tussenkomst echtgenoot; false → weglaten). */
  schenkingEigenGoedDoorGehuwde?: boolean;
  /** Schenking: een last van hulp en bijstand opnemen? (false → weglaten). */
  lastHulpEnBijstand?: boolean;
  /** Schenking: zijn er meerdere begiftigden? (aanwasbeding tussen begiftigden; false → weglaten). */
  meerdereBegiftigden?: boolean;
  /** Schenking: conventioneel beding van terugkeer, of enkel het wettelijk recht. */
  rechtVanTerugkeer?: "conventioneel" | "wettelijk";
  /**
   * Laat (minstens) één partij zich bij de akte vertegenwoordigen (volmacht/
   * lasthebber)? true → vertegenwoordigingsclausule opnemen (de concrete
   * vertegenwoordigingshypothese kiest de notaris); false → clausule weglaten;
   * onbekend → clausule blijft als facultatief gemarkeerd staan.
   */
  partijVertegenwoordigd?: boolean;
  /**
   * Vlaanderen: uit de stedenbouwkundige inlichtingen/opzoekingen blijkt
   * UITDRUKKELIJK dat er geen onteigeningsmaatregel, rooilijn of inneming
   * geldt én dat het goed noch vastgesteld noch beschermd erfgoed is. Kiest —
   * enkel samen met overstromingVeilig — de volledig opgeloste variant van
   * ht-overstroming-rooilijn-erfgoed; anders blijven de A/B/C/D-keuzes open.
   */
  rooilijnOnteigeningErfgoedVrij?: boolean;
  /**
   * Vlaanderen: uit de opzoekingen blijkt UITDRUKKELIJK dat het goed geen bos
   * is, niet in een VEN/GEN ligt en geen natuurbeheerplan heeft, dat er geen
   * herstelvordering woonkwaliteit bestaat én dat het maatregelenregister
   * blanco is. Kiest de opgeloste variant van
   * ht-natuur-bos-herstelvordering-vlaanderen; anders blijven de keuzes open.
   */
  natuurBosHerstelVrij?: boolean;
  /**
   * "geen" = uitdrukkelijk verklaard/vastgesteld dat er geen stookolietank
   * aanwezig is of was — kiest de "geen stookolietank"-hypothese van de
   * stookolietank-clausules; elke andere toestand blijft een notariskeuze.
   */
  stookolietank?: "geen";
  /**
   * Vlaanderen: uit het dossier blijkt UITDRUKKELIJK dat het goed op geen
   * enkele lijst van ongeschiktheid/onbewoonbaarheid/verwaarlozing/leegstand
   * staat, dat er geen stookolietank aanwezig is of was, dat op het goed geen
   * bodemvervuilende activiteiten werden of worden uitgevoerd, én dat het goed
   * niet in een PFAS-no-regret-zone ligt. Kiest de opgeloste variant van
   * ht-leegstand-stookolie-bodem; anders blijven de A/B/C/D-keuzes open.
   */
  leegstandStookolieBodemVrij?: boolean;
}

const BESCHRIJVING_VARIANT: Record<GoedKenmerkType, string> = {
  woning: "huis",
  appartement: "appartement-mede-eigendom",
  grond: "grond",
  ander: "ander-goed-zonder-mede-eigendom",
};

// Een "ander" goed heeft geen eenduidige verzekeringshypothese → niet gemapt.
const RISICO_VERZEKERING_VARIANT: Partial<Record<GoedKenmerkType, string>> = {
  woning: "huis",
  appartement: "mede-eigendom",
  grond: "grond",
};

const GEWEST_ATTEST_VARIANT: Record<Gewest, string> = {
  Vlaanderen: "vlaanderen",
  Brussel: "brussel",
  Wallonië: "wallonie",
};

/**
 * Leidt de hypothesekeuzes deterministisch af uit de kenmerken. Onbekende
 * kenmerken leveren geen keuze op — dan blijven alle hypotheses staan ter keuze
 * door de notaris (nooit een blinde gok).
 */
export function kenmerkenNaarKeuzes(kenmerken: Ontwerpkenmerken): OntwerpKeuze[] {
  const keuzes: OntwerpKeuze[] = [];

  if (kenmerken.goedType) {
    // De verplichte beschrijvingsclausule bestaat als NL-onderdeel én als
    // Franstalige spiegel (-fr) met dezelfde variant-id's (parity). Stuur beide
    // aan, zodat zowel het NL- als het FR-(heldere-taal-)model de juiste
    // goedType-hypothese krijgt in plaats van alle hypotheses te tonen.
    const beschrijvingVariant = BESCHRIJVING_VARIANT[kenmerken.goedType];
    keuzes.push({ onderdeelId: "beschrijving-onroerend-goed", variantId: beschrijvingVariant });
    keuzes.push({ onderdeelId: "beschrijving-onroerend-goed-fr", variantId: beschrijvingVariant });
    const risico = RISICO_VERZEKERING_VARIANT[kenmerken.goedType];
    if (risico) keuzes.push({ onderdeelId: "risico-verzekering", variantId: risico });
    // Heldere-taal-compromis: brandverzekering naar goedType — woning = eigen
    // polis ("woonhuis"), appartement = collectieve blokpolis ("appartement").
    // Bij grond/ander geen automatische keuze (beide hypotheses blijven staan).
    const risicoHT =
      kenmerken.goedType === "woning" ? "woonhuis" : kenmerken.goedType === "appartement" ? "appartement" : undefined;
    if (risicoHT) {
      keuzes.push({ onderdeelId: "ht-risico-verzekering", variantId: risicoHT });
      keuzes.push({ onderdeelId: "ht-risico-verzekering-fr", variantId: risicoHT });
    }
    // Een kavel in mede-eigendom (appartement): neem de syndicus-/mede-eigendom-
    // clausule deterministisch op.
    if (kenmerken.goedType === "appartement") keuzes.push({ onderdeelId: "mede-eigendom-syndicus", opnemen: true });
  }

  if (kenmerken.gewest) {
    keuzes.push({ onderdeelId: "administratieve-attesten", variantId: GEWEST_ATTEST_VARIANT[kenmerken.gewest] });
  }

  // Eigendomsoverdracht en genot.
  if (kenmerken.gebruik === "koper-is-huurder") {
    keuzes.push({ onderdeelId: "eigendomsoverdracht-genot", variantId: "koper-is-huurder" });
  } else if (kenmerken.gebruik === "verhuurd") {
    // Het goed wordt verhuurd overgedragen: neem de verhuurd-clausule op en
    // laat de genot-hypothese open (de notaris bepaalt de modaliteit).
    keuzes.push({ onderdeelId: "verkoop-verhuurd-goed", opnemen: true });
  } else {
    keuzes.push({ onderdeelId: "eigendomsoverdracht-genot", variantId: "vrij-inbezitneming" });
  }

  if (kenmerken.metKrediet) {
    keuzes.push({ onderdeelId: "opschortende-voorwaarde-financiering", opnemen: true });
  }
  if (kenmerken.zonnepanelen) keuzes.push({ onderdeelId: "roerende-goederen-zonnepanelen", opnemen: true });
  if (kenmerken.verpacht) keuzes.push({ onderdeelId: "verkoop-verpacht-goed", opnemen: true });
  if (kenmerken.conventioneelVoorkooprecht) keuzes.push({ onderdeelId: "voorkooprechten-conventioneel", opnemen: true });
  // Uitdrukkelijk geen stookolietank aanwezig (of geweest): kies de
  // "geen stookolietank"-hypothese van de veiligheidsattesten-clausule.
  if (kenmerken.stookolietank === "geen") {
    keuzes.push({ onderdeelId: "veiligheidsattesten-stookolie-klim", variantId: "geen-stookolietank" });
    keuzes.push({ onderdeelId: "veiligheidsattesten-stookolie-klim-fr", variantId: "geen-stookolietank" });
  }

  // Declaratieve regeltabellen (zie kenmerken-regels.ts): de schenking-
  // clausules en de domeinoverstijgende akte-clausules, telkens NL én FR-
  // spiegel samen, met de drieledige true/false/onbekend-semantiek.
  keuzes.push(...pasKenmerkRegelsToe(kenmerken, SCHENKING_REGELS));
  keuzes.push(...pasKenmerkRegelsToe(kenmerken, AKTE_REGELS));

  return keuzes;
}

/**
 * Hypothesekeuzes voor de heldere-taal-compromismodellen (de ht-onderdelen),
 * voor NL én FR (dezelfde variant-id's, onderdeel-id met of zonder "-fr").
 * Maakt het heldere-taal-compromis tot een schone, gewest-bewuste standaard.
 */
export function heldereTaalKeuzes(kenmerken: Ontwerpkenmerken): OntwerpKeuze[] {
  const keuzes: OntwerpKeuze[] = [];
  // Verplicht onderdeel: kies enkel de variant (het onderdeel staat al aan).
  const beide = (basis: string, variantId: string) => {
    keuzes.push({ onderdeelId: `ht-${basis}`, variantId });
    keuzes.push({ onderdeelId: `ht-${basis}-fr`, variantId });
  };
  // Facultatief onderdeel: zet het expliciet aan/uit (en kies optioneel de
  // variant). Standaard staan facultatieve onderdelen UIT (standaardKeuzes), dus
  // enkel een variantId zetten zou ze niet doen verschijnen — vandaar opnemen.
  // conditieBevestigd: true bij elke opnemenBeide-aanroep — deze functie wordt
  // per definitie enkel aangeroepen wanneer het bepalende dossierfeit (gewest,
  // goedType, …) al gekend is, dus de generieke "facultatief — enkel van
  // toepassing indien …"-markering is dan overbodig geworden.
  const opnemenBeide = (basis: string, opnemen: boolean, variantId?: string) => {
    const extra = variantId ? { variantId } : {};
    keuzes.push({ onderdeelId: `ht-${basis}`, opnemen, conditieBevestigd: true, ...extra });
    keuzes.push({ onderdeelId: `ht-${basis}-fr`, opnemen, conditieBevestigd: true, ...extra });
  };

  // Wettelijk voorkooprecht/voorkeurrecht/recht van wederinkoop (§37): geldt
  // voor alle gewesten. Gekende waarde → deterministische hypothese; onbekend
  // → "open" (de volledige, ongewijzigde A/B-keuze, zoals vóór deze opsplitsing).
  beide(
    "voorkooprecht-algemeen",
    kenmerken.wettelijkVoorkooprecht === true ? "bestaat" : kenmerken.wettelijkVoorkooprecht === false ? "geen" : "open"
  );

  // Registratiebelasting/btw (§43): geldt voor alle gewesten. Gekend regime →
  // deterministische hypothese; onbekend → "open" (de volledige A/B/C-keuze).
  beide("registratiebelasting-btw", kenmerken.fiscaalRegime ?? "open");

  if (kenmerken.gewest) {
    const g = kenmerken.gewest;
    // Vlaanderen: bij een reeds bestaand EPC (en geen appartement — de EPC-
    // gemene-delen-hypothese vraagt een aparte beoordeling) ligt de hypothese
    // "EPC bestaat" onmiskenbaar vast; anders blijft de volledige multi-
    // hypothese open (nieuwbouw/geen EPC/niet-residentieel zijn niet uit elkaar
    // te houden zonder verdere gegevens).
    const vlaanderenEpcVariant =
      kenmerken.epcAanwezig && kenmerken.goedType !== "appartement" ? "vlaanderen-epc-bestaat" : "vlaanderen-epc";
    beide("energieprestatie", g === "Vlaanderen" ? vlaanderenEpcVariant : g === "Brussel" ? "brussel-peb" : "wallonie-peb");
    beide("stedenbouw-splitsing", g === "Vlaanderen" ? "vlaanderen-vcro" : g === "Brussel" ? "brussel-bwro" : "wallonie-codt");
    // Vlaanderen: bij P- én G-score "A" ligt "ligt niet in overstromings-
    // gevoelig gebied" vast → specifieke variant (zelfde patroon als het EPC);
    // anders (score onbekend of B/C/D) blijft de generieke variant met de
    // volledige keuze staan.
    beide(
      "overstroming-rooilijn-erfgoed",
      g === "Vlaanderen"
        ? kenmerken.overstromingVeilig === true && kenmerken.rooilijnOnteigeningErfgoedVrij === true
          ? "vlaanderen-vrij-van-maatregelen"
          : kenmerken.overstromingVeilig === true
            ? "vlaanderen-pg-score-erfgoed-geen-overstroming"
            : "vlaanderen-pg-score-erfgoed"
        : g === "Brussel"
          ? "brussel-overstroming-erfgoed"
          : "wallonie-overstroming-erfgoed"
    );
    beide(
      "leegstand-stookolie-bodem",
      g === "Vlaanderen"
        ? kenmerken.leegstandStookolieBodemVrij === true
          ? "vlaanderen-leegstand-stookolie-bodem-vrij"
          : "vlaanderen-leegstand-stookolie-bodem"
        : g === "Brussel"
          ? "brussel-milieu-stookolie-bodem"
          : "wallonie-stookolie-bodem-certibeau"
    );
  }

  if (kenmerken.gebruik === "koper-is-huurder") beide("gebruik-genot", "koper-is-huurder");
  else if (kenmerken.gebruik === "verhuurd") beide("gebruik-genot", kenmerken.gewest === "Brussel" ? "verhuurd-aan-derde-brussel" : "verhuurd-aan-derde");
  else if (kenmerken.gebruik === "vrij") beide("gebruik-genot", "vrij-of-verkoper-gebruikt");

  // Opschortende voorwaarde van krediet (§5, facultatief): aan zodra gekend of
  // er met krediet wordt gefinancierd; anders de "geen opschortende voorwaarde"-
  // variant, zodat de clausule sluitend is.
  if (kenmerken.metKrediet !== undefined) {
    opnemenBeide("opschortende-voorwaarde-krediet", true, kenmerken.metKrediet ? "geen-info-verkoop-gaat-door" : "geen-opschortende-voorwaarde");
  }

  // Renovatieplicht (§29, facultatief): enkel een Vlaamse verplichting. In
  // Vlaanderen opnemen — residentieel voor een woning/appartement, niet-
  // residentieel voor een grond/ander goed; in Brussel/Wallonië onderdrukken.
  if (kenmerken.gewest === "Vlaanderen" && kenmerken.goedType) {
    const resid = kenmerken.goedType === "woning" || kenmerken.goedType === "appartement" ? "residentieel" : "niet-residentieel";
    opnemenBeide("renovatieplicht", true, resid);
  } else if (kenmerken.gewest && kenmerken.gewest !== "Vlaanderen") {
    opnemenBeide("renovatieplicht", false);
  }

  // Gewestelijke aanvullingen die vroeger als [NAKIJKEN OF SCHRAPPEN]-tekst in
  // een gedeelde clausule stonden, zijn opgesplitst in eigen, facultatieve
  // onderdelen zodat ze deterministisch enkel bij het juiste gewest verschijnen
  // — nooit meer clausules over andere gewesten in het gegenereerde document.
  if (kenmerken.gewest) {
    // Natuur/bos en herstelvordering (§39-40): enkel Vlaanderen. Bij een
    // volledig "vrij" dossier (geen bos/VEN/natuurbeheerplan, geen
    // herstelvordering, blanco maatregelenregister) de opgeloste variant;
    // anders de volledige variant met de keuzes in de tekst (zoals voorheen —
    // nooit een open hypothesekeuze tonen enkel omdat het feit ontbreekt).
    opnemenBeide(
      "natuur-bos-herstelvordering-vlaanderen",
      kenmerken.gewest === "Vlaanderen",
      // "volledig" = de volledige brontekst met de keuzes erin (de vroegere
      // enige variant; bewust niet "alle" — dat is de sentinel van de motor).
      kenmerken.natuurBosHerstelVrij === true ? "geen-bijzonderheden" : "volledig"
    );
    // Terugbetaling renovatiepremie (§41): enkel Brussel.
    opnemenBeide("premies-brussel-terugbetaling", kenmerken.gewest === "Brussel");
    // Asbestinventarisattest (§42): enkel Vlaanderen. Een aanwezig attest
    // (asbestAanwezig) heeft voorrang op het bouwjaar: is er een attest, dan
    // was het kennelijk vereist, ongeacht wat het bouwjaar zou suggereren.
    opnemenBeide(
      "asbestinventarisattest-vlaanderen",
      kenmerken.gewest === "Vlaanderen",
      kenmerken.asbestAanwezig ? "aanwezig" : kenmerken.asbestBouwjaarRecent ? "niet-vereist-recent-bouwjaar" : "open"
    );
    // Asbestinventaris gemene/gemeenschappelijk gebruikte delen (§42): enkel bij
    // een appartement (kavel in mede-eigendom) in het Vlaams Gewest — bij een
    // huis, grond of ander goed zonder mede-eigendom is deze clausule nooit van
    // toepassing (er bestaan geen "gemene delen").
    opnemenBeide("asbest-gemene-delen-vlaanderen", kenmerken.gewest === "Vlaanderen" && kenmerken.goedType === "appartement");
    // Registratiebelasting — gewestelijke aanvullingen (§43): van toepassing bij
    // een Brussels of Waals goed, met de variant meteen vastgelegd.
    opnemenBeide(
      "registratiebelasting-brussel-wallonie",
      kenmerken.gewest === "Brussel" || kenmerken.gewest === "Wallonië",
      kenmerken.gewest === "Wallonië" ? "wallonie" : "bruxelles"
    );
    // Waalse energiepremies (§41): enkel Wallonië.
    opnemenBeide("premies-wallonie", kenmerken.gewest === "Wallonië");
    // Premies (§41, algemeen): de informatieve websites verschillen per
    // gewest — deterministisch de hypothese van het juiste gewest kiezen i.p.v.
    // alle drie inline op te sommen.
    beide("premies-algemeen", kenmerken.gewest === "Vlaanderen" ? "vlaanderen" : kenmerken.gewest === "Brussel" ? "brussel" : "wallonie");
  }

  // Mede-eigendom (§19, facultatief): enkel bij een kavel in mede-eigendom
  // (appartement) opnemen; bij een ander goedtype expliciet onderdrukken. De
  // syndicus-variant kiest de notaris (alle hypotheses blijven staan).
  if (kenmerken.goedType === "appartement") {
    opnemenBeide("mede-eigendom-statuten", true);
    opnemenBeide(
      "mede-eigendom-lasten-voorrecht",
      true,
      kenmerken.medeEigendomLastenBetwist === true ? "betwist" : kenmerken.medeEigendomLastenBetwist === false ? "akkoord" : "open"
    );
  } else if (kenmerken.goedType) {
    opnemenBeide("mede-eigendom-statuten", false);
    opnemenBeide("mede-eigendom-lasten-voorrecht", false);
  }

  // Enkel-kenmerk-regels (gezinswoning, zonnepanelen + groenestroom-
  // certificaten, keuring elektrische installatie, syndicus-informatie):
  // declaratief gemigreerd naar HELDERE_TAAL_REGELS (kenmerken-regels.ts,
  // roadmap-punt 2). De multi-kenmerk-logica hierboven (gewest × goedType,
  // EPC, asbest, renovatieplicht, …) blijft imperatief: die past niet in
  // het één-kenmerk-regelmodel.
  keuzes.push(...pasKenmerkRegelsToe(kenmerken, HELDERE_TAAL_REGELS));

  // Ondertekening standaard op het notariskantoor.
  beide("verzekering-overlijden-handtekeningen", "ondertekening-notariskantoor");

  return keuzes;
}




/** Toegestane waarden, voor validatie van binnenkomende MCP-feiten. */
export const GOED_KENMERK_TYPES: GoedKenmerkType[] = ["woning", "appartement", "grond", "ander"];
export const GEBRUIK_KENMERKEN: GebruikKenmerk[] = ["vrij", "koper-is-huurder", "verhuurd"];
export const GEWESTEN_KENMERK: Gewest[] = ["Vlaanderen", "Brussel", "Wallonië"];
export const RECHT_VAN_TERUGKEER_KENMERK: NonNullable<Ontwerpkenmerken["rechtVanTerugkeer"]>[] = ["conventioneel", "wettelijk"];
export const ELEKTRISCHE_KEURING_KENMERK: NonNullable<Ontwerpkenmerken["elektrischeKeuring"]>[] = ["geen-verslag", "conform", "niet-conform", "vrijstelling", "niet-van-toepassing"];
export const SYNDICUS_INFO_KENMERK: NonNullable<Ontwerpkenmerken["syndicusInfo"]>[] = ["geantwoord", "geen-antwoord", "geen-syndicus"];
export const GROENESTROOM_KENMERK: NonNullable<Ontwerpkenmerken["groenestroomcertificaten"]>[] = ["met", "zonder-zonder-subsidie", "zonder-met-subsidie"];

/** Eén kenmerk in de handleiding: naam, type/toegelaten waarden en wat het stuurt. */
export interface KenmerkUitleg {
  naam: keyof Ontwerpkenmerken;
  type: "enum" | "boolean";
  toegelaten?: string[];
  effect: string;
}

/**
 * De uitleg per kenmerk, als Record over ÁLLE sleutels van Ontwerpkenmerken:
 * de compiler dwingt zo af dat elk kenmerk dat de motor kent ook in de
 * handleiding (en dus in het API-/MCP-schema en de parser) staat. In juli 2026
 * bleek tweemaal dat een nieuw motor-kenmerk (overstromingVeilig,
 * gezinswoningVanToepassing) nooit door een agent kon worden aangeleverd omdat
 * de handleiding niet mee was uitgebreid — dit Record maakt die scheefgroei
 * voortaan een compilefout.
 */
const KENMERKEN_UITLEG: Record<keyof Ontwerpkenmerken, Omit<KenmerkUitleg, "naam">> = {
  gewest: { type: "enum", toegelaten: GEWESTEN_KENMERK, effect: "Kiest de gewestgebonden attest-/regelgevingsvarianten (energieprestatie, stedenbouw, overstroming, leegstand/bodem); stuurt de Vlaamse renovatieplicht." },
  goedType: { type: "enum", toegelaten: GOED_KENMERK_TYPES, effect: "Kiest de beschrijvings- en verzekeringshypothese; activeert bij 'appartement' de mede-eigendomclausules." },
  gebruik: { type: "enum", toegelaten: GEBRUIK_KENMERKEN, effect: "Kiest de hypothese voor gebruik/genot (vrij, koper is huurder, verhuurd aan derde)." },
  metKrediet: { type: "boolean", effect: "true → opschortende voorwaarde van financiering; false → sluitende 'geen opschortende voorwaarde'-variant." },
  elektrischeKeuring: { type: "enum", toegelaten: ELEKTRISCHE_KEURING_KENMERK, effect: "Kiest de PID-hypothese voor de keuring elektrische installatie (geen verslag / conform / niet-conform / vrijstelling / niet van toepassing bij een niet-residentieel goed)." },
  syndicusInfo: { type: "enum", toegelaten: SYNDICUS_INFO_KENMERK, effect: "Appartement: kiest de hypothese statuten van mede-eigendom naargelang de syndicus al dan niet antwoordde (of er geen syndicus is)." },
  groenestroomcertificaten: { type: "enum", toegelaten: GROENESTROOM_KENMERK, effect: "Zonnepanelen: kiest de hypothese rond de overdracht van groenestroomcertificaten (met / zonder zonder subsidie / zonder met subsidie)." },
  rechtVanTerugkeer: { type: "enum", toegelaten: RECHT_VAN_TERUGKEER_KENMERK, effect: "Schenking: conventioneel beding van terugkeer of enkel het wettelijk recht." },
  zonnepanelen: { type: "boolean", effect: "true → clausule (roerende goederen/)zonnepanelen opnemen; false → de heldere-taal-zonnepanelenclausule weglaten (geen panelen)." },
  verpacht: { type: "boolean", effect: "true → pachtclausule opnemen." },
  gezinswoningVanToepassing: { type: "boolean", effect: "true → gezinswoningclausule opnemen; false → clausule (titel inbegrepen) weglaten (verkoper woont er niet, of de partner is zelf mede-verkoper); onbekend → clausule blijft als facultatief gemarkeerd staan." },
  conventioneelVoorkooprecht: { type: "boolean", effect: "true → clausule conventioneel voorkooprecht opnemen." },
  epcAanwezig: { type: "boolean", effect: "Vlaanderen, geen appartement: true → kiest de hypothese 'EPC bestaat' van ht-energieprestatie; anders blijft de volledige multi-hypothese open." },
  asbestAanwezig: { type: "boolean", effect: "Vlaanderen: true → kiest de hypothese 'attest aanwezig' van ht-asbestinventarisattest-vlaanderen (voorrang op asbestBouwjaarRecent); anders blijft de keuze open, tenzij het bouwjaar gekend is." },
  asbestBouwjaarRecent: { type: "boolean", effect: "Vlaanderen, enkel zonder aanwezig attest: true (bouwjaar 2001 of recenter) → kiest 'niet vereist' van ht-asbestinventarisattest-vlaanderen; anders/onbekend blijft de volledige keuze open." },
  overstromingVeilig: { type: "boolean", effect: "Vlaanderen: true (P- én G-score beide 'A' op het overstromingsattest) → kiest de hypothese 'ligt niet in overstromingsgevoelig gebied'; anders/onbekend blijft de volledige keuze open." },
  wettelijkVoorkooprecht: { type: "boolean", effect: "true/false → kiest de hypothese 'bestaat'/'geen' van ht-voorkooprecht-algemeen; onbekend → volledige A/B-keuze blijft open." },
  fiscaalRegime: { type: "enum", toegelaten: ["registratiebelasting", "btw", "gemengd"], effect: "Kiest de hypothese van ht-registratiebelasting-btw; onbekend → volledige A/B/C-keuze blijft open." },
  medeEigendomLastenBetwist: { type: "boolean", effect: "Appartement: true/false → kiest 'betwist'/'akkoord' in ht-mede-eigendom-lasten-voorrecht; onbekend → volledige A/B-keuze blijft open." },
  voorbehoudVruchtgebruik: { type: "boolean", effect: "Schenking: true → vruchtgebruikclausule opnemen (schenker behoudt het vruchtgebruik); false → clausule weglaten (schenking in volle eigendom); onbekend → clausule blijft ter beoordeling staan." },
  minderjarigeBegiftigde: { type: "boolean", effect: "Schenking: true → aanvaarding namens de minderjarige begiftigde opnemen; false (alle begiftigden meerderjarig) → clausule weglaten; onbekend → clausule blijft ter beoordeling staan." },
  schenkingEigenGoedDoorGehuwde: { type: "boolean", effect: "Schenking: true → tussenkomst van de echtgenoot bij schenking van een eigen goed opnemen; false (geen gehuwde schenker) → clausule weglaten; onbekend → clausule blijft ter beoordeling staan." },
  lastHulpEnBijstand: { type: "boolean", effect: "Schenking: true → last van hulp en bijstand opnemen; false → clausule weglaten; onbekend → clausule blijft ter beoordeling staan." },
  meerdereBegiftigden: { type: "boolean", effect: "Schenking: true → aanwasbeding tussen begiftigden opnemen; false (één begiftigde) → clausule weglaten; onbekend → clausule blijft ter beoordeling staan." },
  partijVertegenwoordigd: { type: "boolean", effect: "true → vertegenwoordigingsclausule (volmacht) opnemen, de concrete hypothese kiest de notaris; false (alle partijen verschijnen in persoon) → clausule weglaten; onbekend → clausule blijft als facultatief gemarkeerd staan." },
  rooilijnOnteigeningErfgoedVrij: { type: "boolean", effect: "Vlaanderen: true (uit de stedenbouwkundige inlichtingen blijkt uitdrukkelijk géén onteigening/rooilijn/inneming en géén vastgesteld of beschermd erfgoed) → kiest, samen met overstromingVeilig, de volledig opgeloste variant van de clausule overstroming/rooilijn/erfgoed; anders blijven de keuzes open." },
  natuurBosHerstelVrij: { type: "boolean", effect: "Vlaanderen: true (uitdrukkelijk geen bos/VEN/natuurbeheerplan, geen herstelvordering woonkwaliteit én blanco maatregelenregister) → kiest de opgeloste 'geen bijzonderheden'-variant van de natuur/bos/herstelvordering-clausule; anders blijven de keuzes open." },
  stookolietank: { type: "enum", toegelaten: ["geen"], effect: "'geen' (uitdrukkelijk geen stookolietank aanwezig of geweest) → kiest de 'geen stookolietank'-hypothese; elke andere toestand (in gebruik, buiten gebruik, verwijderd) blijft een notariskeuze." },
  leegstandStookolieBodemVrij: { type: "boolean", effect: "Vlaanderen: true (uitdrukkelijk geen leegstand/ongeschiktheid/verwaarlozing, geen stookolietank, geen bodemvervuilende activiteiten én geen PFAS-no-regret-zone) → kiest de opgeloste variant van ht-leegstand-stookolie-bodem; anders blijven de A/B/C/D-keuzes open." },
};

/**
 * Machineleesbare handleiding bij de deterministische hypothese-engine, bedoeld
 * om mee te geven aan elke (probabilistische) AI-agent die de MCP gebruikt: zo
 * weet de agent PRECIES welke niet-persoonsgebonden feiten hij kan aanleveren,
 * welke waarden toegelaten zijn en welke clausules elk feit aanstuurt. Twee
 * agents met dezelfde feiten krijgen gegarandeerd hetzelfde resultaat.
 */
export const KENMERKEN_HANDLEIDING: {
  principe: string;
  veiligheid: string;
  privacy: string;
  kenmerken: KenmerkUitleg[];
} = {
  principe:
    "Notary.AI leidt de hypotheses (welke varianten/clausules) DETERMINISTISCH af uit deze feiten. " +
    "Lever enkel feiten aan die je met zekerheid uit de brondocumenten kent; laat een feit weg als je twijfelt.",
  veiligheid:
    "Bij twijfel niets schrappen: een weggelaten feit laat alle hypotheses staan ter keuze van de notaris. " +
    "Een clausule verdwijnt enkel door een uitdrukkelijk feit. Controleer steeds de 'waarschuwingen' in de output.",
  privacy:
    "Kenmerken zijn algemene feiten (enums/booleans), GEEN persoonsgegevens. Stuur nooit namen, geboortedata, " +
    "adressen, rijksregisternummers of rekeningnummers naar deze API; die vul je lokaal in op het teruggegeven ontwerp.",
  kenmerken: (Object.entries(KENMERKEN_UITLEG) as [keyof Ontwerpkenmerken, Omit<KenmerkUitleg, "naam">][]).map(
    ([naam, uitleg]) => ({ naam, ...uitleg })
  ),
};

// ── Generieke kenmerken-parser (één bron van waarheid: de handleiding) ───────

/** Resultaat van het valideren/parsen van ruwe kenmerken-invoer. */
export interface GeparsteKenmerken {
  kenmerken: Ontwerpkenmerken;
  /** Elke afgewezen of onbekende waarde, expliciet gemeld (nooit stil negeren). */
  problemen: string[];
}

/**
 * Valideert en parseert ruwe kenmerken-invoer (bv. de request-body van een
 * MCP-agent) tegen KENMERKEN_HANDLEIDING — dezelfde bron die de agent als
 * documentatie krijgt. Elk kenmerk dat de motor kent wordt zo automatisch ook
 * door de API aanvaard; een nieuw kenmerk toevoegen aan de handleiding volstaat.
 *
 * - Booleans aanvaarden ook "true"/"false" als tekst (agents die alles als
 *   string doorgeven).
 * - Onbekende sleutels en afgekeurde waarden komen in `problemen`, zodat een
 *   typfout van de agent zichtbaar blijft in plaats van stilzwijgend een open
 *   hypothese op te leveren.
 * - `negeerSleutels`: sleutels die geen kenmerk zijn maar wel legitiem in
 *   dezelfde invoer voorkomen (bv. andere body-velden) — geen melding.
 */
export function parseKenmerken(
  rauw: Record<string, unknown>,
  negeerSleutels: readonly string[] = []
): GeparsteKenmerken {
  const kenmerken: Ontwerpkenmerken = {};
  const problemen: string[] = [];
  const bekend = new Map(KENMERKEN_HANDLEIDING.kenmerken.map((k) => [k.naam as string, k]));

  for (const [sleutel, waarde] of Object.entries(rauw)) {
    if (waarde === undefined || waarde === null) continue;
    const uitleg = bekend.get(sleutel);
    if (!uitleg) {
      if (!negeerSleutels.includes(sleutel)) {
        problemen.push(`Onbekend kenmerk "${sleutel}" — zie de handleiding voor de toegelaten kenmerken.`);
      }
      continue;
    }
    if (uitleg.type === "boolean") {
      const b = waarde === true || waarde === "true" ? true : waarde === false || waarde === "false" ? false : undefined;
      if (b === undefined) problemen.push(`"${uitleg.naam}" moet een boolean zijn (true/false).`);
      else (kenmerken as Record<string, unknown>)[uitleg.naam] = b;
    } else {
      if (typeof waarde === "string" && uitleg.toegelaten?.includes(waarde)) {
        (kenmerken as Record<string, unknown>)[uitleg.naam] = waarde;
      } else {
        problemen.push(`Onbekend "${uitleg.naam}" — één van: ${(uitleg.toegelaten ?? []).join(", ")}.`);
      }
    }
  }

  return { kenmerken, problemen };
}
