import { describe, it, expect } from "vitest";
import JSZip from "jszip";
import { genereerOntwerpWordDocument } from "./genereerOntwerpWordDocument";

async function leesDocumentXml(buffer: Buffer): Promise<string> {
  const zip = await JSZip.loadAsync(buffer);
  return zip.file("word/document.xml")!.async("text");
}

async function leesSettingsXml(buffer: Buffer): Promise<string> {
  const zip = await JSZip.loadAsync(buffer);
  return zip.file("word/settings.xml")!.async("text");
}

async function leesStylesXml(buffer: Buffer): Promise<string> {
  const zip = await JSZip.loadAsync(buffer);
  return zip.file("word/styles.xml")!.async("text");
}

async function leesCoreXml(buffer: Buffer): Promise<string> {
  const zip = await JSZip.loadAsync(buffer);
  return zip.file("docProps/core.xml")!.async("text");
}

async function leesVoettekstXml(buffer: Buffer): Promise<string> {
  const zip = await JSZip.loadAsync(buffer);
  const naam = Object.keys(zip.files).find((n) => /word\/footer\d*\.xml/.test(n));
  return naam ? zip.file(naam)!.async("text") : "";
}

describe("genereerOntwerpWordDocument", () => {
  it("genereert een geldig .docx-bestand (ZIP-magicbytes) uit platte ontwerptekst", async () => {
    const ontwerp = "1. IDENTIFICATIE\n\nDe heer Jan Janssens.\n\n▸ Ongehuwd.";
    const buffer = await genereerOntwerpWordDocument(ontwerp);
    expect(buffer.subarray(0, 2).toString("latin1")).toBe("PK");
  });

  it("begint met de instructiesectie voor de AI Agent vóór het ontwerp", async () => {
    const buffer = await genereerOntwerpWordDocument("1. IDENTIFICATIE\n\nDe heer Jan Janssens.");
    const xml = await leesDocumentXml(buffer);
    const posInstructies = xml.indexOf("Instructies voor de AI Agent");
    const posOntwerp = xml.indexOf("2. Ontwerpdocument");
    expect(posInstructies).toBeGreaterThan(-1);
    expect(posOntwerp).toBeGreaterThan(-1);
    expect(posInstructies).toBeLessThan(posOntwerp);
    expect(xml).toContain("Stuur deze gegevens nooit naar of via Notary.AI");
  });

  it("bevat het Structuuranker met de oorspronkelijke hoofdstukkenlijst, vóór het ontwerp", async () => {
    const ontwerp = "## 1. IDENTIFICATIE\n\nDe heer Jan Janssens.\n\n## 2. PRIJS\n\nDe prijs bedraagt …";
    const buffer = await genereerOntwerpWordDocument(ontwerp);
    const xml = await leesDocumentXml(buffer);
    const posAnker = xml.indexOf("Structuuranker");
    const posOntwerp = xml.indexOf("2. Ontwerpdocument");
    expect(posAnker).toBeGreaterThan(-1);
    expect(posAnker).toBeLessThan(posOntwerp);
    expect(xml).toContain("1. IDENTIFICATIE");
    expect(xml).toContain("2. PRIJS");
  });

  it("schakelt 'Wijzigingen bijhouden' (track changes) in op documentniveau", async () => {
    const buffer = await genereerOntwerpWordDocument("1. IDENTIFICATIE");
    const xml = await leesSettingsXml(buffer);
    // Het echte OOXML-element (w:trackChanges) staat aan; de onwerkzame
    // w:trackRevisions-marker van de docx-bibliotheek is opgeruimd.
    expect(xml).toMatch(/<w:trackChanges\s*\/>/);
    expect(xml).not.toContain("trackRevisions");
  });

  it("gebruikt uitvullen (justify) als standaard alinea-uitlijning", async () => {
    const buffer = await genereerOntwerpWordDocument("1. IDENTIFICATIE\n\nDe heer Jan Janssens.");
    const xml = await leesStylesXml(buffer);
    const docDefaults = xml.match(/<w:docDefaults>[\s\S]*?<\/w:docDefaults>/)?.[0] ?? "";
    expect(docDefaults).toMatch(/<w:jc w:val="both"\s*\/>/);
  });

  it("elke koppenstijl (Titel, Kop 1-4) houdt de kop bij de volgende alinea (keepNext)", async () => {
    const buffer = await genereerOntwerpWordDocument("## 1. IDENTIFICATIE\n\nDe heer Jan Janssens.");
    const xml = await leesStylesXml(buffer);
    for (const styleId of ["Title", "Heading1", "Heading2", "Heading3", "Heading4"]) {
      const stijl = xml.match(new RegExp(`<w:style [^>]*w:styleId="${styleId}"[^>]*>[\\s\\S]*?</w:style>`))?.[0] ?? "";
      expect(stijl, `stijl ${styleId} bevat keepNext`).toMatch(/<w:keepNext\s*\/>/);
    }
  });

  it("zet de spellingtaal op nl-BE (NL) resp. fr-BE (FR) zodat Word in de juiste taal spellingcontroleert", async () => {
    const docDefaults = async (taal: "nl" | "fr") => {
      const xml = await leesStylesXml(await genereerOntwerpWordDocument("1. IDENTIFICATIE", taal));
      return xml.match(/<w:docDefaults>[\s\S]*?<\/w:docDefaults>/)?.[0] ?? "";
    };
    expect(await docDefaults("nl")).toMatch(/<w:lang w:val="nl-BE"/);
    expect(await docDefaults("fr")).toMatch(/<w:lang w:val="fr-BE"/);
  });

  it("vult de documentmetadata (auteur/titel) in docProps/core.xml", async () => {
    const core = await leesCoreXml(await genereerOntwerpWordDocument("1. IDENTIFICATIE"));
    expect(core).toContain("<dc:creator>Notary.AI</dc:creator>");
    expect(core).toMatch(/<dc:title>[^<]*Notary\.AI<\/dc:title>/);
  });

  it("bevat een klikbare inhoudsopgave (TOC-veld) ná de instructiepagina's en vóór de aktetekst, met updateFields aan (backlog punt 31)", async () => {
    const buffer = await genereerOntwerpWordDocument("## 1. IDENTIFICATIE\n\nDe heer Jan Janssens.");
    const xml = await leesDocumentXml(buffer);
    expect(xml).toContain("Inhoudsopgave");
    expect(xml).toMatch(/TOC[^<]*\\o[^<]*1-4/);
    const posKop = xml.indexOf("2. Ontwerpdocument");
    const posToc = xml.search(/TOC[^<]*\\o/);
    const posOntwerp = xml.indexOf("1. IDENTIFICATIE", posToc);
    expect(posKop).toBeGreaterThan(-1);
    expect(posToc).toBeGreaterThan(posKop);
    expect(posOntwerp).toBeGreaterThan(posToc);
    const settings = await leesSettingsXml(buffer);
    expect(settings).toMatch(/<w:updateFields\s*\/>|<w:updateFields\b[^>]*w:val="(1|true)"/);
  });

  it("gebruikt 'Table des matières' als FR-titel van de inhoudsopgave", async () => {
    const buffer = await genereerOntwerpWordDocument("## 1. IDENTIFICATIE", "fr");
    const xml = await leesDocumentXml(buffer);
    expect(xml).toContain("Table des matières");
  });

  it("voegt een paginavoettekst met een levend paginanummer-veld toe", async () => {
    const nl = await leesVoettekstXml(await genereerOntwerpWordDocument("1. IDENTIFICATIE", "nl"));
    expect(nl).toContain("Pagina ");
    expect(nl).toMatch(/PAGE/);
    const fr = await leesVoettekstXml(await genereerOntwerpWordDocument("1. IDENTIFICATIE", "fr"));
    expect(fr).toContain("Page ");
  });
});
