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

const CWD = process.cwd();
const ANNO_DIR = path.join(CWD, "src", "annotations");
const DIST_DIR = path.join(CWD, "dist");
const ALIASES_PATH = path.join(CWD, "src", "anchors", "anchor-aliases.json");

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 escRe(s) {
  return String(s).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}

function inferPageKeyFromFile(fileAbs) {
  const rel = path.relative(ANNO_DIR, fileAbs).replace(/\\/g, "/");
  return rel.replace(/\.(ya?ml|json)$/i, "");
}

function normalizePageKey(s) {
  return String(s || "").replace(/^\/+/, "").replace(/\/+$/, "");
}

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

async function loadAliases() {
  if (!(await exists(ALIASES_PATH))) return {};
  try {
    const raw = await fs.readFile(ALIASES_PATH, "utf8");
    const json = JSON.parse(raw);
    return isPlainObject(json) ? json : {};
  } catch {
    return {};
  }
}

function parseDoc(raw, fileAbs) {
  if (/\.json$/i.test(fileAbs)) return JSON.parse(raw);
  return YAML.parse(raw);
}

function getAlias(aliases, pageKey, oldId) {
  // supporte:
  // 1) { "<pageKey>": { "<old>": "<new>" } }
  // 2) { "<old>": "<new>" }
  const a1 = aliases?.[pageKey]?.[oldId];
  if (a1) return a1;
  const a2 = aliases?.[oldId];
  if (a2) return a2;
  return "";
}

async function main() {
  if (!(await exists(ANNO_DIR))) {
    console.log("✅ annotations: aucun dossier src/annotations — rien à vérifier.");
    process.exit(0);
  }

  if (!(await exists(DIST_DIR))) {
    console.error("FAIL: dist/ absent. Lance d’abord `npm run build` (ou `npm test`).");
    process.exit(1);
  }

  const aliases = await loadAliases();
  const files = (await walk(ANNO_DIR)).filter((p) => /\.(ya?ml|json)$/i.test(p));

  let pages = 0;
  let checked = 0;
  let failures = 0;
  const notes = [];

  for (const f of files) {
    const rel = path.relative(CWD, f).replace(/\\/g, "/");
    const raw = await fs.readFile(f, "utf8");

    let doc;
    try {
      doc = parseDoc(raw, f);
    } catch (e) {
      failures++;
      notes.push(`- PARSE FAIL: ${rel} (${String(e?.message ?? e)})`);
      continue;
    }

    if (!isPlainObject(doc) || doc.schema !== 1) {
      failures++;
      notes.push(`- INVALID: ${rel} (schema must be 1)`);
      continue;
    }

    const pageKey = normalizePageKey(inferPageKeyFromFile(f));

    if (doc.page != null && normalizePageKey(doc.page) !== pageKey) {
      failures++;
      notes.push(`- PAGE MISMATCH: ${rel} (page="${doc.page}" != path="${pageKey}")`);
      continue;
    }

    if (!isPlainObject(doc.paras)) {
      failures++;
      notes.push(`- INVALID: ${rel} (missing object key "paras")`);
      continue;
    }

    const distFile = path.join(DIST_DIR, pageKey, "index.html");
    if (!(await exists(distFile))) {
      failures++;
      notes.push(`- MISSING PAGE: dist/${pageKey}/index.html (from ${rel})`);
      continue;
    }

    pages++;
    const html = await fs.readFile(distFile, "utf8");

    for (const paraId of Object.keys(doc.paras)) {
      checked++;

      if (!/^p-\d+-/i.test(paraId)) {
        failures++;
        notes.push(`- INVALID ID: ${rel} (${paraId})`);
        continue;
      }

      const re = new RegExp(`\\bid=["']${escRe(paraId)}["']`, "g");
      if (re.test(html)) continue;

      const alias = getAlias(aliases, pageKey, paraId);
      if (alias) {
        const re2 = new RegExp(`\\bid=["']${escRe(alias)}["']`, "g");
        if (re2.test(html)) {
          notes.push(`- WARN alias used: ${pageKey} ${paraId} -> ${alias}`);
          continue;
        }
      }

      failures++;
      notes.push(`- MISSING ID: ${pageKey} (#${paraId})`);
    }
  }

  const warns = notes.filter((x) => x.startsWith("- WARN"));

  if (failures > 0) {
    console.error(`FAIL: annotations invalid (pages=${pages} checked=${checked} failures=${failures})`);
    for (const n of notes) console.error(n);
    process.exit(1);
  }

  for (const w of warns) console.log(w);
  console.log(`✅ annotations OK: pages=${pages} checked=${checked} warnings=${warns.length}`);
}

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