/**
 * pres/edit/read — outline + token discovery for an existing .pptx.
 *
 * A .pptx is a ZIP archive containing slide XML files under
 *   ppt/slides/slide1.xml, slide2.xml, ...
 * Each slide XML has `<a:t>...</a:t>` text runs. We unzip in memory,
 * walk the slides, extract their text content, and detect any
 * `{{tokens}}` the agent can later fill via pres/edit/fill.
 *
 * Used by the `pres.read` action.
 */
import fs from 'fs/promises';
import { createRequire } from 'module';

const localRequire = createRequire(import.meta.url);
let JSZip = null;
function loadZip() {
  if (JSZip) return JSZip;
  JSZip = localRequire('jszip');
  return JSZip;
}

const TEXT_RUN_RE = /<a:t[^>]*>([\s\S]*?)<\/a:t>/g;
const TOKEN_RE = /\{\{\s*([a-zA-Z0-9_.-]+)\s*\}\}/g;

function decodeXml(s) {
  return String(s)
    .replace(/&amp;/g, '&')
    .replace(/&lt;/g, '<')
    .replace(/&gt;/g, '>')
    .replace(/&quot;/g, '"')
    .replace(/&apos;/g, "'");
}

function extractText(xml) {
  const lines = [];
  let m;
  TEXT_RUN_RE.lastIndex = 0;
  while ((m = TEXT_RUN_RE.exec(xml)) !== null) {
    const txt = decodeXml(m[1]).trim();
    if (txt) lines.push(txt);
  }
  return lines;
}

function extractTokens(xml) {
  const found = new Set();
  let m;
  TOKEN_RE.lastIndex = 0;
  while ((m = TOKEN_RE.exec(xml)) !== null) {
    found.add(m[1]);
  }
  return [...found];
}

/**
 * Read the structural outline of a .pptx file.
 *
 *   {
 *     slideCount,
 *     slides: [
 *       { index, title, body: [...], tokens: ['name', 'date', ...] }
 *     ],
 *     tokens: ['name', 'date', ...]    // union across all slides
 *   }
 *
 * `title` is the first non-empty text run on the slide (a reasonable
 * heuristic — PPTX has no semantic "title" annotation by default).
 */
export async function readPres(filePath) {
  const Zip = loadZip();
  const buf = await fs.readFile(filePath);
  const zip = await Zip.loadAsync(buf);

  const slideFiles = Object.keys(zip.files)
    .filter(p => /^ppt\/slides\/slide\d+\.xml$/.test(p))
    .sort((a, b) => {
      const ai = Number(a.match(/(\d+)\.xml$/)[1]);
      const bi = Number(b.match(/(\d+)\.xml$/)[1]);
      return ai - bi;
    });

  const slides = [];
  const tokenSet = new Set();

  for (let i = 0; i < slideFiles.length; i++) {
    const xml = await zip.file(slideFiles[i]).async('string');
    const runs = extractText(xml);
    const tokens = extractTokens(xml);
    tokens.forEach(t => tokenSet.add(t));
    slides.push({
      index: i,
      file: slideFiles[i],
      title: runs[0] || '',
      body: runs.slice(1),
      tokens,
    });
  }

  return {
    slideCount: slides.length,
    slides,
    tokens: [...tokenSet],
  };
}
