import type { ModelParameter, Modeldocument, Modelonderdeel, Taal } from "./types";
import {
  extraheerParameters,
  standaardKeuzes,
  verzamelParameters,
  genereerDocument,
  vulParametersIn,
  type Herhaling,
} from "./samenstellen";
import { IDENTITEIT_KOPER_DEMO, IDENTITEIT_VERKOPER_DEMO } from "./identiteit-voorbeeld";

// ── Voorbeeldwaarden ─────────────────────────────────────────────────────────
// Genereert plausibele NL/FR demo-waarden voor {{parameter}}-placeholders, zodat
// de bibliotheek (OnderdelenTab/ModellenTab) en de voorstellen (ValiderenTab)
// een ÉCHT ingevuld voorbeeld kunnen tonen i.p.v. de kale placeholders. Nooit
// dossierdata — uitsluitend demo-tekst, puur op basis van de parameternaam.

/** Eén patroon (regex op de parameternaam) met een NL- en een FR-voorbeeldwaarde. */
interface Patroon {
  regex: RegExp;
  nl: string;
  fr: string;
}

// Volgorde is betekenisvol: het eerste passende patroon wint. Specifieke
// patronen (bv. "datum_geboorte") staan daarom vóór generieke ("datum").
// De trefwoorden dekken zowel de Nederlandse als de Franse parameternamen die
// in de bibliotheek voorkomen (zie extractie van HUIDIGE_GATEN-achtige
// analyse: veelvoorkomende wortels als datum/date, plaats/lieu, identiteit/
// identité, omschrijving/description, …), zodat een voorbeeldweergave zo
// weinig mogelijk op de generieke eindwaarde hoeft terug te vallen.
const PATRONEN: Patroon[] = [
  { regex: /rijksregisternummer|rijksregister/i, nl: "85.03.12-345-67", fr: "85.03.12-345-67" },
  { regex: /ondernemingsnummer|btw.?nummer|kbo/i, nl: "BE 0123.456.789", fr: "BE 0123.456.789" },
  // Combi-parameters "plaats én datum" in één placeholder (bv.
  // "{{plaats_datum_overlijden}}" in "overleden te {{…}}",
  // "{{plaats_datum_huwelijk}}" in "gehuwd te {{…}}"): vóór de losse
  // plaats-/datum-patronen, anders rendert enkel de datum en leest de akte
  // "overleden te 15 maart 2026" (een datum na een plaats-voorzetsel).
  { regex: /geboorteplaats_datum/i, nl: "Leuven op 12 maart 1985", fr: "Louvain le 12 mars 1985" },
  { regex: /plaats_(en_)?datum/i, nl: "Leuven op 15 maart 2026", fr: "Bruxelles le 15 mars 2026" },
  { regex: /geboorteplaats/i, nl: "Leuven", fr: "Louvain" },
  { regex: /^(datum_)?geboorte|geboortedatum/i, nl: "12 maart 1985", fr: "12 mars 1985" },
  // De aktedatum in de aanhef staat voluit en volgt op "HET JAAR {{jaar_voluit}}."
  // — het jaartal dus NIET herhalen ("Op vijftien maart.", niet "Op 15 maart
  // 2026."). Andere voluit-datums dragen het jaar wél in woorden.
  { regex: /datum_akte_voluit/i, nl: "vijftien maart", fr: "quinze mars" },
  { regex: /(^|_)(datum|date)_.*(voluit|lettres)|(voluit|lettres).*_(datum|date)(_|$)/i, nl: "vijftien maart tweeduizend zesentwintig", fr: "quinze mars deux mille vingt-six" },
  { regex: /^datum|_datum$|datum_|^date_|_date$|_date_|^date$/i, nl: "15 maart 2026", fr: "15 mars 2026" },
  // Kaal uur ("om {{uur_opening}} uur", "à {{uur_jaarvergadering}} heures") en
  // een dag-in-de-maand ("de {{dag_jaarvergadering}} van de maand juni"): de
  // omringende tekst schrijft "uur"/"heures" zelf al.
  // Enkel als prefix ("uur_opening", "dag_jaarvergadering"): een samengestelde
  // naam als "bedrag_per_dag" is een bedrag, geen dag.
  { regex: /^uur(_|$)/i, nl: "10", fr: "10" },
  { regex: /^dag(_|$)/i, nl: "eerste dinsdag", fr: "premier mardi" },
  { regex: /percentage|pourcentage|^pct_|_pct$/i, nl: "10 %", fr: "10 %" },
  // Het pro-rata-aandeel van de onroerende voorheffing is een EUR-bedrag, geen
  // breuk — zonder deze match ving het aandeel-patroon ("3/1.000e") het op en
  // las de akte "een bedrag van 3/1.000e EUR".
  { regex: /voorheffing|pr[eé]compte/i, nl: "185,50", fr: "185,50" },
  // Géén "euro"/"euros" in de waarde: elke tekst die {{ki_*}}/{{rc_*}}
  // gebruikt, voegt de munteenheid zelf al toe (bv. "{{ki_samen}} euro" of
  // "€ {{ki_niet_geindexeerd}}") — anders verdubbelt de eenheid
  // ("1.250 euro euro"). Woordgrens-match "(^|_)ki(_|$)" i.p.v. enkel
  // "^ki_|_ki$": een samengestelde naam als "totaal_ki_woorden" heeft "ki"
  // wél tussen twee underscores maar niet aan het begin/einde van de hele
  // naam, en zou anders gemist worden. "rc_"/FR revenu cadastral staat hier
  // vóór de rekening-/compte-match hierna, want een FR
  // kadastraal-inkomenparameter (bv. "rc_appartement") mag nooit als
  // bankrekening ("BE68 …") gerenderd worden.
  // Het KI/RC voluit ("ki_samen_woorden", "rc_…_lettres"): in woorden, sporend
  // met het cijfer 1.250 — anders leest de akte "1.250 euro (1.250 euro)".
  {
    regex: /(^|_)(ki|rc)_.*(woorden|voluit|lettres)|revenu_cadastral_lettres/i,
    nl: "duizend tweehonderdvijftig",
    fr: "mille deux cent cinquante",
  },
  // "[ _]" i.p.v. enkel een spatie: parameterNAMEN schrijven "kadastraal_inkomen"
  // met underscore — met alleen "kadastraal inkomen" (spatie) miste de match en
  // toonde de akte de omschrijving-fallback ("… bedraagt Niet-geïndexeerd
  // kadastraal inkomen van het goed, in euro EUR").
  {
    regex: /kadastraal[ _]inkomen|revenu[ _]cadastral|(^|_)ki(_|$)|(^|_)rc(_|$)/i,
    nl: "1.250",
    fr: "1.250",
  },
  // Vóór de bedrag-match hierna: de TITULARIS van een rekening is een
  // PERSOON, geen geldbedrag — zonder deze eerdere, specifieke match ving de
  // bedrag-regex "titularis_rekening_voorschot" op via het woord
  // "voorschot" dat toevallig in die samengestelde naam zit.
  { regex: /titularis|titulaire/i, nl: "Jan Peeters", fr: "Jean Dupont" },
  // Vóór de rekening-match: de HOUDER/NAAM op een rekening is een persoon,
  // geen IBAN ("op naam van {{houder_rekening_voorschot}}",
  // "au nom de {{nom_compte_tiers}}", "{{naam_koper_rekening}}").
  // De rekening waarvan het voorschot/saldo komt, is die van de KOPER — zelfde
  // demo-naam als de koper-rolnaam verderop, anders spreekt de akte zichzelf tegen.
  { regex: /houder_rekening|houder_van|nom_compte|naam.*rekening/i, nl: "Marie Janssens", fr: "Marie Durand" },
  // Vóór de bedrag-match: de BANK waarbij een voorschot-/saldorekening wordt
  // gehouden ("bij {{bank_voorschot}}") is een instelling, geen bedrag — de
  // naam bevat toevallig "voorschot"/"saldo".
  { regex: /(^|_)(bank|banque)(_|$)/i, nl: "KBC Bank", fr: "KBC Bank" },
  // Eveneens vóór de bedrag-match: een REKENINGNUMMER waaruit een
  // voorschot/saldo afkomstig is (bv. "rekening_herkomst_voorschot") is geen
  // geldbedrag, ook al bevat de naam toevallig "voorschot"/"saldo".
  { regex: /rekening|compte|iban/i, nl: "BE68 5390 0754 7034", fr: "BE68 5390 0754 7034" },
  // Vóór de bedrag-match (die o.m. op "rente" aanslaat): een rentevoet is een
  // percentage ("de rentevoet bedraagt {{rentevoet}}"), de periodiciteit en
  // eerste vervaldag van een (lijf)rente zijn een ritme resp. een datum
  // ("betaalbaar {{frequence_rente}}, voor de eerste maal op
  // {{eerste_vervaldag_rente}}") — geen van drie is een geldbedrag.
  { regex: /rentevoet|(^|_)taux(_|$)/i, nl: "2,15 %", fr: "2,15 %" },
  { regex: /vervaldag|echeance/i, nl: "1 april 2026", fr: "1er avril 2026" },
  { regex: /frequen[ct]|betaalritme|periodicit/i, nl: "maandelijks", fr: "mensuellement" },
  // ── Gedifferentieerde bedragen (vóór de generieke bedrag-match) ────────────
  // Eén en dezelfde 250.000 voor prijs, voorschot, saldo, rente én vergoeding
  // maakt de demo rekenkundig onmogelijk ("voorschot 250.000, saldo 250.000,
  // prijs 250.000") en absurd ("maandelijkse vergoeding van 250.000 EUR").
  // Cijfers en voluit/letters horen per grootheid samen te sporen.
  { regex: /(prijs|prix)_(voluit|letters|lettres)|prix_lettres/i, nl: "tweehonderdvijftigduizend", fr: "deux cent cinquante mille" },
  { regex: /voorschot_(voluit|letters)|acompte_lettres/i, nl: "vijfentwintigduizend", fr: "vingt-cinq mille" },
  { regex: /(saldo|solde)(_prijs|_prix)?_(voluit|letters|lettres)/i, nl: "tweehonderdvijfentwintigduizend", fr: "deux cent vingt-cinq mille" },
  { regex: /rente_(voluit|letters)/i, nl: "duizend honderdvijftig", fr: "mille cent cinquante" },
  { regex: /bouquet_(voluit|letters)/i, nl: "tachtigduizend", fr: "quatre-vingt mille" },
  { regex: /(bedrag|montant|drempelbedrag).*(voluit|letters|lettres)/i, nl: "tweehonderdvijftigduizend", fr: "deux cent cinquante mille" },
  { regex: /voorschot|acompte/i, nl: "25.000", fr: "25.000" },
  { regex: /(^|_)(saldo|solde)(_|$)/i, nl: "225.000", fr: "225.000" },
  { regex: /bouquet/i, nl: "80.000", fr: "80.000" },
  { regex: /(^|_)rente(_|$)/i, nl: "1.150", fr: "1.150" },
  { regex: /huurwaarborg|garantie_locative/i, nl: "1.900", fr: "1.900" },
  { regex: /vergoeding|indemnit/i, nl: "950", fr: "950" },
  // Periodieke (gemeenschappelijke) lasten, plankosten en de totale fiscale
  // last: kleine EUR-bedragen, geen omschrijving-fallback ("… lasten per
  // kwartaal, in euro EUR" / "Plankosten, in euro EUR").
  { regex: /lasten_per|periodieke_lasten|provisie_lasten|charges_trimestrielles|charges_communes/i, nl: "450", fr: "450" },
  { regex: /plankosten|frais_plan/i, nl: "150", fr: "150" },
  { regex: /fiscale_last|charge_fiscale/i, nl: "2.850", fr: "2.850" },
  {
    // "quote.?part" hoort hier NIET bij: een quote-part/aandeel in de
    // gemeenschappelijke delen (bv. "3/1.000e") is geen geldbedrag — zie de
    // aparte match verderop. Géén "euro"/"euros" in de waarde: net als bij
    // kadastraal inkomen voegt vrijwel elke tekst die een bedrag-parameter
    // gebruikt de munteenheid zelf al toe (bv. "{{prijs_cijfers}} EUR",
    // "{{bedrag_inschrijving}} euro in hoofdsom") — anders verdubbelt de
    // eenheid ("250.000 euro euro").
    regex:
      /bedrag|prijs|prix|montant|waarde|valeur|kapitaal|capital|saldo|solde|voorschot|provision|rente|indemnite|vergoeding|huurwaarborg|garantie_locative|fonds_(roulement|reserve)/i,
    nl: "250.000",
    fr: "250.000",
  },
  { regex: /zetel|si[eè]ge/i, nl: "Kerkstraat 12, 3000 Leuven", fr: "Rue de l'Église 12, 1000 Bruxelles" },
  // "woonplaats" hoort bij het adres-patroon: "laatst wonende te
  // {{laatste_woonplaats_erflater}}" en "wonende te {{woonplaats_lasthebber_ad_hoc}}"
  // verwachten een adres, geen persoonsnaam of gemeente.
  { regex: /adres|adresse|woonplaats/i, nl: "Kerkstraat 12, 3000 Leuven", fr: "Rue de l'Église 12, 1000 Bruxelles" },
  // Het ANTWOORD/de INHOUD van een brief van gemeente/ambtenaar/gewest is een
  // korte inhoud, geen plaatsnaam — zonder deze match las de akte "heeft de
  // gemeente geantwoord: Leuven." ("antwoord_gemeente"/"contenu_lettre_commune"
  // vingen op het woord "gemeente"/"commune").
  { regex: /(^|_)antwoord|r[eé]ponse|(^|_)contenu(_|$)/i, nl: "gunstig, zonder opmerkingen", fr: "favorable, sans remarques" },
  // Het kantoor Rechtszekerheid ("overgeschreven op {{kantoor_…}}") is een
  // kantooraanduiding, geen kale gemeente ("transcrit au Bruxelles").
  { regex: /kantoor_rechtszekerheid|bureau_securite/i, nl: "kantoor Rechtszekerheid Leuven 1", fr: "bureau Sécurité juridique Bruxelles 1" },
  // "(^|_)commune(_|$)" i.p.v. een kaal "commune": de FR-parameternamen
  // "quote_part_parties_communes" (aandeel, "3/1.000e") en
  // "conclusion_certificat_amiante_communes" bevatten het meervoud "communes"
  // en werden anders als plaatsnaam gerenderd ("Bruxelles dans les parties
  // communes").
  { regex: /gemeente|(^|_)commune(_|$)|^plaats_|_plaats$|^lieu_|_lieu$/i, nl: "Leuven", fr: "Bruxelles" },
  // Vóór de parking-match hierna: "standplaats" (de zetel van de notaris,
  // bv. "notaris met standplaats te {{standplaats}}") is GEEN parkeerplaats —
  // zonder deze eerdere, specifieke match werd "standplaats" zelf door de
  // bredere /parking/i-regex opgevangen ("notaris te parkeerplaats nr. 12").
  // De standplaats van de tussenkomende/tweede notaris verschilt van die van
  // de instrumenterende notaris — vóór de generieke standplaats-match hierna.
  { regex: /standplaats_tussenkomende|standplaats_tweede/i, nl: "Mechelen", fr: "Malines" },
  { regex: /^standplaats/i, nl: "Tervuren", fr: "Tervuren" },
  { regex: /emplacement.*parking|parking/i, nl: "parkeerplaats nr. 12", fr: "emplacement de parking n° 12" },
  // Werkt zowel wanneer "notarissen"/"notaires" al vóór de placeholder staat
  // ("de geassocieerde notarissen {{kantoornaam}}") als wanneer het er net na
  // komt ("de vennootschap {{kantoornaam}}, geassocieerde notarissen") — een
  // waarde die zelf met "Notarissen"/"Notaires" begint, verdubbelt in het
  // eerste geval.
  { regex: /^kantoornaam$|naam_kantoor|nom_.*etude|nom_.*cabinet/i, nl: "Voorbeeld & Partners", fr: "Exemple & Associés" },
  // Vóór de bedrag-match hierboven: de begunstigde van een recht is een
  // PERSOON/entiteit, geen geldbedrag, en zonder specifieke waarde toont de
  // omschrijving-fallback zich dubbel achter het al aanwezige "de begunstigde".
  { regex: /begunstigde|beneficiaire/i, nl: "Jan Peeters", fr: "Jean Dupont" },
  // Idem: het (fiscale) kantoor/bureau waar iets wordt ingediend, en het
  // erkende keuringsorganisme/de PEB-deskundige, tonen anders hun eigen
  // omschrijving dubbel achter het al aanwezige "kantoor"/"bureau"/"organisme"/
  // "expert" in de tekst.
  { regex: /^btw_kantoor$/i, nl: "Leuven", fr: "Bruxelles" },
  { regex: /keuringsorganisme|organisme_controle/i, nl: "Vinçotte", fr: "Vinçotte" },
  // Zelfde demo-deskundige als de generieke deskundige/expert-match verderop,
  // anders wisselt de deskundige binnen één akte van naam.
  { regex: /epb_deskundige|^expert_peb$/i, nl: "Els Vermeulen", fr: "Éliane Dubois" },
  // Kadastrale sectie: in België steeds een enkele hoofdletter.
  { regex: /sectie_titel|sectie_recent_kadaster|section_titre|section_extrait_recent|section.*kadastr/i, nl: "A", fr: "A" },
  // FR: "en zone {{gbp_bestemming}}"/"en zone {{zone_plan_secteur}}" schrijft
  // "zone" er zelf al bij; NL gebruikt hier geen letterlijk voorafgaand woord
  // en houdt dus de generieke waarde.
  { regex: /gbp_bestemming|zone_plan_secteur/i, nl: "woongebied", fr: "d'habitat" },
  { regex: /^totaal_aandelen$/i, nl: "100", fr: "100" },
  // Een quote-part/aandeel in de gemeenschappelijke delen (bv. "3/1.000e")
  // is geen geldbedrag — vóór de generieke bedrag-match hierboven zou
  // "quote.?part" er anders in verdwaald zijn.
  { regex: /quote.?part|quotit|^aandeel|_aandeel$|aandeel_/i, nl: "3/1.000e", fr: "3/1.000e" },
  // Specifiek vóór de algemene notaris-match hierna: hier staat GEEN "notaris"
  // als los woord vóór de placeholder ("volmacht verleden voor {{…}}"), dus
  // moet de volledige aanduiding (notaris + datum) wél in de waarde zitten.
  { regex: /^notaris_en_datum/i, nl: "notaris Ann Janssens op 15 maart 2026", fr: "le notaire Ann Janssens le 15 mars 2026" },
  // Kale naam (géén "notaris "/"notaire "-voorvoegsel): zo goed als elke
  // tekst die een notaris_*/notaire_*-parameter gebruikt, schrijft het woord
  // "notaris"/"notaire" er zelf al vóór (bv. "verleden voor notaris {{…}}"),
  // anders verdubbelt het ("notaris notaris Ann Janssens").
  // De tussenkomende/tweede notaris moet een ÁNDERE naam dragen dan de
  // instrumenterende notaris (twee keer dezelfde naam in de aanhef "voor ons,
  // meester X en meester X" is logisch onmogelijk) — vóór de generieke
  // notaris- en naam-matches. Idem voor zijn standplaats.
  { regex: /tussenkomende_notaris|tweede_notaris|notaire_intervenant|second_notaire/i, nl: "Peter Van Hove", fr: "Pierre Vanhove" },
  { regex: /^notaris|notaire|notaris_naam|naam_notaris/i, nl: "Ann Janssens", fr: "Ann Janssens" },
  // Rolgebonden demo-namen, vóór de generieke naam-match: dezelfde demo-naam
  // voor onverenigbare rollen maakt de tekst logisch onmogelijk — de
  // ondertekenaar ("Jan Peeters") die zijn EIGEN nalatenschap aanvaardt, of
  // de lastgever die zichzelf als lasthebber aanstelt. De overledene, de
  // lasthebber(s) en de langstlevende krijgen daarom elk een eigen naam.
  { regex: /(naam|identiteit)_?(van_)?(de_)?erflater|naam_overleden/i, nl: "August Peeters", fr: "Auguste Dupont" },
  { regex: /identiteiten_lasthebbers|namen_.*lasthebbers/i, nl: "Marie Janssens en Piet Willems", fr: "Marie Durand et Pierre Martin" },
  { regex: /lasthebber|mandataire/i, nl: "Marie Janssens", fr: "Marie Durand" },
  { regex: /langstlevende|survivant/i, nl: "Marie Janssens", fr: "Marie Durand" },
  // Rolgebonden namen voor de VERKOOP: verkoper en koper met dezelfde demo-naam
  // ("Jan Peeters verkoopt aan Jan Peeters") maakt de akte logisch onmogelijk;
  // de echtgeno(o)t(e)/partner (gezinswoning-instemming) en een medewerker zijn
  // evenmin de partij zelf.
  { regex: /medewerker|collaborateur/i, nl: "Tom Willems", fr: "Thomas Legrand" },
  // De IDENTITEIT van de tussenkomende partner (gezinswoning, art. 215 BW) is
  // een lopende zinsinvoeging: een volledige inline-identificatie van de
  // echtgeno(o)t(e) — NIET de verkoper zelf, en geen meerregelig blok.
  // Vóór de echtgenoot-naam-match hierna ("identiteit_partner_verkoper" zou
  // daar op "partner" matchen) én vóór de blok-identiteiten verderop.
  {
    regex: /identiteit_partner|identite_partenaire/i,
    nl: "mevrouw DE SMET Sofie, geboren te Leuven op 3 januari 1987, ingeschreven in het rijksregister onder nummer 87.01.03-123.45, wonende te 3000 Leuven, Kerkstraat 12",
    fr: "Madame LAMBERT Sophie, née à Namur le 3 janvier 1987, inscrite au registre national sous le numéro 87.01.03-123.45, domiciliée à 1000 Bruxelles, Rue de l'Église 12",
  },
  { regex: /echtgen(o|oo)t|conjoint|[eé]pou(x|se)|(^|_)partner/i, nl: "Sofie De Smet", fr: "Sophie Lambert" },
  // De IDENTITEIT van een comparant is de volledige identificatie zoals de
  // Organieke Wet Notariaat ze vereist. Er bestaat daarvoor precies ÉÉN
  // kantoorclausule ("identiteit-partijen", met burgerlijke staat en — op een
  // eigen alinea — huwelijksstelsel, huwelijksovereenkomst en wijzigingen);
  // de demo-blokken worden er rechtstreeks uit gegenereerd, zodat élk model
  // met een vlakke identiteitsparameter dezelfde clausule hergebruikt
  // (kwaliteitsnazicht notaris 2026-07-11). Zie identiteit-voorbeeld.ts.
  {
    regex: /identiteit(en)?_.*verkoper|identite(s)?_vendeur/i,
    nl: IDENTITEIT_VERKOPER_DEMO.nl,
    fr: IDENTITEIT_VERKOPER_DEMO.fr,
  },
  {
    regex: /identiteit(en)?_.*koper|identite(s)?_acquereur/i,
    nl: IDENTITEIT_KOPER_DEMO.nl,
    fr: IDENTITEIT_KOPER_DEMO.fr,
  },
  { regex: /(naam|namen)_.*verkoper|nom(s)?_.*vendeur/i, nl: "Jan Peeters", fr: "Jean Dupont" },
  { regex: /(naam|namen)_.*koper|nom(s)?_.*acquereur/i, nl: "Marie Janssens", fr: "Marie Durand" },
  {
    regex: /^naam|_naam$|naam_|identiteit|identite|lastgever|mandant|syndic/i,
    nl: "Jan Peeters",
    fr: "Jean Dupont",
  },
  { regex: /^namen|_namen$|namen_|^noms|_noms$|noms_/i, nl: "Jan Peeters en Marie Janssens", fr: "Jean Dupont et Marie Martin" },
  // Een LOT(-benaming) op een verkavelings-/afbakeningsplan is een kort nummer
  // of letter, geen vennootschapsnaam — zonder deze match ving "benaming_lot_
  // op_plan" op het woord "benaming" en las de akte "onder lot NV Voorbeeld".
  // Bewust smal (expliciete namen + ^lot): een breed /(^|_)lot(_|$)/ ving ook
  // "nature_du_lot" ("Le/L' 3 …") en "description_lot_privatif" (kale "3"-regel)
  // — regressie uit het eerste nazicht, gemeld in het tweede.
  { regex: /benaming_lot|denomination_lot|lot_op_plan|nummer_lot|lot_nummer|numero_lot|^lot(_|$)/i, nl: "3", fr: "3" },
  { regex: /vennootschap|soci[eé]t[eé]|benaming|d[eé]nomination|appellation/i, nl: "NV Voorbeeld", fr: "SA Exemple" },
  // De kamer-per-kamer BESCHRIJVING van een verdieping is lopende tekst; het
  // verdiepingsNUMMER is een rangtelwoord — beide vóór de generieke
  // nummer-match, anders las de akte "op de AB123456 verdieping : 2e".
  { regex: /beschrijving.*verdieping|description.*etage/i, nl: "hal, woonkamer, open keuken en badkamer", fr: "hall, séjour, cuisine ouverte et salle de bains" },
  // Ook de FR-namen met een rangtelwoord ertussen ("numero_premier_etage",
  // "numero_deuxieme_etage") — die vielen anders op de nummer-match
  // ("au AB123456er étage").
  { regex: /nummer_.*verdieping|verdieping.*nummer|numero_.*etage|etage.*numero/i, nl: "2e", fr: "2" },
  // De ligging van een kavel in het gebouw is een plaatsbeschrijving, geen
  // omschrijving-fallback ("situé(e) Emplacement du lot dans l'immeuble …").
  { regex: /emplacement_dans|ligging_in_gebouw/i, nl: "links van de inkomhal", fr: "à gauche du hall d'entrée" },
  { regex: /reference_appartement|referentie_appartement/i, nl: "B7", fr: "B7" },
  {
    regex: /nummer|numero|n[uú]mero|referentie|reference|r[eé]f[eé]rence|referte|registre|^uc_|_uc$|code/i,
    nl: "AB123456",
    fr: "AB123456",
  },
  { regex: /oppervlakte|superficie/i, nl: "150 m²", fr: "150 m²" },
  { regex: /beroep|profession/i, nl: "bediende", fr: "employé" },
  { regex: /permis|vergunning/i, nl: "vergunning nr. 2021/123", fr: "permis n° 2021/123" },
  // Bevat NIET het woord "liter"/"litres": de teksten schrijven dat er zelf
  // al bij (bv. "inhoud {{tank_inhoud}} liter"), anders verdubbelt het.
  // De KOSTENVERDELING rond een tank is een afspraak tussen partijen, geen
  // literinhoud — vóór de inhoud-match hierna, anders las de akte "de
  // kostenverdeling worden geregeld als volgt: 1.500".
  {
    regex: /kostenverdeling|verdeling_kosten|repartition.*frais/i,
    nl: "de verkoper en de koper dragen elk de helft van de kosten",
    fr: "le vendeur et l'acquéreur supportent chacun la moitié des frais",
  },
  { regex: /citerne|tank/i, nl: "1.500", fr: "1.500" },
  { regex: /bestemming|destination|^zone|_zone$|zone_/i, nl: "woongebied", fr: "zone d'habitat" },
  // Eén wooneenheid: spoort met de demo-bestemming "eengezinswoning" (BWRO-
  // verklaring Brussel) — vóór de generieke aantal-match ("2").
  { regex: /aantal_wooneenheden|nombre.*logement|unites?_logement/i, nl: "1", fr: "1" },
  { regex: /^aantal|_aantal$|aantal_|^nombre|_nombre$|nombre_/i, nl: "2", fr: "2" },
  // "epb_klasse" (omgekeerde woordvolgorde t.o.v. "klasse_peb"/"classe_peb")
  // hoort hier ook bij, anders mist deze match en valt de parameter terug op
  // haar eigen omschrijving ("energieklasse Energieklasse van het goed …").
  { regex: /klasse_peb|label_epc|classe_peb|epb_klasse/i, nl: "D", fr: "D" },
  // Vóór de peb/epc-match: het VERBRUIK, de GELDIGHEID en de DESKUNDIGE van een
  // EPC/PEB zijn een getal, een datum en een persoon — geen certificaatnummer
  // ("energieverbruik van 20210101-0001234 kWh/m²", "geldig tot 20210101-…",
  // "opgesteld door 20210101-…, erkend energiedeskundige").
  { regex: /verbruik|consommation/i, nl: "245", fr: "245" },
  { regex: /geldigheid|validit[eé]/i, nl: "15 maart 2036", fr: "15 mars 2036" },
  { regex: /landmeter|g[eé]om[eè]tre/i, nl: "Karel Aerts", fr: "Charles Albert" },
  { regex: /deskundige|expert/i, nl: "Els Vermeulen", fr: "Éliane Dubois" },
  { regex: /peb|epc/i, nl: "20210101-0001234", fr: "20210101-0001234" },
  // Bevat NIET het woord "verdieping"/"étage": de teksten die dit gebruiken
  // schrijven dat woord er zelf al bij (bv. "op de {{verdieping}} verdieping",
  // "au {{etage}}ème étage" — de FR-tekst verwacht bovendien een KAAL cijfer,
  // geen ordinaal, en verdraagt geen "1" (onregelmatig: "1er", nooit "1ème").
  // "2e"/"2" ontwijkt die onregelmatigheid en verdubbelt nergens.
  { regex: /verdieping|^etage|_etage$|etage_|[eé]tage/i, nl: "2e", fr: "2" },
  // Bevat NIET het woord "dagen"/"jours": de teksten schrijven dat er zelf al
  // bij (bv. "binnen {{termijn}} dagen"), anders verdubbelt het.
  { regex: /termijn|d[eé]lai|looptijd|duree|duur_/i, nl: "30", fr: "30" },
  { regex: /huur|loyer|charges_locatives/i, nl: "950 euro per maand", fr: "950 euros par mois" },
  { regex: /^email|_email|e.?mail/i, nl: "voorbeeld@notarissen-tervuren.be", fr: "exemple@notarissen-tervuren.be" },
  { regex: /^p_score$|^g_score$/i, nl: "B", fr: "B" },
  { regex: /werken|travaux/i, nl: "renovatiewerken aan de gevel", fr: "travaux de rénovation de la façade" },
  // De oorsprong van eigendom is een volwaardige clausule-alinea; de kale
  // omschrijving-fallback ("Oorsprong van eigendom van het goed") leest als
  // een leeg hoofdstuk in de demo-akte.
  {
    regex: /oorsprong|origine/i,
    nl: "De verkoper verkreeg het goed bij akte van aankoop verleden voor notaris Ann Janssens te Leuven op 12 juni 2015, overgeschreven op het kantoor Rechtszekerheid Leuven 1.",
    fr: "Le vendeur a acquis le bien aux termes d'un acte de vente reçu par le notaire Ann Janssens à Louvain le 12 juin 2015, transcrit au bureau Sécurité juridique compétent.",
  },
  { regex: /bodem|^sol|_sol$|sol_/i, nl: "geen bodemverontreiniging vastgesteld", fr: "aucune pollution du sol constatée" },
  // De conclusie van een asbestinventarisattest (o.m. de gemene delen:
  // "conclusion_certificat_amiante_communes") — voorheen ving die per abuis op
  // het meervoud "communes" ("Bruxelles"); na de verstrenging van dat patroon
  // hoort er een échte conclusie te staan, geen afgekapte fallback.
  { regex: /amiante|asbest/i, nl: "er werden geen asbesthoudende materialen aangetroffen", fr: "aucun matériau contenant de l'amiante n'a été détecté" },
  // Onderstaande vier patronen vervangen anders erg letterlijke, deels
  // grammaticaal onbruikbare omschrijving-fallbacks in de "basisakte
  // mede-eigendom"-clausule: "dit_goed_deze_goederen" en "staat_staan"
  // (FR: "ce_bien_ces_biens"/"est_decrit_sont_decrits") zijn beide bedoeld om
  // MET ELKAAR een vloeiende zin te vormen ("Zoals dit goed beschreven staat
  // in …" / "Tel que ce bien est décrit dans …"); zonder specifieke waarde
  // toont elk zijn eigen, halve omschrijving na elkaar.
  { regex: /^dit_goed_deze_goederen$|^ce_bien_ces_biens$/i, nl: "dit goed", fr: "ce bien" },
  // NL: de tekst zelf schrijft "beschreven" al vóór de placeholder
  // ("… beschreven {{staat_staan}} in de basisakte …"), dus enkel "staat"
  // hier — anders verdubbelt "beschreven" ("beschreven beschreven staat").
  { regex: /^staat_staan$/i, nl: "staat", fr: "staat" },
  { regex: /^est_decrit_sont_decrits$/i, nl: "est décrit", fr: "est décrit" },
  // "Die verklaren {{verdeling_gekochte_delen}} aan te kopen." /
  // "Déclarant acquérir {{répartition…}}." — de omschrijving alleen ("Verdeling
  // van de aangekochte quotiteiten tussen de kopers") past grammaticaal niet
  // in die zin.
  { regex: /verdeling_gekochte_delen|repartition.*quotit/i, nl: "elk voor de onverdeelde helft", fr: "chacun pour moitié indivise" },
  { regex: /^type_goed$|^nature_du_lot$/i, nl: "appartement", fr: "appartement" },
  { regex: /gebruik_of_genot|usage_ou_jouissance/i, nl: "gebruik", fr: "usage" },
  // Bevatten NIET "jaar"/"maand": de teksten schrijven dat er zelf al bij
  // (bv. "in het jaar {{jaar_eerste_jaarvergadering}}"), anders verdubbelt
  // het ("jaar Jaar van de eerste jaarvergadering").
  { regex: /jaar_eerste_jaarvergadering|annee.*assemblee/i, nl: "2027", fr: "2027" },
  { regex: /maand_jaarvergadering|mois.*assemblee/i, nl: "juni", fr: "juin" },
  // NL: de tekst schrijft "vanaf" al vóór de placeholder ("vanaf
  // {{startpunt_bedenktijd}}"), dus geen "vanaf" in de waarde zelf.
  { regex: /^startpunt_bedenktijd$/i, nl: "de ondertekening van het compromis", fr: "la signature du compromis" },
  // Bevatten NIET "goed"/"bien": de teksten schrijven dat er zelf al bij
  // ("het goed {{goed_van_partij_een}}"), anders verdubbelt het.
  { regex: /goed_van_partij_een|goed_van_partij_twee/i, nl: "gelegen te Leuven, Kerkstraat 12", fr: "sis à Bruxelles, Rue de l'Église 12" },
];

/** Zet een parameternaam om in leesbare woorden (eindfallback als er ook geen omschrijving is). */
function verwoordParameternaam(naam: string): string {
  return naam.replace(/_/g, " ").trim();
}

/**
 * Leidt een korte, plausibele voorbeeldwaarde af uit de omschrijving van een
 * parameter: de omschrijving beschrijft weliswaar het VELD (niet de waarde
 * zelf), maar is veel informatiever dan de generieke eindwaarde. Een
 * kwalificatie tussen haakjes aan het einde (bv. "(indien aanwezig)") en
 * alles na de eerste punt/puntkomma worden weggelaten voor de leesbaarheid.
 */
function kortUitOmschrijving(omschrijving: string): string {
  const zonderKwalificatie = omschrijving.replace(/\s*\([^)]*\)\s*$/, "").trim();
  const eerste = zonderKwalificatie.split(/[.;]/)[0].trim();
  const tekst = eerste || zonderKwalificatie;
  return tekst.length <= 90 ? tekst : `${tekst.slice(0, 87).trim()}…`;
}

/** Eén plausibele demo-waarde voor een parameter, in de gevraagde taal. */
export function genereerVoorbeeldwaarde(parameterNaam: string, taal: Taal, parameter?: ModelParameter): string {
  if (parameter?.voorbeeld?.trim()) return parameter.voorbeeld.trim();
  const gevonden = PATRONEN.find((p) => p.regex.test(parameterNaam));
  if (gevonden) return gevonden[taal];
  if (parameter?.omschrijving?.trim()) return kortUitOmschrijving(parameter.omschrijving);
  return `(${verwoordParameternaam(parameterNaam)})`;
}

/**
 * Bouwt een waarden-dictionary voor elke parameternaam die effectief in de
 * meegegeven teksten voorkomt (`extraheerParameters`), zodat er nooit een
 * "[AAN TE VULLEN: …]" overblijft in een demo-weergave. `parameters` levert
 * per-onderdeel `voorbeeld`-hints (indien aanwezig) als voorkeursbron.
 */
export function genereerVoorbeeldwaarden(
  parameterNamen: string[],
  parameters: ModelParameter[],
  taal: Taal
): Record<string, string> {
  // Dezelfde parameternaam kan door meerdere onderdelen in de bibliotheek
  // gedeclareerd zijn (bv. "jaar_voluit" in zowel aanhef-akte als volmacht),
  // vaak met een verschillende — en niet altijd even informatieve —
  // omschrijving. Een declaratie MET een expliciete `voorbeeld`-hint is
  // altijd de betere bron dan een latere declaratie zonder, ongeacht de
  // volgorde waarin de onderdelen hier binnenkomen (anders wint willekeurig
  // de laatste, wat een goed voorbeeld stil kan overschrijven met een kale
  // omschrijving zoals "Jaartal voluit.").
  const perNaam = new Map<string, ModelParameter>();
  for (const p of parameters) {
    const bestaand = perNaam.get(p.naam);
    if (!bestaand || (!bestaand.voorbeeld?.trim() && p.voorbeeld?.trim())) {
      perNaam.set(p.naam, p);
    }
  }
  const waarden: Record<string, string> = {};
  for (const naam of parameterNamen) {
    waarden[naam] = genereerVoorbeeldwaarde(naam, taal, perNaam.get(naam));
  }
  return waarden;
}

/** Eén onderdeel, ingevuld met plausibele voorbeeldwaarden (basistekst + elke hypothese apart). */
export interface OnderdeelVoorbeeld {
  basis: string;
  varianten: { hypothese: string; tekst: string }[];
}

/**
 * Vult een modelonderdeel in met gegenereerde voorbeeldwaarden i.p.v.
 * dossierwaarden — voor de demo-weergave in OnderdelenTab/ValiderenTab.
 * `akteType` bepaalt de rolnamen ({{rol_overdrager}}/{{rol_verkrijger}});
 * ontbreekt die, dan valt de functie terug op het eerste toepasbare aktetype.
 */
export function vulOnderdeelVoorbeeldIn(onderdeel: Modelonderdeel, akteType?: string): OnderdeelVoorbeeld {
  const taal: Taal = onderdeel.taal === "fr" ? "fr" : "nl";
  const type = akteType ?? onderdeel.toepasbaarOp[0] ?? onderdeel.categorie;
  const teksten = [onderdeel.tekst, ...onderdeel.varianten.map((v) => v.tekst)];
  const waarden = genereerVoorbeeldwaarden(extraheerParameters(...teksten), onderdeel.parameters, taal);
  return {
    basis: onderdeel.tekst.trim() ? vulParametersIn(onderdeel.tekst, waarden, type, taal) : "",
    varianten: onderdeel.varianten.map((v) => ({
      hypothese: v.hypothese,
      tekst: vulParametersIn(v.tekst, waarden, type, taal),
    })),
  };
}

/** Platte weergave van `vulOnderdeelVoorbeeldIn` (basistekst + hypotheses onder elkaar), voor gebruik in een enkel tekstblok. */
export function vulOnderdeelVoorbeeldPlatIn(onderdeel: Modelonderdeel, akteType?: string): string {
  const { basis, varianten } = vulOnderdeelVoorbeeldIn(onderdeel, akteType);
  const delen = [basis, ...varianten.map((v) => `▸ HYPOTHESE — ${v.hypothese}:\n${v.tekst}`)];
  return delen.filter((d) => d.trim()).join("\n\n");
}

/**
 * Genereert het volledige model, ingevuld met voorbeeldwaarden i.p.v.
 * dossierwaarden (alle onderdelen/hypotheses behouden, zie `standaardKeuzes`)
 * — voor de demo-weergave in ModellenTab/ValiderenTab.
 */
/**
 * Korte repertorium-label per `akteType`, voor de "{{aktetype_naam}} AKTE"-
 * kop in "INFORMATIE POST AKTE" (onderdeel `hoofding-akte`/`hoofding-akte-fr`
 * in vorm.ts/frans.ts). Die placeholder droeg tot voor kort een vast
 * `voorbeeld: "VERKOOP"`/`"VENTE"` op de parameterdeclaratie zelf — correct
 * voor een verkoopakte, maar manifest fout voor elk ander aktetype (een
 * zorgvolmacht of oprichtingsakte toonde dan ook "VERKOOP AKTE"/"VENTE
 * ACTE"). Hier per `model.akteType` overschreven met een passend label.
 */
const AKTETYPE_LABELS: Record<string, { nl: string; fr: string }> = {
  "aangifte-nalatenschap": { nl: "AANGIFTE NALATENSCHAP", fr: "DÉCLARATION SUCCESSION" },
  "aanpassing-statuten-besloten-vennootschap": { nl: "STATUTENWIJZIGING", fr: "MODIFICATION STATUTS" },
  "akte-erfopvolging": { nl: "ERFOPVOLGING", fr: "HÉRÉDITÉ" },
  "algemeen kader (vastgoedakte)": { nl: "VASTGOED", fr: "IMMOBILIER" },
  basisakte: { nl: "BASISAKTE", fr: "ACTE DE BASE" },
  "echtscheiding-onderlinge-toestemming": { nl: "ECHTSCHEIDING", fr: "DIVORCE" },
  erfovereenkomst: { nl: "ERFOVEREENKOMST", fr: "PACTE SUCCESSORAL" },
  handlichting: { nl: "HANDLICHTING", fr: "MAINLEVÉE" },
  huwelijkscontract: { nl: "HUWELIJKSCONTRACT", fr: "CONTRAT DE MARIAGE" },
  "hypothecair-mandaat": { nl: "HYPOTHECAIR MANDAAT", fr: "MANDAT HYPOTHÉCAIRE" },
  keuzetestament: { nl: "TESTAMENT", fr: "TESTAMENT" },
  kredietakte: { nl: "KREDIET", fr: "CRÉDIT" },
  "minnelijke ontbinding verkoopovereenkomst": { nl: "MINNELIJKE ONTBINDING", fr: "RÉSILIATION AMIABLE" },
  "omzetting-vennootschap": { nl: "OMZETTING", fr: "TRANSFORMATION" },
  "onderhandse volmacht": { nl: "VOLMACHT", fr: "PROCURATION" },
  "ontbinding-vereffening-vennootschap": { nl: "ONTBINDING EN VEREFFENING", fr: "DISSOLUTION ET LIQUIDATION" },
  "openbare-verkoop": { nl: "OPENBARE VERKOOP", fr: "VENTE PUBLIQUE" },
  "oprichting-vennootschap": { nl: "OPRICHTING", fr: "CONSTITUTION" },
  ruil: { nl: "RUIL", fr: "ÉCHANGE" },
  samenlevingsovereenkomst: { nl: "SAMENLEVINGSOVEREENKOMST", fr: "CONTRAT DE COHABITATION" },
  schenkingsakte: { nl: "SCHENKING", fr: "DONATION" },
  "schenkingsakte-roerend-goed": { nl: "SCHENKING", fr: "DONATION" },
  "testament-gezinswoning-partner-restlegaat-kinderen": { nl: "TESTAMENT", fr: "TESTAMENT" },
  "verdeling-uit-onverdeeldheid": { nl: "VERDELING", fr: "PARTAGE" },
  verkavelingsakte: { nl: "VERKAVELING", fr: "LOTISSEMENT" },
  verkoopakte: { nl: "VERKOOP", fr: "VENTE" },
  "verkoopakte - heldere taal": { nl: "VERKOOP", fr: "VENTE" },
  "verkoopakte - lijfrente": { nl: "VERKOOP LIJFRENTE", fr: "VENTE RENTE VIAGÈRE" },
  "verkoopakte - wet breyne": { nl: "VERKOOP WET BREYNE", fr: "VENTE LOI BREYNE" },
  "verkoopovereenkomst (compromis)": { nl: "VERKOOP", fr: "VENTE" },
  "verkoopovereenkomst (compromis) - heldere taal": { nl: "VERKOOP", fr: "VENTE" },
  "verkoopovereenkomst (compromis) - lijfrente": { nl: "VERKOOP LIJFRENTE", fr: "VENTE RENTE VIAGÈRE" },
  "verkoopovereenkomst (compromis) - wet breyne": { nl: "VERKOOP WET BREYNE", fr: "VENTE LOI BREYNE" },
  "vestiging-erfpacht-opstal": { nl: "ERFPACHT/OPSTAL", fr: "EMPHYTÉOSE/SUPERFICIE" },
  volmacht: { nl: "VOLMACHT", fr: "PROCURATION" },
  "wijziging-huwelijksstelsel": { nl: "WIJZIGING HUWELIJKSSTELSEL", fr: "MODIFICATION RÉGIME MATRIMONIAL" },
  zorgvolmacht: { nl: "ZORGVOLMACHT", fr: "MANDAT DE PROTECTION" },
};

/** Repertoriumlabel voor `model.akteType`; onbekend type → afgeleid uit de akteType-string zelf (nooit "VERKOOP"). */
function aktetypeLabel(akteType: string, taal: Taal): string {
  const bekend = AKTETYPE_LABELS[akteType.toLowerCase().trim()];
  if (bekend) return bekend[taal];
  return akteType
    .replace(/\s*\([^)]*\)/g, "")
    .replace(/[-–]/g, " ")
    .trim()
    .toUpperCase();
}

export function vulModelVoorbeeldIn(
  model: Modeldocument,
  onderdelen: Modelonderdeel[],
  /**
   * Waarden en partij-herhalingen uit de ÉCHTE dossierketen (opdracht notaris
   * 2026-07-11: "voorbeeld moet echte workflow volgen"). De aanroeper — de
   * lib-laag, die wél bij lib/dossier kan — levert ze aan uit een fictief
   * demo-dossier (zie lib/modeldocumenten/voorbeeld-workflow.ts); de
   * PATRONEN-waarden hierboven blijven enkel de terugval voor parameters die
   * geen dossierbron hebben. Zonder dit argument (audits, losse
   * onderdeel-previews) blijft het gedrag ongewijzigd.
   */
  workflow?: { waarden?: Record<string, string>; partijHerhalingen?: Herhaling[] }
): string {
  const taal: Taal = model.taal === "fr" ? "fr" : "nl";
  const keuzes = standaardKeuzes(model);
  // Enkel parameters uit onderdelen in DEZELFDE taal als het model: eenzelfde
  // parameternaam kan in de NL- én de FR-tegenhanger van een onderdeel
  // voorkomen (bv. "jaar_voluit" in aanhef-akte/aanhef-akte-fr), elk met een
  // eigen `voorbeeld` in die taal — zonder deze filter zou de eerst
  // aangetroffen declaratie (ongeacht taal) haar voorbeeld aan dit model
  // opdringen.
  const alleParameters = onderdelen
    .filter((o) => (o.taal === "fr" ? "fr" : "nl") === taal)
    .flatMap((o) => o.parameters);
  const waarden = {
    ...genereerVoorbeeldwaarden(verzamelParameters(model, onderdelen, keuzes), alleParameters, taal),
    ...(workflow?.waarden ?? {}),
  };
  if ("aktetype_naam" in waarden) {
    waarden.aktetype_naam = aktetypeLabel(model.akteType, taal);
  }
  return genereerDocument(model, onderdelen, keuzes, waarden, workflow?.partijHerhalingen ?? []);
}
