// scripts/verify-erasure-drill.ts
//
// FU-011 erasure-drill verification (Decision F3).
//
// Standalone tsx-runnable script that demonstrates the four verification
// requirements operator pre-committed to before unit closure:
//
//   1. Test PII record created with known customerRef and known content;
//      creation receipt shows row exists.
//   2. Drill invoked with that customerRef; receipt shows delete count equal
//      to rows created.
//   3. Subsequent SELECT against same customerRef returns zero rows.
//   4. Second drill invocation with same customerRef returns rowsErased: 0
//      (and priorErasureFound: 1 — Decision C refinement working).
//
// Refusal-criteria mapping:
//   #1 (no fake-row seeding): the test PII row is created in a separate
//      explicit step BEFORE the drill is invoked. The drill itself never
//      seeds.
//   #5 (real DB in verification): every query and assertion below executes
//      against the real database via the project's `db` client.
//   #7 (no synthetic fallback): every assertion failure exits non-zero with
//      a specific diagnostic. There is no try/catch around runErasureDrill
//      that could swallow failures.
//
// Pre-flight (Decision D refinement): the script SELECTs for prior
// `ERASE-TEST-%` rows in consent_events at the start of every run and
// REFUSES TO PROCEED if any remain. This protects against orphan test rows
// from a previous run being erased by this run, which would produce
// misleading counts.
//
// Output: writes a captured-evidence file at
// docs/regulatory/erasure-drill-verification-{YYYYMMDDTHHMMSS}.txt with
// preflight result, testRef, both receipts in full, and PASS/FAIL for each
// of the six assertions.
//
// Usage: tsx scripts/verify-erasure-drill.ts
//
// Exit code: 0 only if all six assertions pass; non-zero otherwise.

import { randomBytes, createHash } from "crypto";
import { writeFileSync, mkdirSync } from "fs";
import { eq, like } from "drizzle-orm";
import { db } from "../server/db";
import { consentEvents } from "@shared/schema";
import { runErasureDrill } from "../server/lib/wave13-pilot-readiness";

// Captured-output buffer — appended to throughout the run, written at exit.
const lines: string[] = [];
function log(s: string) { console.log(s); lines.push(s); }
function fail(s: string): never {
  console.error(s);
  lines.push(s);
  flushEvidence("FAIL");
  process.exit(1);
}

const startedAt = new Date();
const tsLabel = startedAt.toISOString().replace(/[-:]/g, "").replace(/\..+/, "");
const evidencePath = `docs/regulatory/erasure-drill-verification-${tsLabel}.txt`;

function flushEvidence(outcome: "PASS" | "FAIL") {
  try { mkdirSync("docs/regulatory", { recursive: true }); } catch {}
  const header = [
    "=".repeat(72),
    `FU-011 erasure-drill verification — outcome: ${outcome}`,
    `Started:  ${startedAt.toISOString()}`,
    `Finished: ${new Date().toISOString()}`,
    `Script:   scripts/verify-erasure-drill.ts`,
    "=".repeat(72),
    "",
  ].join("\n");
  writeFileSync(evidencePath, header + lines.join("\n") + "\n");
}

async function main() {
  log("[VERIFY] FU-011 erasure-drill verification starting.");
  log(`[VERIFY] Evidence will be written to: ${evidencePath}`);
  log("");

  // ---------- Step 1: Pre-flight (Decision D refinement) ----------
  log("[STEP 1] Pre-flight: checking for orphan ERASE-TEST-% rows in consent_events...");
  const orphans = await db
    .select({ id: consentEvents.id, customerRef: consentEvents.customerRef })
    .from(consentEvents)
    .where(like(consentEvents.customerRef, "ERASE-TEST-%"));
  if (orphans.length > 0) {
    log(`[STEP 1] FAIL — found ${orphans.length} orphan ERASE-TEST-% row(s) from a previous run:`);
    for (const o of orphans) log(`         id=${o.id} customer_ref=${o.customerRef}`);
    fail("[VERIFY] REFUSING TO PROCEED — orphan rows would contaminate counts. Clean them manually first.");
  }
  log("[STEP 1] PASS — no orphan ERASE-TEST-% rows present.");
  log("");

  // ---------- Step 2: Create the test PII record ----------
  const testRef = `ERASE-TEST-${Date.now()}-${randomBytes(4).toString("hex")}`;
  log(`[STEP 2] Creating test PII row: customer_ref=${testRef}`);
  const eventRef = `EVT-${randomBytes(8).toString("hex")}`;
  const evidencePayload = `${testRef}|${eventRef}|fu011-erasure-verification|grant`;
  const evidenceHash = createHash("sha256").update(evidencePayload).digest("hex");
  const [created] = await db.insert(consentEvents).values({
    eventRef,
    customerRef: testRef,
    purpose: "fu011-erasure-verification",
    scope: "verification-only",
    action: "grant",
    channel: "script",
    evidenceHash,
    capturedBy: "verify-erasure-drill",
  }).returning();
  log(`[STEP 2] Inserted row id=${created.id} customer_ref=${created.customerRef}`);
  log("");

  // ---------- Step 3: Pre-assert the row exists ----------
  log("[STEP 3] Pre-assert: SELECT confirms the test row exists...");
  const preCheck = await db
    .select({ id: consentEvents.id })
    .from(consentEvents)
    .where(eq(consentEvents.customerRef, testRef));
  if (preCheck.length !== 1) {
    fail(`[STEP 3] FAIL — expected 1 row, got ${preCheck.length}.`);
  }
  log(`[STEP 3] PASS — 1 row present (id=${preCheck[0].id}).`);
  log("");

  // ---------- Step 4: Drill #1 ----------
  log("[STEP 4] Invoking runErasureDrill #1 (expect realRowsErased=1, priorErasureFound=0)...");
  const drill1 = await runErasureDrill({ customerRef: testRef, triggeredBy: "verify-erasure-drill" });
  log(`[STEP 4] Drill #1 receipt:`);
  log(JSON.stringify(drill1, null, 2));
  log("");

  // ---------- Step 5: Assertion #1 ----------
  log("[STEP 5] Assertion #1: drill #1 erased exactly the row we created.");
  const a1_realRowsErased = (drill1 as any).realRowsErased === 1;
  const a1_passed         = (drill1 as any).passed === 1;
  const a1_priorErasure   = (drill1 as any).priorErasureFound === 0;
  log(`         realRowsErased === 1   : ${a1_realRowsErased ? "PASS" : "FAIL"}`);
  log(`         passed === 1           : ${a1_passed ? "PASS" : "FAIL"}`);
  log(`         priorErasureFound === 0: ${a1_priorErasure ? "PASS" : "FAIL"}`);
  if (!a1_realRowsErased || !a1_passed || !a1_priorErasure) {
    fail("[STEP 5] FAIL — assertion #1 did not hold.");
  }
  log("[STEP 5] PASS.");
  log("");

  // ---------- Step 6: Post-assert no residual ----------
  log("[STEP 6] Post-assert: SELECT against the test customer_ref returns 0 rows...");
  const postCheck = await db
    .select({ id: consentEvents.id })
    .from(consentEvents)
    .where(eq(consentEvents.customerRef, testRef));
  if (postCheck.length !== 0) {
    fail(`[STEP 6] FAIL — expected 0 residual rows, got ${postCheck.length}.`);
  }
  log("[STEP 6] PASS — 0 residual rows.");
  log("");

  // ---------- Step 7: Drill #2 ----------
  log("[STEP 7] Invoking runErasureDrill #2 against same customerRef (expect realRowsErased=0, priorErasureFound=1)...");
  const drill2 = await runErasureDrill({ customerRef: testRef, triggeredBy: "verify-erasure-drill" });
  log(`[STEP 7] Drill #2 receipt:`);
  log(JSON.stringify(drill2, null, 2));
  log("");

  // ---------- Step 8: Assertion #2 ----------
  log("[STEP 8] Assertion #2: drill #2 finds nothing to erase but knows a prior receipt exists.");
  const a2_realRowsErased = (drill2 as any).realRowsErased === 0;
  const a2_passed         = (drill2 as any).passed === 1;
  const a2_priorErasure   = (drill2 as any).priorErasureFound === 1;
  log(`         realRowsErased === 0   : ${a2_realRowsErased ? "PASS" : "FAIL"}`);
  log(`         passed === 1           : ${a2_passed ? "PASS" : "FAIL"}`);
  log(`         priorErasureFound === 1: ${a2_priorErasure ? "PASS" : "FAIL"}`);
  if (!a2_realRowsErased || !a2_passed || !a2_priorErasure) {
    fail("[STEP 8] FAIL — assertion #2 did not hold.");
  }
  log("[STEP 8] PASS.");
  log("");

  // ---------- Success summary (Adjustment 3) ----------
  log("=".repeat(72));
  log("[VERIFY] Erasure drill verification PASSED.");
  log(`  testRef: ${testRef}`);
  log(`  Drill 1: realRowsErased=${(drill1 as any).realRowsErased}, passed=${(drill1 as any).passed}, priorErasureFound=${(drill1 as any).priorErasureFound}  PASS`);
  log(`  Drill 2: realRowsErased=${(drill2 as any).realRowsErased}, passed=${(drill2 as any).passed}, priorErasureFound=${(drill2 as any).priorErasureFound}  PASS`);
  log(`  Post-erasure SELECT count: 0  PASS`);
  log(`  Evidence file: ${evidencePath}`);
  log("=".repeat(72));

  flushEvidence("PASS");
  process.exit(0);
}

main().catch((e) => {
  log(`[VERIFY] UNCAUGHT ERROR: ${e?.message || String(e)}`);
  log(e?.stack || "");
  fail("[VERIFY] Verification aborted due to uncaught error.");
});
