<%*
/* PF2e AI Session Summary — CHUNKED + ROBUST (AI for Templater)
   - Summarizes each section separately to avoid long-context issues
   - Preserves [[wikilinks]]; strips headings/labels/scaffolding
   - Writes the final recap between SUMMARY markers
*/

const file = app.workspace.getActiveFile();
if (!file) { new Notice("Open a session note first."); return; }

// ---------- read & basic cleaning ----------
let raw = await app.vault.read(file);
raw = raw.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, ""); // strip YAML
raw = raw.replace(/<!--\s*SUMMARY-START\s*-->[\s\S]*?<!--\s*SUMMARY-END\s*-->/gi, "");
raw = raw.replace(/```[\s\S]*?```/g, "");                // code fences
raw = raw.replace(/`=\s*[^`]+`/g, "");                   // inline dataview
raw = raw.replace(/\r/g, "");

// ---------- helpers ----------
function sectionBetween(names){
  for (const name of names){
    const reHead = new RegExp(`^##\\s*.*${name}.*$`,"im");
    const m = raw.match(reHead);
    if (!m) continue;
    const start = m.index + m[0].length;
    const rest = raw.slice(start);
    const next = rest.search(/^##\s+/m);
    return raw.slice(start, next === -1 ? raw.length : start + next).trim();
  }
  return "";
}
function linesClean(block){
  if (!block) return [];
  const BOLD_LABEL=/^\s*(?:[-*]\s+)?\*\*[^*]+:\*\*\s*$/i;  // **Label:**
  const ROUND=/^\s*(?:[-*]\s*)?round\s+\d+\s*$/i;
  const HR=/^\s*[-*_]{3,}\s*$/;
  const QUOTE=/^\s*>/;
  const H=/^\s*#{1,6}\s+/;
  return block.split("\n")
    .map(s=>s.trim())
    .filter(s=>s && !BOLD_LABEL.test(s) && !ROUND.test(s) && !HR.test(s) && !QUOTE.test(s) && !H.test(s))
    .map(s=>s.replace(/\[\[\s*\]\]/g,""))
    .map(s=>s.replace(/\s+—\s*$/,""));
}

// ---------- pull sections (accept emoji or plain headings) ----------
const S = {
  whereWhen : sectionBetween(["Where / When"]),
  pcs       : sectionBetween(["PCs Present / Absent","PCs Present","Present / Absent"]),
  highlights: sectionBetween(["⭐ Highlights","Highlights"]),
  quests    : sectionBetween(["🎯 Quests & Progress","Quests & Progress","Quests"]),
  npcs      : sectionBetween(["🗣️ NPCs Met / Factions","NPCs Met / Factions","NPCs / Factions","NPCs"]),
  encounters: sectionBetween(["⚔️ Encounters","Encounters"]),
  intel     : sectionBetween(["🧠 Intel Captured","Intel Captured"]),
  treasure  : sectionBetween(["📦 Treasure & Rewards","Treasure & Rewards","Treasure"]),
  nextHooks : sectionBetween(["🔜 Next Hooks / To-Dos","Next Hooks / To-Dos","Next Hooks","Hooks"]),
};

// ---------- chunk prompts ----------
async function summarizeBullets(label, text, max=6){
  text = linesClean(text).join("\n").trim();
  if (!text) return [];
  const prompt = `You are a concise Pathfinder 2e GM assistant.
Summarize the "${label}" section below as **bullet points only** (max ${max}). 
Keep existing [[wikilinks]] exactly as written. Do not echo headings, labels, or placeholders.

=== ${label.toUpperCase()} START ===
${text}
=== ${label.toUpperCase()} END ===`;
  const out = await tp.ai.chat(prompt);
  return String(out)
    .split(/\n+/)
    .map(s=>s.replace(/^\s*[-*•]\s*/,"").trim())
    .filter(Boolean)
    .slice(0, max);
}

async function oneLineRecap(name, date, allBullets){
  const prompt = `Write a **single bold one-line recap** for a PF2e session called "${name}" on ${date}.
Base it ONLY on these bullets:\n${allBullets.map(b=>`- ${b}`).join("\n")}\nReturn exactly one bold line.`;
  return String(await tp.ai.chat(prompt)).trim();
}

// ---------- run chunked summaries ----------
const cache = app.metadataCache.getFileCache(file) || {};
const fm = cache.frontmatter || {};
const seshName = fm.Session_Name || tp.file.title || file.basename;
const seshDate = fm.Date || tp.date.now("YYYY-MM-DD");

const hi    = await summarizeBullets("Highlights", S.highlights, 8);
const enc   = await summarizeBullets("Encounters", S.encounters, 6);
const intel = await summarizeBullets("Intel", S.intel, 6);
const quests= await summarizeBullets("Quests", S.quests, 6);
const nexts = await summarizeBullets("Next Hooks", S.nextHooks, 6);
const npcfx = await summarizeBullets("NPCs & Factions", S.npcs, 1); // we’ll show names inline instead of bullets
const loot  = await summarizeBullets("Treasure & Rewards", S.treasure, 3);

const allForRecap = [].concat(hi, enc, intel, quests, nexts).slice(0, 24);
let oneLine = await oneLineRecap(seshName, seshDate, allForRecap);
if (!oneLine.startsWith("**")) oneLine = `**${seshName} — ${seshDate}:** ${allForRecap[0] || "Session recap."}`;

// ---------- compose final ----------
const out = [];
out.push(oneLine);
if (hi.length)    out.push(...hi.map(b=>`- ${b}`));
if (enc.length)   out.push(...enc.map(b=>`- ${b}`));
if (intel.length) out.push(...intel.map(b=>`- ${b}`));
if (quests.length)out.push(...quests.map(b=>`- ${b}`));
if (npcfx.length) out.push(`- NPCs/Factions touched: ${npcfx[0].replace(/^NPCs?:\s*/i,"")}`);
if (loot.length)  out.push(...loot.map(b=>`- ${b}`));
if (nexts.length) out.push(...nexts.map(b=>`- ${b}`));

// ---------- inject between markers ----------
const START="<!-- SUMMARY-START -->", END="<!-- SUMMARY-END -->";
let content = await app.vault.read(file);
const sIdx = content.indexOf(START), eIdx = content.indexOf(END);
if (sIdx===-1 || eIdx===-1 || eIdx<sIdx){ new Notice("Could not find SUMMARY markers."); return; }
await app.vault.modify(file, content.slice(0, sIdx+START.length) + "\n" + out.join("\n") + "\n" + content.slice(eIdx));
new Notice("AI (chunked) session summary generated.");
%>
