/**
 * AEGIS SOAR (AS/SOAR/2026/001) — M1 Gate 2 — INGESTION-ROUTE BEHAVIORAL BATTERY.
 * Spec/plan: build POST /api/fincrime/ingest (a signal → one OPEN fincrime case)
 * to DEV-VERIFIED, then HOLD at the dev gate.
 *
 * Drives the REAL running dev server (:5000) exactly as an authenticated client
 * would — login → csrf → POST — then reads the durable effect back out-of-band
 * two ways:
 *   • owner pool (DATABASE_URL = BYPASSRLS) for GROUND TRUTH (does the row exist
 *     at all, in ANY tenant — the only honest way to prove a cross-tenant row was
 *     NOT created), and
 *   • withTenantRls(tenant) (the app role with the tenant GUC set) for ROLE-
 *     SWITCHED RLS-isolation reads (tenant A sees its case; tenant B does not).
 *
 * Rule 18 (paired controls): every denial is paired, in the SAME run, with a
 * positive enforcement, so no green is HOLLOW-GREEN.
 * Rule 19 (verified-vs-assumed): each verdict states HOW it was checked
 * (HTTP status + which read-back connection proved the durable effect).
 *
 * Scope fence: this exercises ONLY the OPEN-creation seam. It asserts the created
 * case is OPEN with ALL disposition fields NULL (no investigation / disposition /
 * STR — those are M2/M3).
 *
 * Side-effect discipline: imports only the inbound DTO + canonical hash helper
 * (to compute the exact server-side source_signal_hash), withTenantRls, pg and
 * bcryptjs. Does NOT import server/index.ts or server/routes.ts (no second server
 * boot). All synthetic seed users/cases/events are cleaned up in `finally`; the
 * immutable hash-chained audit_chain_entries rows are intentionally preserved.
 */

import pg from "pg";
import bcrypt from "bcryptjs";
import { randomUUID } from "node:crypto";
import { sql } from "drizzle-orm";
import {
  ingestSignalSchema,
  computeSourceSignalHash,
} from "../server/lib/fincrime/ingestion";
import { withTenantRls } from "../server/db";

const owner = new pg.Pool({ connectionString: process.env.DATABASE_URL });

const SERVER = "http://127.0.0.1:5000";
const TENANT_A = "aegis-sovereign"; // real seeded dev tenant (the authed actor's tenant)
const TENANT_B = "stanbic-ug-001"; // a DIFFERENT real seeded dev tenant
const base = Date.now();
// Per-actor source IPs (RFC 5737 TEST-NET-3). The login limiter is keyed by IP
// (5/15min) and counts persist in the live server across harness runs, so each
// actor gets its OWN IP (1 login each) and the octet rotates per run (base-
// derived, idx*7-spaced so the four are always distinct) to avoid accumulation.
const ipOct = (idx: number) => ((Math.floor(base / 1000) + idx * 7) % 240) + 1;
const RM_XFF = `203.0.113.${ipOct(0)}`;
const ADMIN_XFF = `203.0.113.${ipOct(1)}`;
const AUD_XFF = `203.0.113.${ipOct(2)}`;
const ANON_XFF = `203.0.113.${ipOct(3)}`;

const settle = (ms = 600) => new Promise((r) => setTimeout(r, ms));

interface LegResult {
  id: string;
  scenario: string;
  expectation: string;
  observed: string;
  verdict: string;
  pass: boolean;
}
const results: LegResult[] = [];
const rec = (r: LegResult) => results.push(r);
const createdCaseIds = new Set<string>();

// ── owner (BYPASSRLS) ground-truth read-back helpers ─────────────────────────
interface CaseRow {
  id: string;
  tenant_id: string;
  source_type: string;
  source_ref: string | null;
  source_signal_hash: string;
  subject_ref: string;
  subject_ref_kind: string;
  state: string;
  disposition: string | null;
  disposition_reason: string | null;
  dispositioned_at: Date | null;
  str_filed: boolean;
  audit_chain_entry_id: number | null;
}
async function casesByHash(tenantId: string, hash: string): Promise<CaseRow[]> {
  const r = await owner.query<CaseRow>(
    "SELECT * FROM fincrime_cases WHERE tenant_id = $1 AND source_signal_hash = $2",
    [tenantId, hash],
  );
  return r.rows;
}
async function caseCountBySubjectAnyTenant(subjectRef: string): Promise<number> {
  const r = await owner.query<{ n: string }>(
    "SELECT count(*)::int AS n FROM fincrime_cases WHERE subject_ref = $1",
    [subjectRef],
  );
  return Number(r.rows[0].n);
}
async function caseCountBySubjectInTenant(tenantId: string, subjectRef: string): Promise<number> {
  const r = await owner.query<{ n: string }>(
    "SELECT count(*)::int AS n FROM fincrime_cases WHERE tenant_id = $1 AND subject_ref = $2",
    [tenantId, subjectRef],
  );
  return Number(r.rows[0].n);
}
async function openedEvents(tenantId: string, caseId: string) {
  const r = await owner.query<{
    event_type: string;
    from_state: string | null;
    to_state: string | null;
    payload_hash: string;
    audit_chain_entry_id: number | null;
  }>(
    "SELECT event_type, from_state, to_state, payload_hash, audit_chain_entry_id FROM fincrime_case_events WHERE tenant_id = $1 AND case_id = $2",
    [tenantId, caseId],
  );
  return r.rows;
}
async function attemptAuditCount(caseId: string): Promise<number> {
  const r = await owner.query<{ n: string }>(
    "SELECT count(*)::int AS n FROM audit_chain_entries WHERE action = 'FINCRIME_CASE_OPEN_ATTEMPT' AND resource = $1",
    [`fincrime_case:${caseId}`],
  );
  return Number(r.rows[0].n);
}
// role-switched (RLS-subject) visibility: count under the app role with the
// tenant GUC set — proves RLS actually scopes the row, not just an app filter.
async function rlsVisibleCount(tenantId: string, hash: string): Promise<number> {
  return withTenantRls(tenantId, async (tdb) => {
    const res: any = await tdb.execute(
      sql`SELECT count(*)::int AS n FROM fincrime_cases WHERE source_signal_hash = ${hash}`,
    );
    return Number(res.rows[0].n);
  });
}

const hashFor = (body: Record<string, unknown>): string =>
  computeSourceSignalHash(ingestSignalSchema.parse(body));

// ── HTTP cookie jar + login/post helpers ─────────────────────────────────────
class Jar {
  private cookies = new Map<string, string>();
  ingest(res: Response) {
    const setCookies =
      (res.headers as unknown as { getSetCookie?: () => string[] }).getSetCookie?.() ?? [];
    for (const sc of setCookies) {
      const pair = sc.split(";")[0];
      const idx = pair.indexOf("=");
      if (idx > 0) this.cookies.set(pair.slice(0, idx).trim(), pair.slice(idx + 1).trim());
    }
  }
  header(): string {
    return [...this.cookies.entries()].map(([k, v]) => `${k}=${v}`).join("; ");
  }
}

interface Session {
  jar: Jar;
  csrf: string;
  xff: string;
}

async function getCsrf(jar: Jar, xff: string): Promise<string> {
  const res = await fetch(`${SERVER}/api/csrf-token`, {
    headers: { cookie: jar.header(), "x-forwarded-for": xff },
  });
  jar.ingest(res);
  return ((await res.json().catch(() => ({}))) as any)?.csrfToken;
}

async function login(username: string, password: string, xff: string): Promise<Session> {
  const jar = new Jar();
  const res = await fetch(`${SERVER}/api/auth/login`, {
    method: "POST",
    headers: { "content-type": "application/json", "x-forwarded-for": xff },
    body: JSON.stringify({ username, password }),
  });
  jar.ingest(res);
  const j: any = await res.json().catch(() => ({}));
  if (res.status !== 200 || j?.success !== true) {
    throw new Error(`login(${username}) failed HTTP ${res.status} ${JSON.stringify(j).slice(0, 160)}`);
  }
  const csrf = await getCsrf(jar, xff);
  return { jar, csrf, xff };
}

function ingest(
  jar: Jar | null,
  csrf: string | null,
  body: unknown,
  xff: string,
): Promise<Response> {
  const headers: Record<string, string> = {
    "content-type": "application/json",
    "x-forwarded-for": xff,
  };
  if (jar) headers.cookie = jar.header();
  if (csrf) headers["x-csrf-token"] = csrf;
  return fetch(`${SERVER}/api/fincrime/ingest`, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
  });
}
async function ingestJson(jar: Jar | null, csrf: string | null, body: unknown, xff: string) {
  const res = await ingest(jar, csrf, body, xff);
  const json: any = await res.json().catch(() => ({}));
  return { status: res.status, json };
}
const ingestAs = (s: Session, body: unknown) => ingestJson(s.jar, s.csrf, body, s.xff);

// ── seeded synthetic users (cleaned up in finally) ───────────────────────────
interface Seed {
  id: string;
  username: string;
  password: string;
  role: "admin" | "risk_manager" | "auditor";
}
function mkSeed(role: Seed["role"], tag: string): Seed {
  return {
    id: randomUUID(),
    username: `fc-ing-${tag}-${base}`,
    password: `Fc!Ing-${tag}-${base}`,
    role,
  };
}
const rmSeed = mkSeed("risk_manager", "rm");
const adminSeed = mkSeed("admin", "admin");
const audSeed = mkSeed("auditor", "aud");
const allSeeds = [rmSeed, adminSeed, audSeed];

async function seedUser(s: Seed, tenantId: string): Promise<void> {
  await owner.query(
    `INSERT INTO users (id, username, password, role, full_name, is_active)
     VALUES ($1, $2, $3, $4, $5, true)`,
    [s.id, s.username, bcrypt.hashSync(s.password, 10), s.role, `FINCRIME INGEST BATTERY ${s.role} (test, delete ok)`],
  );
  await owner.query(
    `INSERT INTO user_tenant_roles (user_id, tenant_id, role, is_active, is_primary)
     VALUES ($1, $2, $3, true, true)`,
    [s.id, tenantId, s.role],
  );
}

// ─────────────────────────────────────────────────────────────────────────────
async function main() {
  console.log("=".repeat(82));
  console.log("AEGIS SOAR M1 Gate 2 — POST /api/fincrime/ingest behavioral battery");
  console.log("spec AS/SOAR/2026/001 | env: DEV |", new Date().toISOString());
  console.log("=".repeat(82));

  // valid signal fixtures (opaque subject refs; allowlisted source)
  const validBody = {
    sourceType: "KYT_ALERT",
    sourceRef: `ext-${base}-A`,
    subjectRef: `opaque:subjA-${base}`,
    signal: { amount: 5000, currency: "UGX", reason: "structuring-pattern" },
  };
  const validHash = hashFor(validBody);

  try {
    for (const s of allSeeds) await seedUser(s, TENANT_A);
    const rm = await login(rmSeed.username, rmSeed.password, RM_XFF);
    const admin = await login(adminSeed.username, adminSeed.password, ADMIN_XFF);
    const aud = await login(audSeed.username, audSeed.password, AUD_XFF);

    // ── LEG 1a — POSITIVE: configured source ⇒ exactly one OPEN case ──────────
    const r1 = await ingestAs(rm, validBody);
    await settle();
    const c1 = await casesByHash(TENANT_A, validHash);
    const caseId = r1.json?.caseId ?? c1[0]?.id;
    if (caseId) createdCaseIds.add(caseId);
    const ev1 = caseId ? await openedEvents(TENANT_A, caseId) : [];
    const audit1 = caseId ? await attemptAuditCount(caseId) : 0;
    const row1 = c1[0];
    const pass1 =
      r1.status === 201 &&
      r1.json?.created === true &&
      r1.json?.state === "OPEN" &&
      c1.length === 1 &&
      row1?.state === "OPEN" &&
      row1?.disposition === null &&
      row1?.disposition_reason === null &&
      row1?.dispositioned_at === null &&
      row1?.str_filed === false &&
      row1?.source_type === "KYT_ALERT" &&
      row1?.source_ref === validBody.sourceRef &&
      row1?.subject_ref === validBody.subjectRef &&
      row1?.subject_ref_kind === "OPAQUE_EXTERNAL_REF" &&
      row1?.tenant_id === TENANT_A &&
      typeof row1?.audit_chain_entry_id === "number" &&
      ev1.length === 1 &&
      ev1[0].event_type === "CASE_OPENED" &&
      ev1[0].from_state === null &&
      ev1[0].to_state === "OPEN" &&
      /^[0-9a-f]{64}$/.test(ev1[0].payload_hash ?? "") &&
      typeof ev1[0].audit_chain_entry_id === "number" &&
      audit1 === 1;
    rec({
      id: "1a",
      scenario: "POSITIVE risk_manager + allowlisted KYT_ALERT signal",
      expectation:
        "HTTP 201 created; exactly ONE durable OPEN case; all disposition fields NULL; str_filed false; opaque subject_ref + source fields persisted; audit_chain_entry_id set; exactly one CASE_OPENED event (null→OPEN, payload_hash); exactly one FINCRIME_CASE_OPEN_ATTEMPT audit row",
      observed: `HTTP ${r1.status}; created=${r1.json?.created}; cases=${c1.length}; state=${row1?.state}; disp=${row1?.disposition}/${row1?.disposition_reason}/${row1?.dispositioned_at}; str=${row1?.str_filed}; auditFk=${row1?.audit_chain_entry_id}; events=${ev1.length}(${ev1[0]?.event_type} ${ev1[0]?.from_state}->${ev1[0]?.to_state}); attemptAudits=${audit1}`,
      verdict: pass1
        ? "PASS — functionally-verified (HTTP 201 + owner BYPASSRLS read-back of case+event+audit)"
        : "FAIL",
      pass: pass1,
    });

    // ── LEG 1b — DENIAL: unknown/disabled source ⇒ nothing ───────────────────
    const unkSubject = `opaque:subjUnknown-${base}`;
    const r1b = await ingestAs(rm, {
      sourceType: "UNKNOWN_SOURCE_X",
      subjectRef: unkSubject,
      signal: { amount: 1 },
    });
    await settle();
    const unkCount = await caseCountBySubjectAnyTenant(unkSubject);
    const pass1b = r1b.status === 400 && unkCount === 0;
    rec({
      id: "1b",
      scenario: "DENIAL risk_manager + source NOT in the allowlist (UNKNOWN_SOURCE_X)",
      expectation: "HTTP 400; ZERO cases created for that subject (honest no-source default — never a synthetic alert)",
      observed: `HTTP ${r1b.status}; casesForSubject(any tenant)=${unkCount}`,
      verdict: pass1b
        ? "PASS — functionally-verified (HTTP 400 + owner read-back proves no row in any tenant)"
        : "FAIL",
      pass: pass1b,
    });

    // ── LEG 2a — IDEMPOTENCY: identical replay ⇒ existing, no new side-effects ─
    const r2 = await ingestAs(rm, validBody);
    await settle();
    const c2 = await casesByHash(TENANT_A, validHash);
    const ev2 = await openedEvents(TENANT_A, caseId);
    const audit2 = await attemptAuditCount(caseId);
    const pass2 =
      r2.status === 200 &&
      r2.json?.created === false &&
      r2.json?.caseId === caseId &&
      c2.length === 1 &&
      ev2.length === 1 &&
      audit2 === 1;
    rec({
      id: "2a",
      scenario: "IDEMPOTENCY — identical signal replayed (same canonical hash)",
      expectation:
        "HTTP 200 created=false; SAME caseId; still exactly ONE case, ONE CASE_OPENED event, ONE attempt audit (no duplicate side-effects)",
      observed: `HTTP ${r2.status}; created=${r2.json?.created}; sameCaseId=${r2.json?.caseId === caseId}; cases=${c2.length}; events=${ev2.length}; attemptAudits=${audit2}`,
      verdict: pass2
        ? "PASS — functionally-verified (HTTP 200 replay + owner read-back: counts unchanged)"
        : "FAIL",
      pass: pass2,
    });

    // ── LEG 2b — IDEMPOTENCY: key-reordered signal hashes identically ─────────
    const reordered = {
      signal: { reason: "structuring-pattern", currency: "UGX", amount: 5000 },
      subjectRef: validBody.subjectRef,
      sourceRef: validBody.sourceRef,
      sourceType: validBody.sourceType,
    };
    const r2b = await ingestAs(rm, reordered);
    await settle();
    const c2b = await casesByHash(TENANT_A, validHash);
    const pass2b =
      r2b.status === 200 &&
      r2b.json?.created === false &&
      r2b.json?.caseId === caseId &&
      c2b.length === 1 &&
      hashFor(reordered) === validHash;
    rec({
      id: "2b",
      scenario: "IDEMPOTENCY — same identity, keys reordered (canonical-hash stability)",
      expectation: "key-sorted canonical hash is identical ⇒ HTTP 200 created=false, SAME caseId, still ONE case",
      observed: `hashStable=${hashFor(reordered) === validHash}; HTTP ${r2b.status}; created=${r2b.json?.created}; sameCaseId=${r2b.json?.caseId === caseId}; cases=${c2b.length}`,
      verdict: pass2b
        ? "PASS — functionally-verified (reordered replay dedupes to the same case)"
        : "FAIL",
      pass: pass2b,
    });

    // ── LEG 3a — CROSS-TENANT payload injection refused (strict DTO) ──────────
    const xtSubject = `opaque:xtenant-${base}`;
    const r3a1 = await ingestAs(rm, {
      sourceType: "KYT_ALERT",
      subjectRef: xtSubject,
      tenantId: TENANT_B, // unknown key — .strict() must reject
      signal: {},
    });
    const r3a2 = await ingestAs(rm, {
      sourceType: "KYT_ALERT",
      subjectRef: xtSubject,
      tenant_id: TENANT_B, // snake_case variant — also unknown
      signal: {},
    });
    await settle();
    const xtInB = await caseCountBySubjectInTenant(TENANT_B, xtSubject);
    const xtInA = await caseCountBySubjectInTenant(TENANT_A, xtSubject);
    const pass3a =
      r3a1.status === 400 && r3a2.status === 400 && xtInB === 0 && xtInA === 0;
    rec({
      id: "3a",
      scenario: "DENIAL — authed-as-A POSTs a payload carrying tenantId/tenant_id = B",
      expectation: "HTTP 400 on both (strict DTO rejects the unknown tenant key); ZERO cases in tenant B AND none in A (nothing landed)",
      observed: `HTTP tenantId=${r3a1.status} tenant_id=${r3a2.status}; casesInB=${xtInB}; casesInA=${xtInA}`,
      verdict: pass3a
        ? "PASS — functionally-verified (payload cannot assert tenant; owner read-back proves no B row)"
        : "FAIL",
      pass: pass3a,
    });

    // ── LEG 3b — RLS isolation (role-switched reads): A sees it, B does not ───
    const seenA = await rlsVisibleCount(TENANT_A, validHash);
    const seenB = await rlsVisibleCount(TENANT_B, validHash);
    const pass3b = seenA === 1 && seenB === 0;
    rec({
      id: "3b",
      scenario: "RLS isolation of the LEG-1a case under the app role (GUC role-switched)",
      expectation: "visible under withTenantRls(A)=1 (positive); invisible under withTenantRls(B)=0 (denial)",
      observed: `rlsVisible(A)=${seenA}; rlsVisible(B)=${seenB}`,
      verdict: pass3b
        ? "PASS — functionally-verified (same row, app-role + tenant GUC: A reads it, B is denied by RLS)"
        : "FAIL",
      pass: pass3b,
    });

    // ── LEG 4 — PII / malformed subjectRef refused (paired w/ 1a opaque accept)
    const piiSubject = "john.doe@example.com";
    const r4 = await ingestAs(rm, {
      sourceType: "KYT_ALERT",
      subjectRef: piiSubject, // fails the opaque-prefix regex
      signal: {},
    });
    await settle();
    const piiCount = await caseCountBySubjectAnyTenant(piiSubject);
    const pass4 =
      r4.status === 400 &&
      piiCount === 0 &&
      JSON.stringify(r4.json).toLowerCase().includes("fincrime_signal");
    rec({
      id: "4",
      scenario: "DENIAL — raw-PII subjectRef (email) rejected; opaque-only spine enforced",
      expectation: "HTTP 400 (opaque-prefix regex fails); ZERO rows; (paired positive: 1a accepted an opaque ref)",
      observed: `HTTP ${r4.status}; casesForPii=${piiCount}; body=${JSON.stringify(r4.json).slice(0, 120)}`,
      verdict: pass4
        ? "PASS — functionally-verified (raw PII refused at the DTO; no row landed)"
        : "FAIL",
      pass: pass4,
    });

    // ── LEG 5a — POSITIVE: admin role is also authorized ──────────────────────
    const adminBody = {
      sourceType: "SANCTIONS_SCREEN_HIT",
      subjectRef: `opaque:subjAdmin-${base}`,
      signal: { list: "OFAC" },
    };
    const adminHash = hashFor(adminBody);
    const r5a = await ingestAs(admin, adminBody);
    await settle();
    const c5a = await casesByHash(TENANT_A, adminHash);
    if (c5a[0]?.id) createdCaseIds.add(c5a[0].id);
    const pass5a =
      r5a.status === 201 && r5a.json?.created === true && c5a.length === 1 && c5a[0].state === "OPEN";
    rec({
      id: "5a",
      scenario: "POSITIVE admin role + allowlisted SANCTIONS_SCREEN_HIT",
      expectation: "HTTP 201 created; exactly ONE OPEN case (admin is an authorized ingester alongside risk_manager)",
      observed: `HTTP ${r5a.status}; created=${r5a.json?.created}; cases=${c5a.length}; state=${c5a[0]?.state}`,
      verdict: pass5a
        ? "PASS — functionally-verified (HTTP 201 + owner read-back of the OPEN case)"
        : "FAIL",
      pass: pass5a,
    });

    // ── LEG 5b — DENIAL: auditor (wrong role) refused ────────────────────────
    const audSubject = `opaque:subjAuditor-${base}`;
    const r5b = await ingestAs(aud, {
      sourceType: "KYT_ALERT",
      subjectRef: audSubject,
      signal: {},
    });
    await settle();
    const audCount = await caseCountBySubjectAnyTenant(audSubject);
    const pass5b = r5b.status === 403 && audCount === 0;
    rec({
      id: "5b",
      scenario: "DENIAL — authenticated auditor (role not admin/risk_manager)",
      expectation: "HTTP 403 (requireRole denies); ZERO cases (paired positives: 1a risk_manager, 5a admin)",
      observed: `HTTP ${r5b.status}; casesForSubject=${audCount}`,
      verdict: pass5b
        ? "PASS — functionally-verified (HTTP 403 + owner read-back proves no row)"
        : "FAIL",
      pass: pass5b,
    });

    // ── LEG 5c — DENIAL: unauthenticated (valid CSRF, no session) ─────────────
    const anonJar = new Jar();
    const anonCsrf = await getCsrf(anonJar, ANON_XFF); // csrf without logging in
    const unauthSubject = `opaque:subjUnauth-${base}`;
    const r5c = await ingestJson(anonJar, anonCsrf, {
      sourceType: "KYT_ALERT",
      subjectRef: unauthSubject,
      signal: {},
    }, ANON_XFF);
    await settle();
    const unauthCount = await caseCountBySubjectAnyTenant(unauthSubject);
    const pass5c = (r5c.status === 401 || r5c.status === 403) && unauthCount === 0;
    rec({
      id: "5c",
      scenario: "DENIAL — unauthenticated request (CSRF satisfied, no session)",
      expectation: "HTTP 401/403 (requireAuth/CSRF denies); ZERO cases (paired positives: 1a/5a authenticated)",
      observed: `HTTP ${r5c.status}; casesForSubject=${unauthCount}`,
      verdict: pass5c
        ? "PASS — functionally-verified (rejected unauthenticated + owner read-back proves no row)"
        : "FAIL",
      pass: pass5c,
    });

    // ── LEG 6 — ATOMICITY: no respond-before-commit (zero-delay cross-conn read)
    const atomBody = {
      sourceType: "MANUAL_REFERRAL",
      subjectRef: `opaque:subjAtomic-${base}`,
      signal: { note: "commit-before-respond probe" },
    };
    const atomHash = hashFor(atomBody);
    const r6 = await ingestAs(rm, atomBody);
    // NO settle: the route awaits COMMIT before responding, so a separate
    // BYPASSRLS connection MUST already see the row the instant the 201 returns.
    const c6 = await casesByHash(TENANT_A, atomHash);
    if (c6[0]?.id) createdCaseIds.add(c6[0].id);
    const pass6 = r6.status === 201 && c6.length === 1 && c6[0].state === "OPEN";
    rec({
      id: "6",
      scenario: "ATOMICITY — durable row visible to a separate connection with ZERO delay after the 201",
      expectation: "the case is committed BEFORE the response is sent (no respond-before-commit false-green)",
      observed: `HTTP ${r6.status}; immediateCrossConnRows=${c6.length}; state=${c6[0]?.state}`,
      verdict: pass6
        ? "PASS — functionally-verified (zero-delay cross-connection read sees the committed row)"
        : "FAIL",
      pass: pass6,
    });
  } finally {
    // cleanup: synthetic cases + their events first (composite FK: events→cases),
    // then UTRs/sessions; deactivate users (hard-delete blocked by audit FK refs).
    // audit_chain_entries are immutable (hash-chained) and intentionally preserved.
    try {
      const ids = [...createdCaseIds];
      if (ids.length) {
        const evDel = await owner.query(
          "DELETE FROM fincrime_case_events WHERE case_id = ANY($1::varchar[])",
          [ids],
        );
        const caseDel = await owner.query(
          "DELETE FROM fincrime_cases WHERE id = ANY($1::varchar[])",
          [ids],
        );
        console.log(`\ncleanup: deleted ${evDel.rowCount} event row(s), ${caseDel.rowCount} case row(s).`);
      }
    } catch (e: any) {
      console.log(`cleanup cases/events (non-fatal): ${e?.message ?? e}`);
    }
    for (const s of allSeeds) {
      try {
        await owner.query("DELETE FROM user_tenant_roles WHERE user_id = $1", [s.id]);
        await owner.query("DELETE FROM sessions WHERE user_id = $1", [s.id]);
        await owner.query(
          "UPDATE users SET is_active = false, password = gen_random_uuid()::text WHERE id = $1",
          [s.id],
        );
      } catch (e: any) {
        console.log(`cleanup user ${s.username} (non-fatal): ${e?.message ?? e}`);
      }
    }
    console.log("cleanup: synthetic seed users deactivated + credentials neutralized (audit chain preserved).");
    await owner.end().catch(() => {});
  }

  console.log("");
  for (const r of results) {
    console.log("-".repeat(82));
    console.log(`[${r.id}] ${r.scenario}`);
    console.log(`     expect  : ${r.expectation}`);
    console.log(`     observe : ${r.observed}`);
    console.log(`     VERDICT : ${r.verdict}`);
  }
  console.log("-".repeat(82));
  const passed = results.filter((r) => r.pass).length;
  const allPass = results.length > 0 && results.every((r) => r.pass);
  console.log(`ACCEPTANCE: ${passed}/${results.length} checks PASS — ${allPass ? "ALL PASS" : "SEE FAILURES ABOVE"}`);
  process.exit(allPass ? 0 : 1);
}

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