import JSZip from "jszip";

/**
 * Parse a .docx ArrayBuffer and extract all {{placeholder}} patterns.
 * A .docx is a ZIP; we scan all word/*.xml files for patterns.
 */
export async function parsePlaceholdersFromBuffer(
  buffer: ArrayBuffer,
): Promise<string[]> {
  const zip = await JSZip.loadAsync(buffer);

  // Collect all word/ XML files (document, headers, footers, etc.)
  const xmlFileNames = Object.keys(zip.files).filter(
    (name) => name.startsWith("word/") && name.endsWith(".xml"),
  );

  const xmlContents = await Promise.all(
    xmlFileNames.map((name) => {
      const file = zip.files[name];
      return file ? file.async("string") : Promise.resolve("");
    }),
  );

  const combined = xmlContents.join("\n");

  // Strip XML tags — placeholders may be split across <w:t> runs, so we
  // also need to find patterns that cross tag boundaries. Strategy:
  // 1) Try matching in raw XML first (most placeholders appear intact)
  // 2) Then strip tags and try again to catch split-run cases
  const rawMatches = [...combined.matchAll(/\{\{([^{}]+?)\}\}/g)].map((m) =>
    (m[1] ?? "").trim(),
  );

  const strippedText = combined.replace(/<[^>]+>/g, "");
  const strippedMatches = [...strippedText.matchAll(/\{\{([^{}]+?)\}\}/g)].map(
    (m) => (m[1] ?? "").trim(),
  );

  const all = [...rawMatches, ...strippedMatches];
  return [...new Set(all)].filter((p) => p.length > 0 && p.length < 80);
}

/**
 * Fetch a URL and parse placeholders from the .docx binary.
 * May fail if CORS blocks the fetch.
 */
export async function parsePlaceholdersFromUrl(url: string): Promise<string[]> {
  const response = await fetch(url);
  if (!response.ok)
    throw new Error(`HTTP ${response.status}: ${response.statusText}`);
  const buffer = await response.arrayBuffer();
  return parsePlaceholdersFromBuffer(buffer);
}
