// scripts/verify-ir-drill.ts
// FU-011 silent-pass conv #2 (2026-05-06) verification harness.
// Refusal-criterion #5: real DB at every step, no mocks.
// Refusal-criterion #3 evidence: force-fail injection asserts perPhaseErrors
// captures the failure point and passed=0 is recorded (not silently swallowed).
//
// Invocation:  npx tsx scripts/verify-ir-drill.ts
import { db } from "../server/db";
import { irDrillRuns, incidentSLAs } from "@shared/schema";
import { runIrDrill } from "../server/lib/wave13-pilot-readiness";
import { eq } from "drizzle-orm";

const TRIGGERED_BY = "verify-ir-drill-script";
const out = (label: string, ok: boolean, detail = "") => {
  console.log(`${ok ? "PASS" : "FAIL"}  ${label}${detail ? "  — " + detail : ""}`);
  if (!ok) process.exitCode = 1;
};

async function main() {
  console.log("─── ir-drill happy-path ───");
  const drill: any = await runIrDrill({ scenario: "ransomware", triggeredBy: TRIGGERED_BY });

  // 1. Drill returns incidentRef !== null
  out("1. drill.incidentRef populated", drill.incidentRef !== null,
      `incidentRef=${drill.incidentRef}`);

  // 2. SELECT incident_slas WHERE id=incidentRef returns 1 row, status='resolved'
  const [inc] = await db.select().from(incidentSLAs)
    .where(eq(incidentSLAs.id, drill.incidentRef!));
  out("2. underlying incident exists with status='resolved'",
      !!inc && inc.status === "resolved",
      inc ? `id=${inc.id} status=${inc.status} title=${inc.title}` : "row missing");

  // 3a. All phase ms > 0 (real DB writes always take measurable time)
  const allPhasesPositive = drill.detectMs > 0 && drill.containMs > 0 &&
                            drill.eradicateMs > 0 && drill.recoverMs > 0 && drill.pirMs > 0;
  out("3a. phase ms all > 0 (real DB writes)", allPhasesPositive,
      `detect=${drill.detectMs} contain=${drill.containMs} eradicate=${drill.eradicateMs} recover=${drill.recoverMs} pir=${drill.pirMs}`);

  // 3b. totalMs aligned with DB-clock delta from createdAt → resolvedAt
  const dbDelta = inc?.resolvedAt && inc?.createdAt
    ? new Date(inc.resolvedAt).getTime() - new Date(inc.createdAt).getTime()
    : -1;
  const totalConsistent = Math.abs(drill.totalMs - dbDelta) < 5000; // 5s tolerance
  out("3b. totalMs aligned with DB-clock delta (within 5s)", totalConsistent,
      `drill.totalMs=${drill.totalMs} dbDelta=${dbDelta}`);

  // 4. passed=1 AND perPhaseErrors=[]
  const perPhaseErrors = drill.perPhaseErrors ?? [];
  out("4. happy path passed=1, perPhaseErrors empty",
      drill.passed === 1 && perPhaseErrors.length === 0,
      `passed=${drill.passed} errors=${JSON.stringify(perPhaseErrors)}`);

  // 5. PIR honesty disclosure fields (operator spec 2026-05-06)
  const dj = drill.detailsJson ?? {};
  const pirHonest = dj.pirMode === "automated-drill-template" &&
                    dj.humanPirDeferred === true &&
                    typeof dj.pirContent === "string" && dj.pirContent.length > 0;
  out("5. PIR honesty disclosure (pirMode + humanPirDeferred + pirContent)",
      pirHonest,
      `pirMode=${dj.pirMode} humanPirDeferred=${dj.humanPirDeferred} pirContent.length=${dj.pirContent?.length ?? 0}`);

  // 5b. phaseMsConvention disclosure present
  out("5b. phaseMsConvention documented in detailsJson",
      typeof dj.phaseMsConvention === "string" && dj.phaseMsConvention.length > 0,
      `convention.length=${dj.phaseMsConvention?.length ?? 0}`);

  // 6. Force-fail injection: monkey-patch acknowledgeIncident to throw, run
  //    drill, assert passed=0 and perPhaseErrors records contain phase failure.
  console.log("\n─── ir-drill force-fail (refusal #3 evidence) ───");
  const sla = await import("../server/lib/incident-sla");
  const orig = sla.incidentSlaTracker.acknowledgeIncident;
  (sla.incidentSlaTracker as any).acknowledgeIncident = async () => {
    throw new Error("INJECTED: simulated acknowledgeIncident failure");
  };
  try {
    const failDrill: any = await runIrDrill({ scenario: "data_exfil", triggeredBy: TRIGGERED_BY });
    const failErrors = failDrill.perPhaseErrors ?? [];
    out("6. force-fail produced passed=0 + perPhaseErrors records contain",
        failDrill.passed === 0 && failErrors.some((e: any) => e.phase === "contain"),
        `passed=${failDrill.passed} errors=${JSON.stringify(failErrors)}`);

    // 6b. Force-fail receipt still has incidentRef (detect succeeded before contain failed)
    out("6b. force-fail receipt has incidentRef from successful detect phase",
        failDrill.incidentRef !== null,
        `incidentRef=${failDrill.incidentRef}`);

    // 6c. Force-fail receipt has phaseMs=0 for skipped phases (eradicate/recover/pir)
    const skippedZero = failDrill.eradicateMs === 0 && failDrill.recoverMs === 0 && failDrill.pirMs === 0;
    out("6c. skipped phases recorded as phaseMs=0 (per convention)",
        skippedZero,
        `eradicate=${failDrill.eradicateMs} recover=${failDrill.recoverMs} pir=${failDrill.pirMs}`);
  } finally {
    (sla.incidentSlaTracker as any).acknowledgeIncident = orig;
  }

  console.log("\n─── done ───");
  process.exit(process.exitCode ?? 0);
}

main().catch((e) => {
  console.error("FATAL", e);
  process.exit(1);
});
