// scripts/build-annotations-index.mjs
import fs from "node:fs/promises";
import path from "node:path";
import YAML from "yaml";

function parseArgs(argv) {
  const out = {
    inDir: "src/annotations",
    outFile: "dist/annotations-index.json",
  };

  for (let i = 0; i < argv.length; i++) {
    const a = argv[i];

    if (a === "--in" && argv[i + 1]) out.inDir = argv[++i];
    else if (a.startsWith("--in=")) out.inDir = a.slice("--in=".length);

    if (a === "--out" && argv[i + 1]) out.outFile = argv[++i];
    else if (a.startsWith("--out=")) out.outFile = a.slice("--out=".length);
  }
  return out;
}

async function exists(p) {
  try { await fs.access(p); return true; } catch { return false; }
}

async function walk(dir) {
  const out = [];
  const ents = await fs.readdir(dir, { withFileTypes: true });
  for (const e of ents) {
    const p = path.join(dir, e.name);
    if (e.isDirectory()) out.push(...(await walk(p)));
    else out.push(p);
  }
  return out;
}

function inferPageKeyFromFile(inDirAbs, fileAbs) {
  // src/annotations/<page>.yml -> "<page>"
  const rel = path.relative(inDirAbs, fileAbs).replace(/\\/g, "/");
  return rel.replace(/\.(ya?ml|json)$/i, "");
}

function assert(cond, msg) {
  if (!cond) throw new Error(msg);
}

function isPlainObject(x) {
  return !!x && typeof x === "object" && !Array.isArray(x);
}

function normalizePageKey(s) {
  // pas de / en tête/fin
  return String(s || "").replace(/^\/+/, "").replace(/\/+$/, "");
}

function validateAndNormalizeDoc(doc, pageKey, fileRel) {
  assert(isPlainObject(doc), `${fileRel}: document must be an object`);
  assert(doc.schema === 1, `${fileRel}: schema must be 1`);
  if (doc.page != null) {
    assert(
      normalizePageKey(doc.page) === pageKey,
      `${fileRel}: page mismatch (page="${doc.page}" vs path="${pageKey}")`
    );
  }
  assert(isPlainObject(doc.paras), `${fileRel}: missing object key "paras"`);

  const parasOut = Object.create(null);

  for (const [paraId, entry] of Object.entries(doc.paras)) {
    assert(/^p-\d+-/i.test(paraId), `${fileRel}: invalid para id "${paraId}"`);

    // entry peut être vide, mais doit être un objet si présent
    assert(entry == null || isPlainObject(entry), `${fileRel}: paras.${paraId} must be an object`);

    const e = entry ? { ...entry } : {};

    // Sanity checks (non destructifs : on n’écrase pas, on vérifie juste les types)
    if (e.refs != null) assert(Array.isArray(e.refs), `${fileRel}: paras.${paraId}.refs must be an array`);
    if (e.authors != null) assert(Array.isArray(e.authors), `${fileRel}: paras.${paraId}.authors must be an array`);
    if (e.quotes != null) assert(Array.isArray(e.quotes), `${fileRel}: paras.${paraId}.quotes must be an array`);
    if (e.media != null) assert(Array.isArray(e.media), `${fileRel}: paras.${paraId}.media must be an array`);
    if (e.comments_editorial != null) assert(Array.isArray(e.comments_editorial), `${fileRel}: paras.${paraId}.comments_editorial must be an array`);

    parasOut[paraId] = e;
  }

  return parasOut;
}

async function readDoc(fileAbs) {
  const raw = await fs.readFile(fileAbs, "utf8");
  if (/\.json$/i.test(fileAbs)) return JSON.parse(raw);
  return YAML.parse(raw);
}

async function main() {
  const { inDir, outFile } = parseArgs(process.argv.slice(2));
  const CWD = process.cwd();

  const inDirAbs = path.isAbsolute(inDir) ? inDir : path.join(CWD, inDir);
  const outAbs = path.isAbsolute(outFile) ? outFile : path.join(CWD, outFile);

  // antifragile
  if (!(await exists(inDirAbs))) {
    console.log(`ℹ️  annotations-index: skip (input missing): ${inDir}`);
    process.exit(0);
  }

  const files = (await walk(inDirAbs)).filter((p) => /\.(ya?ml|json)$/i.test(p));
  if (!files.length) {
    console.log(`ℹ️  annotations-index: skip (no .yml/.yaml/.json found in): ${inDir}`);
    process.exit(0);
  }

  const pages = Object.create(null);
  let paraCount = 0;

  for (const f of files) {
    const fileRel = path.relative(CWD, f).replace(/\\/g, "/");
    const pageKey = normalizePageKey(inferPageKeyFromFile(inDirAbs, f));
    assert(pageKey, `${fileRel}: cannot infer page key`);

    let doc;
    try {
      doc = await readDoc(f);
    } catch (e) {
      throw new Error(`${fileRel}: parse failed: ${String(e?.message ?? e)}`);
    }

    const paras = validateAndNormalizeDoc(doc, pageKey, fileRel);

    // 1 fichier = 1 page (canon)
    assert(!pages[pageKey], `${fileRel}: duplicate page "${pageKey}" (only one file per page)`);
    pages[pageKey] = { paras };
    paraCount += Object.keys(paras).length;
  }

  const out = {
    schema: 1,
    generatedAt: new Date().toISOString(),
    pages,
    stats: {
      pages: Object.keys(pages).length,
      paras: paraCount,
    },
  };

  await fs.mkdir(path.dirname(outAbs), { recursive: true });
  await fs.writeFile(outAbs, JSON.stringify(out), "utf8");

  console.log(`✅ annotations-index: pages=${out.stats.pages} paras=${out.stats.paras} -> ${path.relative(CWD, outAbs)}`);
}

main().catch((e) => {
  console.error("FAIL: build-annotations-index crashed:", e);
  process.exit(1);
});
