/* pdf_localization.js — Tier-2 PDF hash-tree localization (proven parity port).
 * Mirrors mysterion-app/lib/crypto/pdf_localization.dart byte-for-byte
 * (validated 46/46 fixtures + PDFium-WASM extraction parity on c4611).
 * Exposes globalThis.MysterionPdfLoc with normalize/split/buildTree/compareTree.
 * Hash fn is injected (blake3hex from blake3.js). */
(function () {
  'use strict';
  const _WS = /\s+/g;                 // Dart \s is Unicode-aware; JS /\s+/ matches.
  const _NUM_RUN = /[0-9.]+/g;
  const _SENT = /(?<=[.])\s+(?=[A-Z])/;

  function normalizePdfText(input) {
    const collapsed = input.replace(_WS, ' ').replace(/^ | $/g, '');
    return collapsed.replace(_NUM_RUN, (run) => {
      if (!/[0-9]/.test(run)) return run;
      const dots = (run.match(/\./g) || []).length;
      if (dots > 1) return run;
      const dot = run.indexOf('.');
      if (dot < 0) return run;
      let i = run.slice(0, dot), f = run.slice(dot + 1);
      if (i === '') i = '0';
      f = f.replace(/0+$/, '');
      return f === '' ? i : i + '.' + f;
    });
  }
  function splitSegments(pageText) {
    const n = normalizePdfText(pageText);
    if (n === '') return [];
    return n.split(_SENT).map((p) => p.replace(/^\s+|\s+$/g, '')).filter((p) => p !== '');
  }
  function buildTree(pageTexts, hashFn) {
    const pages = [], paragraphs = [];
    for (const t of pageTexts) {
      pages.push(hashFn(normalizePdfText(t)));
      paragraphs.push(splitSegments(t).map(hashFn));
    }
    return { v: 1, pages, paragraphs };
  }
  function compareTree(pageTexts, reference, hashFn) {
    const changes = [];
    const nP = pageTexts.length, rP = reference.pages.length, common = Math.min(nP, rP);
    for (let i = 0; i < common; i++) {
      const cur = hashFn(normalizePdfText(pageTexts[i]));
      if (cur === reference.pages[i]) continue;
      const cp = splitSegments(pageTexts[i]).map(hashFn);
      const rp = reference.paragraphs[i] || [];
      if (cp.length !== rp.length) {
        let at = 0; const lim = Math.min(cp.length, rp.length);
        while (at < lim && cp[at] === rp[at]) at++;
        changes.push({ page: i + 1, paragraphs: [], paragraphCountChanged: true, divergesAtParagraph: at + 1 });
      } else {
        const diff = [];
        for (let p = 0; p < cp.length; p++) if (cp[p] !== rp[p]) diff.push(p + 1);
        changes.push({ page: i + 1, paragraphs: diff, paragraphCountChanged: false });
      }
    }
    if (nP !== rP) for (let i = common; i < nP; i++)
      changes.push({ page: i + 1, paragraphs: [], paragraphCountChanged: true, divergesAtParagraph: 1 });
    return { intact: changes.length === 0, changes };
  }
  globalThis.MysterionPdfLoc = { normalizePdfText, splitSegments, buildTree, compareTree };
})();
