/**
 * AEGIS SOAR — prod-structural-read-back vs dev golden (AS/PLATFORM/2026/002 §4, §5.6).
 *
 * For declarative-constraint gates (RLS / CHECK / composite-FK / index migrations).
 * Captures a dev GOLDEN of a table's structure — including ORDERED conkey/confkey for
 * composite FKs (catches a transposed (tenant_id, id) FK that a naive 2-col check
 * passes), column nullability/defaults, RLS USING + WITH CHECK, CHECKs, and indexes —
 * then diffs a target read-back byte-for-byte. Behavioral firing is named
 * enforced-by-construction when the prod SQL surface is read-only + route-less.
 *
 * Usage:
 *   capture: npx tsx scripts/prod-structural-readback.ts capture --table <t> --golden <file>
 *   verify:  npx tsx scripts/prod-structural-readback.ts verify  --table <t> --golden <file>
 *
 * Target DB resolution: TARGET_DATABASE_URL ?? DATABASE_URL (string never printed, Rule 1).
 * HOLD: dev-vs-dev verify proves the diff MECHANISM (identical → 0 diffs); a real
 * prod read-back is deferred to the M3 schema gate (Rule 19 — dev-verified, not prod-re-run).
 *
 * Exit codes: 0 = captured / identical; 1 = diffs found / table missing; 2 = usage.
 */
import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
import path from "node:path";
import { openTargetPool, currentDatabase, snapshotTable, canonical, diffStructures, type TableStructure } from "./lib/schema-probe";

function single(argv: string[], flag: string): string | undefined {
  const i = argv.indexOf(flag);
  return i >= 0 ? argv[i + 1] : undefined;
}

async function main(): Promise<void> {
  const argv = process.argv.slice(2);
  const mode = argv[0];
  const table = single(argv, "--table");
  const goldenPath = single(argv, "--golden");

  if ((mode !== "capture" && mode !== "verify") || !table || !goldenPath) {
    console.error("usage: prod-structural-readback.ts <capture|verify> --table <t> --golden <file>");
    process.exit(2);
  }

  const pool = openTargetPool();
  try {
    const db = await currentDatabase(pool);
    const snap = await snapshotTable(pool, table);
    console.log("=".repeat(82));
    console.log(`prod-structural-read-back [${mode}]  db=${db}  table=${table}`);
    console.log("-".repeat(82));

    if (!snap) {
      console.log(`FAIL — table ${table} not found in ${db}`);
      process.exit(1);
    }

    if (mode === "capture") {
      mkdirSync(path.dirname(path.resolve(goldenPath)), { recursive: true });
      writeFileSync(path.resolve(goldenPath), canonical(snap) + "\n", "utf8");
      console.log(`captured golden → ${goldenPath}`);
      console.log(`  columns=${snap.columns.length} constraints=${snap.constraints.length} policies=${snap.policies.length} indexes=${snap.indexes.length}`);
      const fks = snap.constraints.filter((c) => c.type === "f");
      for (const fk of fks) console.log(`  FK ${fk.name}: conkey=${JSON.stringify(fk.conkey)} confkey=${JSON.stringify(fk.confkey)} → ${fk.refTable}  [${fk.definition}]`);
      process.exit(0);
    }

    // verify
    const golden = JSON.parse(readFileSync(path.resolve(goldenPath), "utf8")) as TableStructure;
    const diffs = diffStructures(golden, snap);
    if (diffs.length === 0) {
      console.log("STRUCTURAL-READ-BACK: PASS — read-back is byte-for-byte identical to the golden.");
      process.exit(0);
    }
    console.log(`STRUCTURAL-READ-BACK: FAIL — ${diffs.length} structural difference(s):`);
    for (const d of diffs) console.log(`  - ${d}`);
    process.exit(1);
  } finally {
    await pool.end();
  }
}

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