#!/usr/bin/env node

import { readFile, writeFile } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";

const ROOT_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const CONTRACTS_PATH = path.join(ROOT_DIR, "packages", "contracts", "src", "index.ts");
const CANONICAL_JSON_PATH = path.join(ROOT_DIR, "docs", "oss", "failure-taxonomy.runtime.json");
const CANONICAL_MARKDOWN_PATH = path.join(ROOT_DIR, "docs", "oss", "FAILURE-TAXONOMY-12.md");

const CLASS_DESCRIPTIONS = {
  logic_error: "Core reasoning or implementation logic did not satisfy objective constraints.",
  hallucination: "Agent asserted or used unsupported repo/runtime facts.",
  syntax_error: "Generated code failed syntax validation before runtime correctness checks.",
  type_error: "Generated code failed static type checks.",
  test_regression: "Changes caused tests that previously passed to fail.",
  scope_creep: "Attempt modified behavior or files outside the declared objective boundary.",
  no_progress: "Retries continued without meaningful objective progress.",
  repo_grounding_failure: "Edits were not grounded to actual repository symbols/paths/state.",
  verification_failure: "Verifier gate failed; run did not satisfy completion criteria.",
  environment_mismatch: "Execution failed due to host/runtime/tooling mismatch.",
  budget_pressure: "Run hit or approached budget/iteration/token constraints.",
  safety_leash_blocked: "Policy/safety enforcement blocked unsafe or disallowed actions."
};

function parseFailureClasses(source) {
  const match = source.match(/export const FAILURE_CLASSES = \[([\s\S]*?)\] as const;/);
  if (!match) {
    throw new Error("Unable to find FAILURE_CLASSES in contracts source.");
  }
  const body = match[1];
  const classes = [...body.matchAll(/"([a-z_]+)"/g)].map((entry) => entry[1]);
  if (classes.length === 0) {
    throw new Error("FAILURE_CLASSES was found but no class labels were parsed.");
  }
  if (new Set(classes).size !== classes.length) {
    throw new Error("FAILURE_CLASSES contains duplicate labels.");
  }
  return classes;
}

function toCanonicalJson(classes) {
  return `${JSON.stringify(
    {
      schemaVersion: "martin.failure-taxonomy.runtime.v1",
      source: "packages/contracts/src/index.ts#FAILURE_CLASSES",
      canonicalClassCount: classes.length,
      failureClasses: classes
    },
    null,
    2
  )}\n`;
}

function toCanonicalMarkdown(classes) {
  const rows = classes
    .map((classId) => `| \`${classId}\` | ${CLASS_DESCRIPTIONS[classId] ?? "Runtime failure classification."} |`)
    .join("\n");
  return [
    "# Failure Taxonomy (12 Runtime Classes)",
    "",
    "<!-- Generated by scripts/failure-taxonomy.mjs. Do not hand-edit class rows. -->",
    "",
    "This is the canonical MartinLoop taxonomy. The class list is generated from `FAILURE_CLASSES` in `@martin/contracts`.",
    "",
    "| Runtime class | Meaning |",
    "| --- | --- |",
    rows,
    ""
  ].join("\n");
}

async function ensureMatchesOrWrite(targetPath, expectedContents, write) {
  if (write) {
    await writeFile(targetPath, expectedContents, "utf8");
    return;
  }
  const existing = await readFile(targetPath, "utf8").catch(() => "");
  const normalize = (value) => value.replace(/\r\n/g, "\n");
  if (normalize(existing) !== normalize(expectedContents)) {
    throw new Error(`Taxonomy artifact drift detected: ${path.relative(ROOT_DIR, targetPath)}`);
  }
}

export async function loadFailureClassesFromContracts() {
  const source = await readFile(CONTRACTS_PATH, "utf8");
  return parseFailureClasses(source);
}

export async function syncFailureTaxonomyArtifacts({ write = false } = {}) {
  const classes = await loadFailureClassesFromContracts();
  await ensureMatchesOrWrite(CANONICAL_JSON_PATH, toCanonicalJson(classes), write);
  await ensureMatchesOrWrite(CANONICAL_MARKDOWN_PATH, toCanonicalMarkdown(classes), write);
  return classes;
}

async function main() {
  const write = process.argv.includes("--write");
  if (write) {
    const classes = await syncFailureTaxonomyArtifacts({ write: true });
    process.stdout.write(`failure-taxonomy synced (${classes.length} classes)\n`);
    return;
  }
  const classes = await syncFailureTaxonomyArtifacts({ write: false });
  process.stdout.write(`failure-taxonomy parity OK (${classes.length} classes)\n`);
}

if (process.argv[1] && fileURLToPath(import.meta.url) === path.resolve(process.argv[1])) {
  main().catch((error) => {
    process.stderr.write(`failure-taxonomy check failed: ${error.message}\n`);
    process.exitCode = 1;
  });
}
