// ── OOXML-helpers voor template-merge ────────────────────────────────────────
// Kleine bouwstenen om WordprocessingML-paragrafen als string te genereren,
// zodat we ze rechtstreeks in het document.xml van een bestaand .docx-sjabloon
// kunnen injecteren (met behoud van de benoemde stijlen, kaders, icoontjes en
// het lettertype van het sjabloon). Geen docx-library hier: die bouwt een eigen
// package en kan geen bestaand sjabloon overnemen.

import { segmenteer } from "@/lib/word/runs";

/**
 * XML-escape voor tekst binnen <w:t> én binnen attribuutwaarden (bv.
 * w:author="…" in track-changes): ook de aanhalingstekens worden ge-escapet,
 * anders breekt een waarde met een '"' de attribuutsyntax en is het hele
 * document.xml ongeldig.
 */
export function escapeXml(tekst: string): string {
  return tekst
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&apos;");
}

export interface RunOpties {
  bold?: boolean;
  italics?: boolean;
  /**
   * Onderdruk de spelling-/grammaticacontrole op deze runs (w:noProof). Voor
   * de werkblad-/instructiepagina's die vóór gebruik worden verwijderd: zo
   * tekent Word geen rode kronkellijnen op prompttekst.
   */
  noProof?: boolean;
}

export interface ParagraafOpties {
  /** Benoemde alineastijl (w:pStyle), bv. "Kop1", "Kop2", "Hoofdstukken". */
  stijl?: string;
  run?: RunOpties;
  /** Arceringskleur (hex, bv. "FFF4CE") voor een gemarkeerde alinea. */
  arcering?: string;
  /**
   * Onderdruk lijstnummering op deze alinea (numId 0). Nodig voor koppen die een
   * stijl erven welke via basedOn aan een genummerde lijst hangt (bv. Kop2 →
   * Inhoud): zo blijven het echte koppen zonder ongewenste "1.1."-nummering.
   */
  geenNummering?: boolean;
  /**
   * Vervangt een statisch gebakken volgnummer door een echt Word-veld (SEQ):
   * `naam` is de gedeelde teller (bv. "Clausule") en `waarde` het nummer zoals
   * berekend bij het genereren — dat blijft de cache-tekst die Word toont tot de
   * velden vernieuwd worden. Zo herbereken Word (bij het openen, via
   * `updateFields` in settings.xml, of manueel met Ctrl+A F9) de volgnummers
   * automatisch zodra de notaris een clausule schrapt of toevoegt, zonder de
   * lijststijl-nummering te gebruiken (die eerder ongewenste, inconsistente
   * "1.1."-nummering gaf — zie compromisSjabloon.ts).
   */
  seqVeld?: { naam: string; waarde: number };
  /**
   * Vervangt een statisch gebakken "N.M."-tussentitelnummer door twee echte
   * Word-velden: het hoofdnummer (\c = herhaal de laatste waarde van de
   * gedeelde clausuleteller zonder ze op te hogen) en een tweede, geneste
   * teller die per clausule herstart (\r 1 op de EERSTE tussentitel van een
   * clausule, anders een gewone doorlopende SEQ). Zonder dit blijft "N.M."
   * statische tekst die stil incorrect wordt zodra de notaris een clausule
   * schrapt/toevoegt en het hoofdnummer via het SEQ-veld hierboven herrekend
   * wordt — precies het lek dat deze optie dicht.
   */
  seqSubVeld?: { hoofdNaam: string; subNaam: string; hoofdWaarde: number; subWaarde: number; herstart: boolean };
}

/** Eén <w:fldSimple>-achtige veld-run-reeks (begin/instrText/separate/cache/end). */
function veldRuns(instructie: string, cacheTekst: string): string {
  return (
    `<w:r><w:fldChar w:fldCharType="begin"/></w:r>` +
    `<w:r><w:instrText xml:space="preserve"> ${instructie} </w:instrText></w:r>` +
    `<w:r><w:fldChar w:fldCharType="separate"/></w:r>` +
    `<w:r><w:t>${cacheTekst}</w:t></w:r>` +
    `<w:r><w:fldChar w:fldCharType="end"/></w:r>`
  );
}

/** Bouwt de OOXML voor een SEQ-veld ("<naam>. ") met de opgegeven cache-waarde. */
function seqVeldRuns(veld: { naam: string; waarde: number }): string {
  return veldRuns(`SEQ ${veld.naam} \\* ARABIC`, String(veld.waarde)) + `<w:r><w:t xml:space="preserve">. </w:t></w:r>`;
}

/** Bouwt de OOXML voor een "N.M. "-tussentitelnummer met twee geneste SEQ-velden. */
function seqSubVeldRuns(veld: { hoofdNaam: string; subNaam: string; hoofdWaarde: number; subWaarde: number; herstart: boolean }): string {
  const subInstructie = veld.herstart ? `SEQ ${veld.subNaam} \\r 1 \\* ARABIC` : `SEQ ${veld.subNaam} \\* ARABIC`;
  return (
    veldRuns(`SEQ ${veld.hoofdNaam} \\c \\* ARABIC`, String(veld.hoofdWaarde)) +
    `<w:r><w:t>.</w:t></w:r>` +
    veldRuns(subInstructie, String(veld.subWaarde)) +
    `<w:r><w:t xml:space="preserve">. </w:t></w:r>`
  );
}

/** Bouwt één <w:p>-paragraaf met optionele stijl/arcering en run-opmaak. */
export function paragraaf(tekst: string, opties: ParagraafOpties = {}): string {
  const ppr: string[] = [];
  if (opties.stijl) ppr.push(`<w:pStyle w:val="${opties.stijl}"/>`);
  if (opties.geenNummering) ppr.push(`<w:numPr><w:ilvl w:val="0"/><w:numId w:val="0"/></w:numPr>`);
  if (opties.arcering) ppr.push(`<w:shd w:val="clear" w:color="auto" w:fill="${opties.arcering}"/>`);
  const pprXml = ppr.length ? `<w:pPr>${ppr.join("")}</w:pPr>` : "";
  const seqRuns = opties.seqVeld ? seqVeldRuns(opties.seqVeld) : opties.seqSubVeld ? seqSubVeldRuns(opties.seqSubVeld) : "";
  if (tekst === "") return `<w:p>${pprXml}${seqRuns}</w:p>`;
  // Vet voor bedragen/namen (U+E000/U+E001) en gele arcering voor commentaarhaken
  // en geel-gemarkeerde standaardverklaringen (U+E002/U+E003). segmenteer()
  // ster-markeert ook de commentaren voor de Word-zoeksneltoets.
  const runs = segmenteer(tekst)
    .map((seg) => {
      const rpr: string[] = [];
      if (opties.run?.bold || seg.vet) rpr.push("<w:b/>");
      if (opties.run?.italics) rpr.push("<w:i/>");
      // Schemavolgorde CT_RPr: noProof hoort ná b/i en vóór u/highlight.
      if (opties.run?.noProof) rpr.push("<w:noProof/>");
      if (seg.onder) rpr.push(`<w:u w:val="single"/>`);
      if (seg.geel) rpr.push(`<w:highlight w:val="yellow"/>`);
      // Hyperlink naar de woordenlijst via een HYPERLINK-veld (geen relationship
      // nodig in document.xml.rels, zodat dit ook in een sjabloon-merge werkt).
      if (seg.href) {
        const linkRpr = `<w:rPr><w:rStyle w:val="Hyperlink"/><w:color w:val="0563C1"/><w:u w:val="single"/>${rpr.join("")}</w:rPr>`;
        return (
          `<w:r><w:fldChar w:fldCharType="begin"/></w:r>` +
          `<w:r><w:instrText xml:space="preserve"> HYPERLINK "${escapeXml(seg.href)}" </w:instrText></w:r>` +
          `<w:r><w:fldChar w:fldCharType="separate"/></w:r>` +
          `<w:r>${linkRpr}<w:t xml:space="preserve">${escapeXml(seg.text)}</w:t></w:r>` +
          `<w:r><w:fldChar w:fldCharType="end"/></w:r>`
        );
      }
      const rprXml = rpr.length ? `<w:rPr>${rpr.join("")}</w:rPr>` : "";
      return `<w:r>${rprXml}<w:t xml:space="preserve">${escapeXml(seg.text)}</w:t></w:r>`;
    })
    .join("");
  return `<w:p>${pprXml}${seqRuns}${runs}</w:p>`;
}

/** Een harde pagina-einde-paragraaf. */
export function paginaEinde(): string {
  return `<w:p><w:r><w:br w:type="page"/></w:r></w:p>`;
}

/**
 * Bouwt de OOXML voor een klikbare, verversbare inhoudsopgave (TOC-veld,
 * kopniveaus 1 t.e.m. 4 — Kop1 t.e.m. Kop4 in het sjabloon). Word berekent de
 * paginanummers en de klikbare koppelingen bij het openen (`updateFields`,
 * al aangezet in settings.xml door de sjabloon-merge — zie
 * compromisSjabloon.ts/akteSjabloon.ts) of handmatig met Ctrl+A F9.
 */
export function inhoudsopgaveVeld(placeholderTekst: string): string {
  return `<w:p>${veldRuns('TOC \\o "1-4" \\h \\z \\u', escapeXml(placeholderTekst))}</w:p>`;
}
