#!/usr/bin/env node
/**
 * Generate the §9 ablation corpus from this repo's real exported symbols/files.
 *
 * Each task is a CHECKABLE REPO-FACT QUESTION: "which file defines `<name>`,
 * and what are its parameters?" — not a code-writing task. A wrong answer is an
 * unambiguous, checkable hallucination (the file path), and the prompt forbids
 * example code, so the guard measures GROUNDING rather than guard precision on
 * illustrative scaffolding. The grounded arm (given exact signatures grouped by
 * file) answers correctly; the ungrounded arm must guess → hallucinates.
 * Deterministic (index order + a fixed stride), so re-running is reproducible.
 *
 *   node scripts/gen-ablation-corpus.mjs            # write benchmarks/llm-ablation-tasks.json
 *   node scripts/gen-ablation-corpus.mjs --count 100
 */

import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { createRequire } from 'module';

const require = createRequire(import.meta.url);
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const ROOT = path.resolve(__dirname, '..');
const { buildSigIndex } = require(path.join(ROOT, 'src/retrieval/ranker.js'));

const countIdx = process.argv.indexOf('--count');
const COUNT = countIdx !== -1 && process.argv[countIdx + 1] ? parseInt(process.argv[countIdx + 1], 10) : 100;

/** Pull the function/def name out of a signature string. */
function fnName(sig) {
  const m = String(sig).match(/\b(?:async\s+function|function|def)\s+([A-Za-z_$][\w$]*)/);
  return m ? m[1] : null;
}

function slug(s) { return String(s).replace(/[^A-Za-z0-9]+/g, '-').replace(/^-|-$/g, '').toLowerCase(); }

// Collect (file, fn) candidates — one representative function per file, in index order.
const index = buildSigIndex(ROOT);
const candidates = [];
for (const [file, sigs] of index) {
  const rel = path.relative(ROOT, file).replace(/\\/g, '/');
  if (!rel.startsWith('src/') && !rel.startsWith('packages/')) continue;
  for (const sig of sigs || []) {
    const name = fnName(sig);
    if (name && !name.startsWith('_')) { candidates.push({ rel, name }); break; }
  }
}

// Even spread across the file list so tasks span many subsystems.
const picked = [];
const stride = Math.max(1, Math.floor(candidates.length / COUNT));
for (let i = 0; i < candidates.length && picked.length < COUNT; i += stride) picked.push(candidates[i]);
for (let i = 0; i < candidates.length && picked.length < COUNT; i++) {
  if (!picked.includes(candidates[i])) picked.push(candidates[i]);
}

const seen = new Set();
const tasks = picked.map(({ rel, name }) => {
  // Unique, informative id: fact-<name>-<file-stem> (with a fallback on collision).
  const stem = slug(rel.replace(/\.[^.]+$/, '').split('/').pop());
  let id = `fact-${slug(name)}-${stem}`;
  while (seen.has(id)) id += '-x';
  seen.add(id);
  return {
    id,
    prompt: `In the SigMap repository, the \`${name}\` function is defined in exactly one source file. Reply with: (1) the full repository path to that file (e.g. \`src/foo/bar.js\`), and (2) the exact parameter list of \`${name}\`. Reference only real files, symbols, and parameters that exist in this repository — do not invent paths, and do not write illustrative or example code.`,
  };
});

const out = path.join(ROOT, 'benchmarks', 'llm-ablation-tasks.json');
const payload = {
  _comment: `LLM A/B hallucination ablation corpus (IMPL §9), generated by scripts/gen-ablation-corpus.mjs from real repo symbols. ${tasks.length} checkable repo-fact questions (which file defines <name>, what params) — a wrong file path is a checkable hallucination; prompts forbid example code so the metric isolates grounding. Run: npm run benchmark:llm-ablation (needs ANTHROPIC_API_KEY or GEMINI_API_KEY).`,
  tasks,
};
fs.writeFileSync(out, JSON.stringify(payload, null, 2) + '\n');
console.log(`[gen-ablation-corpus] wrote ${tasks.length} tasks → benchmarks/llm-ablation-tasks.json`);
