// ============================================================================
// VF — Fault-injection deployment-safety gate verification
// ----------------------------------------------------------------------------
// Proves BOTH directions of assertFaultInjectionDeploymentSafety()
// (server/lib/fault-injection.ts), the Rule 14-style boot gate added during
// the Platform Verification arc (ref AS/AEGIS-CYBER/VERIFY/2026/001):
//
//   TRIP   — FAULT_INJECTION_ENABLED="true" AND
//            ( REPLIT_DEPLOYMENT="1" OR NODE_ENV="production" )
//            ⇒ process.exit(1), boot refuses to serve.
//   NO-TRIP — every other combination, in particular:
//            • deployment with the flag absent (clean prod boot)
//            • the dev/test verification path (armed, NODE_ENV=development,
//              NOT a deployment) — this MUST still work or the swallow-fix
//              demo breaks.
//
// The NODE_ENV="production" arm (Rule 7) covers the bank-laptop Docker
// deployment, which runs NODE_ENV=production but carries no REPLIT_DEPLOYMENT.
//
// Each case runs in a child process (the gate calls process.exit, so it cannot
// be exercised in-process) with NODE_ENV controlled explicitly per case.
// Self-contained: the runner re-invokes itself with `--child`, which calls the
// gate and prints OK-CONTINUED iff the gate returned.
// ============================================================================

import { spawnSync } from "node:child_process";
import { fileURLToPath } from "node:url";
import { assertFaultInjectionDeploymentSafety } from "../server/lib/fault-injection";

const SELF = fileURLToPath(import.meta.url);

if (process.argv.includes("--child")) {
  assertFaultInjectionDeploymentSafety();
  // Only reached when the gate did NOT exit.
  console.log("OK-CONTINUED");
  process.exit(0);
}

interface GateCase {
  name: string;
  set: Record<string, string>;
  unset: string[];
  expectExit: number;
  expectContinue: boolean;
}

const cases: GateCase[] = [
  {
    name: "A — TRIPS: Replit deployment + armed (the dangerous combination)",
    set: { REPLIT_DEPLOYMENT: "1", FAULT_INJECTION_ENABLED: "true", FAULT_INJECTION_TARGET: "regulator.saveSAR" },
    unset: ["NODE_ENV"],
    expectExit: 1,
    expectContinue: false,
  },
  {
    name: "B — no-trip: Replit deployment + flag ABSENT (clean prod boot)",
    set: { REPLIT_DEPLOYMENT: "1" },
    unset: ["FAULT_INJECTION_ENABLED", "FAULT_INJECTION_TARGET", "NODE_ENV"],
    expectExit: 0,
    expectContinue: true,
  },
  {
    name: "C — no-trip: dev/test verification path (armed, NODE_ENV=development, NOT a deployment)",
    set: { NODE_ENV: "development", FAULT_INJECTION_ENABLED: "true", FAULT_INJECTION_TARGET: "regulator.saveSAR" },
    unset: ["REPLIT_DEPLOYMENT"],
    expectExit: 0,
    expectContinue: true,
  },
  {
    name: "D — no-trip: nothing set (no deployment, no flag, NODE_ENV unset)",
    set: {},
    unset: ["REPLIT_DEPLOYMENT", "FAULT_INJECTION_ENABLED", "FAULT_INJECTION_TARGET", "NODE_ENV"],
    expectExit: 0,
    expectContinue: true,
  },
  {
    name: "E — no-trip: Replit deployment + flag set to wrong value (not exactly 'true')",
    set: { REPLIT_DEPLOYMENT: "1", FAULT_INJECTION_ENABLED: "1" },
    unset: ["FAULT_INJECTION_TARGET", "NODE_ENV"],
    expectExit: 0,
    expectContinue: true,
  },
  {
    name: "F — TRIPS (Docker gap, Rule 7): NODE_ENV=production + armed, NO REPLIT_DEPLOYMENT",
    set: { NODE_ENV: "production", FAULT_INJECTION_ENABLED: "true", FAULT_INJECTION_TARGET: "regulator.saveSAR" },
    unset: ["REPLIT_DEPLOYMENT"],
    expectExit: 1,
    expectContinue: false,
  },
  {
    name: "G — no-trip: NODE_ENV=production + flag ABSENT (clean Docker prod boot)",
    set: { NODE_ENV: "production" },
    unset: ["REPLIT_DEPLOYMENT", "FAULT_INJECTION_ENABLED", "FAULT_INJECTION_TARGET"],
    expectExit: 0,
    expectContinue: true,
  },
];

let pass = 0;
let fail = 0;

for (const c of cases) {
  const env: NodeJS.ProcessEnv = { ...process.env };
  for (const k of c.unset) delete env[k];
  for (const [k, v] of Object.entries(c.set)) env[k] = v;

  const r = spawnSync("npx", ["tsx", SELF, "--child"], { env, encoding: "utf8" });
  const out = `${r.stdout || ""}`;
  const continued = out.includes("OK-CONTINUED");
  const exitOk = r.status === c.expectExit;
  const continueOk = continued === c.expectContinue;
  const ok = exitOk && continueOk;
  if (ok) pass++;
  else fail++;
  console.log(
    `${ok ? "PASS" : "FAIL"}  ${c.name}\n` +
      `        exit=${r.status} (expect ${c.expectExit}); continued=${continued} (expect ${c.expectContinue})`,
  );
}

console.log(`\n${fail === 0 ? "ALL PASS" : "FAILURES PRESENT"} — ${pass}/${cases.length} cases passed.`);
process.exit(fail === 0 ? 0 : 1);
