/**
 * AEGIS SOAR — absent-check / crash-loop pre-check (AS/PLATFORM/2026/002 §4, §5).
 *
 * Run BEFORE a fail-fast boot-DDL `ADD CONSTRAINT` (the §14 fail-fast pattern: a
 * broken control crashes boot rather than serving false-green). Confirms:
 *   (1) the target constraint is FULLY absent on the target table — no partial/orphan
 *       state that would make `ADD CONSTRAINT` throw and crash-loop the deployment;
 *   (2) the FK target table exists — so the constraint the boot DDL adds is creatable;
 *   (3) the child columns the constraint will bind exist (no missing-column throw).
 *
 * Usage:
 *   npx tsx scripts/absent-crashloop-precheck.ts \
 *     --table <child> --constraint <conname> --fk-target <parent> [--column <c> ...]
 *
 * Target DB resolution: TARGET_DATABASE_URL ?? DATABASE_URL (string never printed, Rule 1).
 * HOLD: dev mechanism check; the real pre-boot prod assertion is deferred to the M3
 * schema gate (Rule 19 — dev-verified, not prod-re-run).
 *
 * Exit codes: 0 = SAFE to add (absent + target + columns present); 1 = NOT safe; 2 = usage.
 */
import { openTargetPool, currentDatabase, tableExists, constraintExists } from "./lib/schema-probe";
import pg from "pg";

function single(argv: string[], flag: string): string | undefined {
  const i = argv.indexOf(flag);
  return i >= 0 ? argv[i + 1] : undefined;
}
function multi(argv: string[], flag: string): string[] {
  const out: string[] = [];
  for (let i = 0; i < argv.length; i++) if (argv[i] === flag && argv[i + 1]) out.push(argv[i + 1]);
  return out;
}

async function columnExists(pool: pg.Pool, table: string, column: string): Promise<boolean> {
  const r = await pool.query(
    `SELECT 1 FROM pg_attribute
      WHERE attrelid = to_regclass($1) AND attname = $2 AND attnum > 0 AND NOT attisdropped`,
    [table, column],
  );
  return (r.rowCount ?? 0) > 0;
}

async function main(): Promise<void> {
  const argv = process.argv.slice(2);
  const table = single(argv, "--table");
  const constraint = single(argv, "--constraint");
  const fkTarget = single(argv, "--fk-target");
  const columns = multi(argv, "--column");

  if (!table || !constraint) {
    console.error("usage: absent-crashloop-precheck.ts --table <child> --constraint <conname> [--fk-target <parent>] [--column <c> ...]");
    process.exit(2);
  }

  const pool = openTargetPool();
  const blockers: string[] = [];
  try {
    const db = await currentDatabase(pool);
    console.log("=".repeat(82));
    console.log(`absent-crashloop-precheck  db=${db}`);
    console.log(`  target: ADD CONSTRAINT ${constraint} ON ${table}${fkTarget ? ` → ${fkTarget}` : ""}`);
    console.log("-".repeat(82));

    // child table must exist for the ALTER to target
    const childOk = await tableExists(pool, table);
    console.log(`  ${childOk ? "OK  " : "FAIL"} child table ${table} exists = ${childOk}`);
    if (!childOk) blockers.push(`child table ${table} missing`);

    // (1) constraint must be FULLY absent (no partial/orphan)
    if (childOk) {
      const conPresent = await constraintExists(pool, table, constraint);
      console.log(`  ${!conPresent ? "OK  " : "FAIL"} constraint ${constraint} absent = ${!conPresent}${conPresent ? " (PRESENT — ADD would collide / crash-loop)" : ""}`);
      if (conPresent) blockers.push(`constraint ${constraint} already present`);
    }

    // (2) FK target must exist
    if (fkTarget) {
      const tgtOk = await tableExists(pool, fkTarget);
      console.log(`  ${tgtOk ? "OK  " : "FAIL"} FK target ${fkTarget} exists = ${tgtOk}`);
      if (!tgtOk) blockers.push(`FK target ${fkTarget} missing`);
    }

    // (3) child columns the constraint binds must exist
    if (childOk) {
      for (const c of columns) {
        const colOk = await columnExists(pool, table, c);
        console.log(`  ${colOk ? "OK  " : "FAIL"} child column ${table}.${c} exists = ${colOk}`);
        if (!colOk) blockers.push(`child column ${c} missing`);
      }
    }
  } finally {
    await pool.end();
  }

  console.log("=".repeat(82));
  if (blockers.length === 0) {
    console.log("CRASH-LOOP PRE-CHECK: SAFE — constraint absent, target + columns present; boot-DDL ADD will not crash-loop.");
    process.exit(0);
  }
  console.log(`CRASH-LOOP PRE-CHECK: NOT SAFE — ${blockers.length} blocker(s):`);
  for (const b of blockers) console.log(`  - ${b}`);
  process.exit(1);
}

main().catch((e) => { console.error("crashloop-precheck crashed:", e?.message ?? e); process.exit(2); });
