/**
 * KYC v1 — FEATURE 3 — DECISION-ENGINE FUNCTIONAL PROOF (binding exit-criterion).
 * Spec: docs/regulatory/AEGIS_CYBER_KYC_V1_BUILD_SPEC.md (AS/KYC/2026/002).
 *
 * Central guarantee under proof (spec §6 / Part C):
 *   A high-risk / PEP / sanctioned subject can NEVER be auto-approved. A customer
 *   becomes APPROVED only through the ONE locked-door finalizer
 *   (storage.recordApprovalAndFinalize), and only when EVERY precondition holds:
 *   the outcome is an APPROVE awaiting senior sign-off, fresh re-collected signals
 *   STILL decide APPROVE, the fresh signals hash equals the decided-on hash, the
 *   approver is a senior authority (risk_manager/admin), and the approver is a
 *   DIFFERENT person than the decider (two-eyes).
 *
 * Rule 18 (paired controls): every denial assertion is paired, in the same run,
 * with a positive-enforcement assertion, so a green is never HOLLOW-GREEN.
 *
 * Why injected fakes (plan T2.6): the live adapters are all "none" (NOT_CONFIGURED)
 * ⇒ every subject is INDETERMINATE ⇒ escalates ⇒ APPROVE is unreachable (the correct
 * safe state). The ONLY way to exercise positive APPROVE paths is to inject real-
 * provider FAKES in-process. These fakes live ONLY in this test file — no synthetic
 * provider is ever added to any adapter allowlist. The running server (:5000) is a
 * SEPARATE process and is untouched by this harness (it still uses the "none" seam).
 *
 *   LEG 0 — PURE engine decision matrix (no DB): structural never-approve, identity
 *           reject, composition (indeterminate ⇒ escalate), sanctions potential,
 *           EDD/PEP + high-risk approvable-only-with-senior, standard approve. Global
 *           sweep: decide() NEVER sets a customer APPROVED, and EVERY APPROVE is
 *           state=PENDING_SENIOR_APPROVAL + requiresSeniorApproval=true.
 *   LEG 1 — PURE locked-door guard matrix (assertFinalizePreconditions): a valid arg
 *           set passes (positive); each single-field mutation (wrong disposition /
 *           state / not-senior-required / fresh-not-APPROVE / hash mismatch / non-
 *           senior role / two-eyes) throws (denials).
 *   LEG 2 — REAL storage locked door (dev DB, RLS-enforced tenant tx via
 *           runWithTenantContext, injected fakes), end-to-end decide→persist→finalize:
 *           2a two-eyes (decider==approver) refused, then a different senior succeeds
 *              (a HIGH-risk subject approved ONLY via two-eyes; never at decide time);
 *           2b non-senior role refused, then senior succeeds;
 *           2c stale signals-hash refused, then unchanged signals succeed;
 *           2d circumstances-changed (became sanctioned) refused, then restored succeed;
 *           2e cross-tenant: context mismatch (500), other-tenant NOT_FOUND, and
 *              no-active-context (500) all refused; same-tenant finalize succeeds;
 *           2f double-approve refused (exactly one approval row); first approve succeeds;
 *           2g front door: createKycCustomer/updateKycCustomerStatusAndRisk cannot write
 *              APPROVED (the locked door's bypass is the ONLY APPROVED writer).
 *
 * Side-effect discipline: imports only storage, db (withTenantRls), tenant-context,
 * the decision engine, and the three adapters' test setters — NOT server/index.ts or
 * server/routes.ts (no server / scheduler boot).
 */

import pg from "pg";
import { randomUUID } from "node:crypto";
import { readFileSync, readdirSync, statSync } from "node:fs";
import { join } from "node:path";
import { storage } from "../server/storage";
import { withTenantRls } from "../server/db";
import { runWithTenantContext } from "../server/lib/tenant-context";
import type { KYCRiskTier } from "@shared/schema";
import {
  collectFreshSignals,
  deriveRiskTier,
  coerceRiskTier,
  buildSignalsSnapshot,
  computeSignalsHash,
  decide,
  assertFinalizePreconditions,
  FinalizeGuardError,
  type DecisionPlan,
  type FreshSignals,
  type FinalizePreconditionArgs,
  type KycSubjectForDecision,
} from "../server/lib/kyc/decision-engine";
import {
  __setIdentityVerificationAdapterForTests,
  __resetIdentityVerificationAdapterCacheForTests,
  type IdentityVerificationAdapter,
  type IdentityVerificationInput,
  type IdentityVerificationResult,
  type IdentityVerificationStatus,
} from "../server/lib/kyc/identity-verification-adapter";
import {
  __setPepScreeningAdapterForTests,
  __resetPepScreeningAdapterCacheForTests,
  type PepScreeningAdapter,
} from "../server/lib/kyc/pep-screening-adapter";
import {
  __setSanctionsScreeningAdapterForTests,
  __resetSanctionsScreeningAdapterCacheForTests,
  type SanctionsScreeningAdapter,
} from "../server/lib/kyc/sanctions-screening-adapter";
import {
  type ScreeningInput,
  type ScreeningKind,
  type ScreeningResult,
  type ScreeningStatus,
} from "../server/lib/kyc/screening-types";

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

const TENANT_A = "aegis-sovereign"; // real seeded dev tenant
const TENANT_B = "stanbic-ug-001"; // a DIFFERENT real seeded dev tenant
const base = Date.now();

const cid = (n: string) => `kyc-f3-${base}-${n}`; // sentinel customer id
const nid = (n: string) => `NID-F3-${base}-${n}`; // sentinel national id (plaintext)
const corr = (n: string) => `kyc-f3-${base}-${n}`;
const DECIDER = `kyc-f3-decider-${base}`;
const APPROVER = `kyc-f3-approver-${base}`;

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 createdOutcomeIds: string[] = [];

// ── test-only FAKE provider adapters (NEVER added to any allowlist) ───────────
class FakeIdentityAdapter implements IdentityVerificationAdapter {
  readonly id: string;
  readonly displayName = "FAKE identity provider (test-only)";
  constructor(private status: IdentityVerificationStatus, providerId = "fake-identity-v1") {
    this.id = providerId;
  }
  isConfigured(): boolean {
    return true;
  }
  async verifyIdentity(input: IdentityVerificationInput): Promise<IdentityVerificationResult> {
    const verified = this.status === "VERIFIED";
    return {
      status: this.status,
      verified,
      confidence: verified ? 0.99 : undefined,
      providerReference: verified ? `fake-ref-${input.customerId}` : undefined,
      evidenceSummary: `FAKE identity result: ${this.status} (test harness).`,
      providerId: this.id,
      checkedAt: new Date().toISOString(),
    };
  }
}

function buildScreening(kind: ScreeningKind, status: ScreeningStatus, providerId: string): ScreeningResult {
  const isMatch = status === "POTENTIAL_MATCH" || status === "CONFIRMED_MATCH";
  const screened = status === "CLEAR" || isMatch;
  return {
    kind,
    status,
    screened,
    matches: isMatch
      ? [{ listName: kind === "SANCTIONS" ? "OFAC SDN" : "PEP-Tier1", score: 0.95 }]
      : [],
    evidenceSummary: `FAKE ${kind} result: ${status} (test harness).`,
    providerId,
    checkedAt: new Date().toISOString(),
  };
}

class FakePepAdapter implements PepScreeningAdapter {
  readonly id: string;
  readonly displayName = "FAKE PEP provider (test-only)";
  constructor(private status: ScreeningStatus, providerId = "fake-pep-v1") {
    this.id = providerId;
  }
  isConfigured(): boolean {
    return true;
  }
  async screenPep(_input: ScreeningInput): Promise<ScreeningResult> {
    return buildScreening("PEP", this.status, this.id);
  }
}

class FakeSanctionsAdapter implements SanctionsScreeningAdapter {
  readonly id: string;
  readonly displayName = "FAKE sanctions provider (test-only)";
  constructor(private status: ScreeningStatus, providerId = "fake-sanctions-v1") {
    this.id = providerId;
  }
  isConfigured(): boolean {
    return true;
  }
  async screenSanctions(_input: ScreeningInput): Promise<ScreeningResult> {
    return buildScreening("SANCTIONS", this.status, this.id);
  }
}

function inject(
  idStatus: IdentityVerificationStatus,
  pepStatus: ScreeningStatus,
  sanStatus: ScreeningStatus,
  sanProvider = "fake-sanctions-v1",
): void {
  __setIdentityVerificationAdapterForTests(new FakeIdentityAdapter(idStatus));
  __setPepScreeningAdapterForTests(new FakePepAdapter(pepStatus));
  __setSanctionsScreeningAdapterForTests(new FakeSanctionsAdapter(sanStatus, sanProvider));
}
function setSanctions(status: ScreeningStatus, provider = "fake-sanctions-v1"): void {
  __setSanctionsScreeningAdapterForTests(new FakeSanctionsAdapter(status, provider));
}
function resetFakes(): void {
  __resetIdentityVerificationAdapterCacheForTests();
  __resetPepScreeningAdapterCacheForTests();
  __resetSanctionsScreeningAdapterCacheForTests();
}

// ── pure-signal fixtures for LEG 0 / LEG 1 (built directly, no adapters) ───────
function idResult(status: IdentityVerificationStatus): IdentityVerificationResult {
  const verified = status === "VERIFIED";
  return {
    status,
    verified,
    confidence: verified ? 0.99 : undefined,
    providerReference: verified ? "fake-ref" : undefined,
    evidenceSummary: `fixture ${status}`,
    providerId: status === "NOT_CONFIGURED" ? "none" : "fake-identity-v1",
    checkedAt: new Date().toISOString(),
  };
}
function scrResult(kind: ScreeningKind, status: ScreeningStatus): ScreeningResult {
  const isMatch = status === "POTENTIAL_MATCH" || status === "CONFIRMED_MATCH";
  const screened = status === "CLEAR" || isMatch;
  return {
    kind,
    status,
    screened,
    matches: isMatch ? [{ listName: kind === "SANCTIONS" ? "OFAC SDN" : "PEP-Tier1", score: 0.9 }] : [],
    evidenceSummary: `fixture ${status}`,
    providerId: status === "NOT_CONFIGURED" ? "none" : kind === "PEP" ? "fake-pep-v1" : "fake-sanctions-v1",
    checkedAt: new Date().toISOString(),
  };
}
function FS(
  idStatus: IdentityVerificationStatus,
  pepStatus: ScreeningStatus,
  sanStatus: ScreeningStatus,
): FreshSignals {
  return {
    identity: idResult(idStatus),
    pep: scrResult("PEP", pepStatus),
    sanctions: scrResult("SANCTIONS", sanStatus),
  };
}

// ── owner (BYPASSRLS) read-back helpers ──────────────────────────────────────
async function ownerCustomer(id: string): Promise<{ status: string; risk_tier: string } | null> {
  const r = await owner.query<{ status: string; risk_tier: string }>(
    "SELECT status, risk_tier FROM kyc_customers WHERE id = $1",
    [id],
  );
  return r.rows[0] ?? null;
}
async function ownerOutcomeState(id: string): Promise<string | null> {
  const r = await owner.query<{ state: string }>(
    "SELECT state FROM kyc_decision_outcomes WHERE id = $1",
    [id],
  );
  return r.rows[0]?.state ?? null;
}
async function ownerApprovalCount(decisionId: string): Promise<number> {
  const r = await owner.query<{ n: string }>(
    "SELECT count(*)::int AS n FROM kyc_decision_approvals WHERE decision_id = $1",
    [decisionId],
  );
  return Number(r.rows[0].n);
}

async function expectThrow(
  fn: () => Promise<unknown>,
): Promise<{ threw: boolean; msg: string; isGuard: boolean; code: string | undefined }> {
  try {
    await fn();
    return { threw: false, msg: "(did NOT throw)", isGuard: false, code: undefined };
  } catch (e) {
    const err = e as { message?: string; code?: string };
    return {
      threw: true,
      msg: err?.message ?? String(e),
      isGuard: e instanceof FinalizeGuardError,
      code: err?.code,
    };
  }
}

async function seedCustomer(n: string, riskTier: KYCRiskTier, tenantId = TENANT_A): Promise<void> {
  await withTenantRls(tenantId, (tdb) =>
    storage.createKycCustomer(
      {
        id: cid(n),
        nationalId: nid(n),
        fullName: `KYC-F3 Subject ${n}`,
        dateOfBirth: "1990-01-01",
        phoneNumber: "+256700000000",
        riskTier,
      },
      tenantId,
      { _db: tdb },
    ),
  );
}

// Decide + persist an outcome inside a REAL RLS-enforced tenant tx (mirrors the
// decide route's engine flow: collect fresh signals → derive → decide → snapshot →
// hash → createKycDecisionOutcome). Returns the persisted outcome id + hash + plan.
async function decideAndPersist(
  tenantId: string,
  customerId: string,
  deciderUserId: string,
  correlationId: string,
): Promise<{ outcomeId: string; hash: string; plan: DecisionPlan }> {
  return runWithTenantContext(tenantId, async () => {
    const customer = await storage.getKycCustomerById(customerId, tenantId);
    if (!customer) throw new Error(`decideAndPersist: seed missing ${customerId}`);
    const existing = coerceRiskTier(customer.riskTier);
    const subject: KycSubjectForDecision = {
      id: customer.id,
      tenantId,
      nationalId: customer.nationalId,
      fullName: customer.fullName,
      dateOfBirth: customer.dateOfBirth,
      phoneNumber: customer.phoneNumber ?? null,
      riskTier: existing,
    };
    const signals = await collectFreshSignals(subject, correlationId);
    const derived = deriveRiskTier(existing, signals);
    const plan = decide(signals, derived);
    const snapshot = buildSignalsSnapshot(tenantId, customer.id, existing, derived, signals);
    const hash = computeSignalsHash(snapshot);
    const outcome = await storage.createKycDecisionOutcome(
      {
        customerId: customer.id,
        disposition: plan.disposition,
        strFlag: plan.strFlag,
        strReason: plan.strReason,
        state: plan.state,
        requiresSeniorApproval: plan.requiresSeniorApproval,
        riskTier: plan.derivedRiskTier,
        signalsSnapshot: JSON.stringify(snapshot),
        signalsHash: hash,
        explanation: `${plan.ruleId}: ${plan.explanation}`,
        decidedByUserId: deciderUserId,
        decidedByRole: "admin",
      },
      tenantId,
    );
    createdOutcomeIds.push(outcome.id);
    return { outcomeId: outcome.id, hash, plan };
  });
}

function finalize(
  tenantId: string,
  decisionId: string,
  approverUserId: string,
  approverRole: string,
  correlationId: string,
  finalizeTenantId: string = tenantId,
): Promise<unknown> {
  // tenantId = the ACTIVE request context; finalizeTenantId = the tenant passed in
  // the finalize args (normally identical — differs only for the cross-tenant test).
  return runWithTenantContext(tenantId, () =>
    storage.recordApprovalAndFinalize({
      decisionId,
      tenantId: finalizeTenantId,
      approverUserId,
      approverRole,
      reason: "harness",
      correlationId,
    }),
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// LEG 0 — PURE engine decision matrix
// ─────────────────────────────────────────────────────────────────────────────
function leg0(): void {
  interface Case {
    name: string;
    existing: KYCRiskTier;
    signals: FreshSignals;
    disposition: DecisionPlan["disposition"];
    ruleId: string;
    derived: KYCRiskTier;
    strFlag: boolean;
    role: "denial" | "positive";
  }
  const cases: Case[] = [
    { name: "verified + pep clear + sanctions clear, LOW", existing: "LOW", signals: FS("VERIFIED", "CLEAR", "CLEAR"), disposition: "APPROVE", ruleId: "R-APPROVE-STANDARD", derived: "LOW", strFlag: false, role: "positive" },
    { name: "sanctions CONFIRMED ⇒ hard reject", existing: "LOW", signals: FS("VERIFIED", "CLEAR", "CONFIRMED_MATCH"), disposition: "REJECT", ruleId: "R-SANCTIONS-CONFIRMED", derived: "SANCTIONS", strFlag: true, role: "denial" },
    { name: "identity NOT_VERIFIED ⇒ reject", existing: "LOW", signals: FS("NOT_VERIFIED", "CLEAR", "CLEAR"), disposition: "REJECT", ruleId: "R-IDENTITY-NOT-VERIFIED", derived: "LOW", strFlag: false, role: "denial" },
    { name: "sanctions POTENTIAL ⇒ escalate (STR)", existing: "LOW", signals: FS("VERIFIED", "CLEAR", "POTENTIAL_MATCH"), disposition: "ESCALATE_TO_EDD", ruleId: "R-SANCTIONS-POTENTIAL", derived: "SANCTIONS", strFlag: true, role: "denial" },
    { name: "identity NOT_CONFIGURED ⇒ composition escalate", existing: "LOW", signals: FS("NOT_CONFIGURED", "CLEAR", "CLEAR"), disposition: "ESCALATE_TO_EDD", ruleId: "R-INDETERMINATE", derived: "LOW", strFlag: false, role: "denial" },
    { name: "pep NOT_CONFIGURED ⇒ composition escalate", existing: "LOW", signals: FS("VERIFIED", "NOT_CONFIGURED", "CLEAR"), disposition: "ESCALATE_TO_EDD", ruleId: "R-INDETERMINATE", derived: "LOW", strFlag: false, role: "denial" },
    { name: "sanctions PROVIDER_ERROR ⇒ composition escalate", existing: "LOW", signals: FS("VERIFIED", "CLEAR", "PROVIDER_ERROR"), disposition: "ESCALATE_TO_EDD", ruleId: "R-INDETERMINATE", derived: "LOW", strFlag: false, role: "denial" },
    { name: "PEP POTENTIAL + verified + sanctions clear ⇒ approve w/ senior", existing: "LOW", signals: FS("VERIFIED", "POTENTIAL_MATCH", "CLEAR"), disposition: "APPROVE", ruleId: "R-APPROVE-EDD-SENIOR", derived: "PEP", strFlag: true, role: "positive" },
    { name: "existing HIGH + all clear ⇒ approve w/ senior (never downgraded)", existing: "HIGH", signals: FS("VERIFIED", "CLEAR", "CLEAR"), disposition: "APPROVE", ruleId: "R-APPROVE-EDD-SENIOR", derived: "HIGH", strFlag: false, role: "positive" },
    { name: "existing MEDIUM + all clear ⇒ standard approve", existing: "MEDIUM", signals: FS("VERIFIED", "CLEAR", "CLEAR"), disposition: "APPROVE", ruleId: "R-APPROVE-STANDARD", derived: "MEDIUM", strFlag: false, role: "positive" },
  ];

  let neverAutoApproved = true; // decide() NEVER sets a customer APPROVED
  let everyApproveTwoEyes = true; // every APPROVE ⇒ PENDING_SENIOR_APPROVAL + requiresSeniorApproval
  let nonApproveNoSenior = true; // REJECT/ESCALATE ⇒ requiresSeniorApproval=false
  const failed: string[] = [];

  for (const c of cases) {
    const derived = deriveRiskTier(c.existing, c.signals);
    const plan = decide(c.signals, derived);

    if (plan.nextCustomerStatus === "APPROVED") neverAutoApproved = false;
    if (plan.disposition === "APPROVE") {
      if (plan.state !== "PENDING_SENIOR_APPROVAL" || plan.requiresSeniorApproval !== true)
        everyApproveTwoEyes = false;
    } else if (plan.requiresSeniorApproval !== false) {
      nonApproveNoSenior = false;
    }

    const ok =
      plan.disposition === c.disposition &&
      plan.ruleId === c.ruleId &&
      derived === c.derived &&
      plan.strFlag === c.strFlag &&
      plan.nextCustomerStatus !== "APPROVED";
    if (!ok) {
      failed.push(
        `${c.name}: got disp=${plan.disposition} rule=${plan.ruleId} derived=${derived} str=${plan.strFlag} next=${plan.nextCustomerStatus}`,
      );
    }
  }

  const matrixPass = failed.length === 0;
  rec({
    id: "L0",
    scenario: `engine decision matrix — ${cases.length} cases (positive APPROVE paths + denial paths)`,
    expectation:
      "each case yields the exact expected disposition/rule/derived-tier/STR; positives reach APPROVE, denials reject/escalate",
    observed: matrixPass ? `all ${cases.length} cases matched` : `MISMATCH: ${failed.join(" | ")}`,
    verdict: matrixPass ? "PASS (deterministic precedence proven across the matrix)" : "FAIL",
    pass: matrixPass,
  });

  const sweepPass = neverAutoApproved && everyApproveTwoEyes && nonApproveNoSenior;
  rec({
    id: "L0-INV",
    scenario: "central invariant sweep over the whole matrix",
    expectation:
      "decide() NEVER returns nextCustomerStatus=APPROVED; every APPROVE is PENDING_SENIOR_APPROVAL+requiresSeniorApproval; non-approvals require no senior",
    observed: `neverAutoApproved=${neverAutoApproved}; everyApproveTwoEyes=${everyApproveTwoEyes}; nonApproveNoSenior=${nonApproveNoSenior}`,
    verdict: sweepPass ? "PASS (no decide path can auto-approve a customer)" : "FAIL",
    pass: sweepPass,
  });
}

// ─────────────────────────────────────────────────────────────────────────────
// LEG 1 — PURE locked-door guard matrix (assertFinalizePreconditions)
// ─────────────────────────────────────────────────────────────────────────────
function leg1(): void {
  const HASH = "a".repeat(64);
  const approvePlan = decide(FS("VERIFIED", "CLEAR", "CLEAR"), "LOW"); // APPROVE
  const rejectPlan = decide(FS("NOT_VERIFIED", "CLEAR", "CLEAR"), "LOW"); // REJECT
  const baseArgs: FinalizePreconditionArgs = {
    outcome: {
      disposition: "APPROVE",
      state: "PENDING_SENIOR_APPROVAL",
      requiresSeniorApproval: true,
      signalsHash: HASH,
      decidedByUserId: DECIDER,
    },
    freshPlan: approvePlan,
    freshHash: HASH,
    approverUserId: APPROVER,
    approverRole: "admin",
  };

  // positive: a fully valid arg set must NOT throw.
  let positivePass = false;
  let positiveErr = "";
  try {
    assertFinalizePreconditions(baseArgs);
    positivePass = true;
  } catch (e) {
    positiveErr = (e as Error)?.message ?? String(e);
  }

  const mutate = (m: Partial<FinalizePreconditionArgs>): FinalizePreconditionArgs => ({
    ...baseArgs,
    ...m,
    outcome: { ...baseArgs.outcome, ...(m.outcome ?? {}) },
  });

  const denials: Array<{ name: string; args: FinalizePreconditionArgs }> = [
    { name: "outcome disposition != APPROVE", args: mutate({ outcome: { ...baseArgs.outcome, disposition: "ESCALATE_TO_EDD" } }) },
    { name: "outcome state != PENDING_SENIOR_APPROVAL", args: mutate({ outcome: { ...baseArgs.outcome, state: "FINAL" } }) },
    { name: "outcome does not require senior approval", args: mutate({ outcome: { ...baseArgs.outcome, requiresSeniorApproval: false } }) },
    { name: "fresh re-decision is not APPROVE (circumstances changed)", args: mutate({ freshPlan: rejectPlan }) },
    { name: "signals hash mismatch (stale)", args: mutate({ freshHash: "b".repeat(64) }) },
    { name: "approver role is not senior (auditor)", args: mutate({ approverRole: "auditor" }) },
    { name: "two-eyes: approver == decider", args: mutate({ approverUserId: DECIDER }) },
  ];

  const denialResults = denials.map((d) => {
    let threw = false;
    let guard = false;
    let msg = "";
    try {
      assertFinalizePreconditions(d.args);
    } catch (e) {
      threw = true;
      guard = e instanceof FinalizeGuardError;
      msg = (e as Error)?.message ?? String(e);
    }
    return { name: d.name, threw, guard, msg };
  });

  const allDenied = denialResults.every((r) => r.threw && r.guard);
  const pass = positivePass && allDenied;
  rec({
    id: "L1",
    scenario: "assertFinalizePreconditions — valid set passes; each single violation throws",
    expectation:
      "valid args do NOT throw (positive); all 7 single-field violations throw FinalizeGuardError (denials)",
    observed: positivePass
      ? `positive=PASS; denials: ${denialResults.map((r) => `${r.threw && r.guard ? "✓" : "✗"}`).join("")} (${denialResults.filter((r) => r.threw && r.guard).length}/7)`
      : `positive=FAIL ("${positiveErr.slice(0, 80)}")`,
    verdict: pass ? "PASS (the locked-door guard opens only when EVERY condition holds)" : "FAIL",
    pass,
  });
  if (!allDenied) {
    for (const r of denialResults.filter((x) => !(x.threw && x.guard))) {
      rec({
        id: "L1-x",
        scenario: `denial leak: ${r.name}`,
        expectation: "throws FinalizeGuardError",
        observed: `threw=${r.threw} guard=${r.guard} msg="${r.msg.slice(0, 80)}"`,
        verdict: "FAIL",
        pass: false,
      });
    }
  }
}

// ─────────────────────────────────────────────────────────────────────────────
// LEG 2 — REAL storage locked door (dev DB, RLS tenant tx, injected fakes)
// ─────────────────────────────────────────────────────────────────────────────

// 2a — two-eyes refused, then a DIFFERENT senior succeeds (HIGH-risk subject
//      approvable ONLY via two-eyes; never auto-approved at decide time).
async function leg2a(): Promise<void> {
  inject("VERIFIED", "CLEAR", "CLEAR");
  await seedCustomer("a", "HIGH");
  const { outcomeId, plan } = await decideAndPersist(TENANT_A, cid("a"), DECIDER, corr("a"));
  const beforeApprove = await ownerCustomer(cid("a"));

  const sameDecider = await expectThrow(() => finalize(TENANT_A, outcomeId, DECIDER, "admin", corr("a")));
  const afterDenial = await ownerCustomer(cid("a"));

  await finalize(TENANT_A, outcomeId, APPROVER, "admin", corr("a"));
  const afterApprove = await ownerCustomer(cid("a"));
  const outState = await ownerOutcomeState(outcomeId);
  const approvals = await ownerApprovalCount(outcomeId);

  const pass =
    plan.disposition === "APPROVE" &&
    plan.requiresSeniorApproval === true &&
    beforeApprove?.status !== "APPROVED" &&
    sameDecider.threw &&
    sameDecider.isGuard &&
    /two-eyes/i.test(sameDecider.msg) &&
    afterDenial?.status !== "APPROVED" &&
    afterApprove?.status === "APPROVED" &&
    afterApprove?.risk_tier === "HIGH" &&
    outState === "FINAL" &&
    approvals === 1;
  rec({
    id: "L2a",
    scenario: "HIGH-risk: two-eyes refused (decider==approver), then a different senior succeeds",
    expectation:
      "decide proposes APPROVE+senior (customer NOT approved); decider==approver THROWS (customer stays non-APPROVED); a different admin finalizes ⇒ APPROVED, riskTier HIGH, outcome FINAL, 1 approval",
    observed: `decideApprove=${plan.disposition}/senior=${plan.requiresSeniorApproval}; preApprove=${beforeApprove?.status}; sameDeciderThrew=${sameDecider.threw}(guard=${sameDecider.isGuard}); postDenial=${afterDenial?.status}; final=${afterApprove?.status}/${afterApprove?.risk_tier}; outState=${outState}; approvals=${approvals}`,
    verdict: pass ? "PASS (a high-risk subject is approved ONLY via two-eyes senior sign-off)" : "FAIL",
    pass,
  });
}

// 2b — non-senior role refused, then a senior succeeds.
async function leg2b(): Promise<void> {
  inject("VERIFIED", "CLEAR", "CLEAR");
  await seedCustomer("b", "HIGH");
  const { outcomeId } = await decideAndPersist(TENANT_A, cid("b"), DECIDER, corr("b"));

  const auditor = await expectThrow(() => finalize(TENANT_A, outcomeId, APPROVER, "auditor", corr("b")));
  const afterDenial = await ownerCustomer(cid("b"));

  await finalize(TENANT_A, outcomeId, APPROVER, "risk_manager", corr("b"));
  const afterApprove = await ownerCustomer(cid("b"));

  const pass =
    auditor.threw &&
    auditor.isGuard &&
    /not a senior authority/i.test(auditor.msg) &&
    afterDenial?.status !== "APPROVED" &&
    afterApprove?.status === "APPROVED";
  rec({
    id: "L2b",
    scenario: "non-senior approver refused, then risk_manager succeeds",
    expectation: "approverRole=auditor THROWS (not approved); approverRole=risk_manager finalizes ⇒ APPROVED",
    observed: `auditorThrew=${auditor.threw}(guard=${auditor.isGuard}) msg="${auditor.msg.slice(0, 60)}"; postDenial=${afterDenial?.status}; final=${afterApprove?.status}`,
    verdict: pass ? "PASS (only risk_manager/admin can finalize)" : "FAIL",
    pass,
  });
}

// 2c — stale signals-hash refused, then unchanged signals succeed.
async function leg2c(): Promise<void> {
  inject("VERIFIED", "CLEAR", "CLEAR", "fake-sanctions-v1");
  await seedCustomer("c", "LOW");
  const { outcomeId } = await decideAndPersist(TENANT_A, cid("c"), DECIDER, corr("c"));

  // signals change (different sanctions provider id ⇒ different hash, still CLEAR/APPROVE)
  setSanctions("CLEAR", "fake-sanctions-v2");
  const stale = await expectThrow(() => finalize(TENANT_A, outcomeId, APPROVER, "admin", corr("c")));
  const afterDenial = await ownerCustomer(cid("c"));

  // restore the original signals ⇒ fresh hash matches the decided-on hash
  setSanctions("CLEAR", "fake-sanctions-v1");
  await finalize(TENANT_A, outcomeId, APPROVER, "admin", corr("c"));
  const afterApprove = await ownerCustomer(cid("c"));

  const pass =
    stale.threw &&
    stale.isGuard &&
    /hash mismatch/i.test(stale.msg) &&
    afterDenial?.status !== "APPROVED" &&
    afterApprove?.status === "APPROVED";
  rec({
    id: "L2c",
    scenario: "stale signals-hash refused, then unchanged signals succeed",
    expectation: "changed signals ⇒ hash mismatch THROWS (not approved); restored signals ⇒ APPROVED",
    observed: `staleThrew=${stale.threw}(guard=${stale.isGuard}) msg="${stale.msg.slice(0, 60)}"; postDenial=${afterDenial?.status}; final=${afterApprove?.status}`,
    verdict: pass ? "PASS (a senior approval is bound to the exact decided-on signals)" : "FAIL",
    pass,
  });
}

// 2d — circumstances changed (became sanctioned) refused, then restored succeed.
async function leg2d(): Promise<void> {
  inject("VERIFIED", "CLEAR", "CLEAR");
  await seedCustomer("d", "HIGH");
  const { outcomeId } = await decideAndPersist(TENANT_A, cid("d"), DECIDER, corr("d"));

  // subject becomes a confirmed sanctions match AFTER the decision
  setSanctions("CONFIRMED_MATCH");
  const changed = await expectThrow(() => finalize(TENANT_A, outcomeId, APPROVER, "admin", corr("d")));
  const afterDenial = await ownerCustomer(cid("d"));

  // restore clear signals ⇒ fresh re-decision is APPROVE again and hash matches
  setSanctions("CLEAR");
  await finalize(TENANT_A, outcomeId, APPROVER, "admin", corr("d"));
  const afterApprove = await ownerCustomer(cid("d"));

  const pass =
    changed.threw &&
    changed.isGuard &&
    /fresh re-decision/i.test(changed.msg) &&
    /REJECT/.test(changed.msg) &&
    afterDenial?.status !== "APPROVED" &&
    afterApprove?.status === "APPROVED";
  rec({
    id: "L2d",
    scenario: "circumstances changed (became sanctioned) refused, then restored succeed",
    expectation:
      "fresh re-decision REJECT at approve time THROWS (not approved); a sanctioned subject can never be approved on a stale APPROVE; restored ⇒ APPROVED",
    observed: `changedThrew=${changed.threw}(guard=${changed.isGuard}) msg="${changed.msg.slice(0, 60)}"; postDenial=${afterDenial?.status}; final=${afterApprove?.status}`,
    verdict: pass ? "PASS (approve re-validates fresh signals; never trusts the stale decision)" : "FAIL",
    pass,
  });
}

// 2e — cross-tenant: context mismatch (500), other-tenant NOT_FOUND, and
//      no-active-context (500) refused; same-tenant finalize succeeds.
async function leg2e(): Promise<void> {
  inject("VERIFIED", "CLEAR", "CLEAR");
  await seedCustomer("e", "HIGH");
  const { outcomeId } = await decideAndPersist(TENANT_A, cid("e"), DECIDER, corr("e"));

  // (i) active context B, finalize args tenant A ⇒ tenant-context mismatch (plain Error → 500)
  const mismatch = await expectThrow(() => finalize(TENANT_B, outcomeId, APPROVER, "admin", corr("e"), TENANT_A));
  // (ii) active+args tenant B, A's outcome ⇒ tenant-scoped lookup NOT_FOUND
  const crossTenant = await expectThrow(() => finalize(TENANT_B, outcomeId, APPROVER, "admin", corr("e")));
  // (iii) no active tenant context at all ⇒ plain Error (must run inside request tx)
  const noCtx = await expectThrow(() =>
    storage.recordApprovalAndFinalize({
      decisionId: outcomeId,
      tenantId: TENANT_A,
      approverUserId: APPROVER,
      approverRole: "admin",
      reason: "harness",
      correlationId: corr("e"),
    }),
  );
  const afterDenials = await ownerCustomer(cid("e"));

  // positive: same-tenant (A) finalize succeeds
  await finalize(TENANT_A, outcomeId, APPROVER, "admin", corr("e"));
  const afterApprove = await ownerCustomer(cid("e"));

  const pass =
    mismatch.threw &&
    /context mismatch/i.test(mismatch.msg) &&
    crossTenant.threw &&
    crossTenant.isGuard &&
    crossTenant.code === "NOT_FOUND" &&
    noCtx.threw &&
    /no active request tenant transaction/i.test(noCtx.msg) &&
    afterDenials?.status !== "APPROVED" &&
    afterApprove?.status === "APPROVED";
  rec({
    id: "L2e",
    scenario: "cross-tenant + no-context refused; same-tenant succeeds",
    expectation:
      "context-mismatch THROWS (500); other-tenant lookup ⇒ NOT_FOUND; no-context THROWS (500); customer not approved by any; A finalizes ⇒ APPROVED",
    observed: `mismatch=${mismatch.threw}("${mismatch.msg.slice(0, 40)}"); crossTenant=${crossTenant.threw}/code=${crossTenant.code}; noCtx=${noCtx.threw}("${noCtx.msg.slice(0, 40)}"); postDenials=${afterDenials?.status}; final=${afterApprove?.status}`,
    verdict: pass ? "PASS (finalize is bound to the active request tenant tx; no cross-tenant approval)" : "FAIL",
    pass,
  });
}

// 2f — double-approve refused (exactly one approval row); first approve succeeds.
async function leg2f(): Promise<void> {
  inject("VERIFIED", "CLEAR", "CLEAR");
  await seedCustomer("f", "HIGH");
  const { outcomeId } = await decideAndPersist(TENANT_A, cid("f"), DECIDER, corr("f"));

  await finalize(TENANT_A, outcomeId, APPROVER, "admin", corr("f")); // first succeeds
  const second = await expectThrow(() => finalize(TENANT_A, outcomeId, `${APPROVER}-2`, "admin", corr("f")));
  const approvals = await ownerApprovalCount(outcomeId);
  const cust = await ownerCustomer(cid("f"));

  const pass =
    second.threw &&
    second.isGuard &&
    /already has a recorded senior approval/i.test(second.msg) &&
    approvals === 1 &&
    cust?.status === "APPROVED";
  rec({
    id: "L2f",
    scenario: "double-approve refused; exactly one approval row",
    expectation: "first finalize ⇒ APPROVED; a second finalize THROWS; exactly ONE approval row exists",
    observed: `secondThrew=${second.threw}(guard=${second.isGuard}) msg="${second.msg.slice(0, 60)}"; approvals=${approvals}; status=${cust?.status}`,
    verdict: pass ? "PASS (a customer is finalized at most once)" : "FAIL",
    pass,
  });
}

// 2g — front door cannot write APPROVED (the locked door's bypass is the ONLY writer).
async function leg2g(): Promise<void> {
  // createKycCustomer(status APPROVED) must throw; no row lands.
  const createApproved = await expectThrow(() =>
    withTenantRls(TENANT_A, (tdb) =>
      storage.createKycCustomer(
        {
          id: cid("g"),
          nationalId: nid("g"),
          fullName: "KYC-F3 Subject g",
          dateOfBirth: "1990-01-01",
          phoneNumber: "+256700000000",
          status: "APPROVED",
        },
        TENANT_A,
        { _db: tdb },
      ),
    ),
  );
  const gRow = await ownerCustomer(cid("g"));

  // legal update succeeds (positive); update→APPROVED throws (denial).
  await seedCustomer("g2", "LOW");
  let legalStatus = "n/a";
  await withTenantRls(TENANT_A, async (tdb) => {
    const upd = await storage.updateKycCustomerStatusAndRisk(
      cid("g2"),
      TENANT_A,
      { status: "REVIEW_REQUIRED" },
      { _db: tdb },
    );
    legalStatus = upd?.status ?? "undefined";
  });
  const updApproved = await expectThrow(() =>
    withTenantRls(TENANT_A, (tdb) =>
      storage.updateKycCustomerStatusAndRisk(cid("g2"), TENANT_A, { status: "APPROVED" }, { _db: tdb }),
    ),
  );
  const g2Row = await ownerCustomer(cid("g2"));

  const pass =
    createApproved.threw &&
    gRow === null &&
    legalStatus === "REVIEW_REQUIRED" &&
    updApproved.threw &&
    g2Row?.status === "REVIEW_REQUIRED";
  rec({
    id: "L2g",
    scenario: "front door (create/update) cannot write APPROVED",
    expectation:
      "createKycCustomer(APPROVED) THROWS (no row); a legal status update succeeds; updateKycCustomerStatusAndRisk(APPROVED) THROWS; durable row stays non-APPROVED",
    observed: `createThrew=${createApproved.threw}/rowPresent=${gRow !== null}; legalUpdate=${legalStatus}; updApprovedThrew=${updApproved.threw}; durable=${g2Row?.status}`,
    verdict: pass ? "PASS (the locked door is the ONLY APPROVED writer)" : "FAIL",
    pass,
  });
}

// 2-SINK — STATIC source-scan (regulator-facing): the ONLY code in server/ that
// performs a raw `.set({ status: "APPROVED" })` write on kyc_customers must be the
// locked-door finalizer recordApprovalAndFinalize. Every other status writer routes
// through assertNotApprovedWrite (proven functionally in L2g). This static leg proves
// no NEW bypass writer can be added without this proof turning red.
function leg2sink(): void {
  const tsFiles: string[] = [];
  const walk = (dir: string): void => {
    for (const entry of readdirSync(dir)) {
      const p = join(dir, entry);
      if (statSync(p).isDirectory()) walk(p);
      else if (p.endsWith(".ts")) tsFiles.push(p);
    }
  };
  walk("server");

  // Drizzle update-write form: `.set({ ... status: "APPROVED" ... })` on a single line.
  const writeRe = /\.set\(\{[^}]*status:\s*"APPROVED"/;
  const sinks: string[] = [];
  for (const f of tsFiles) {
    readFileSync(f, "utf8")
      .split("\n")
      .forEach((line, i) => {
        if (writeRe.test(line)) sinks.push(`${f}:${i + 1}`);
      });
  }

  // The single sink must live inside the recordApprovalAndFinalize method body.
  let insideLockedDoor = false;
  if (sinks.length === 1 && sinks[0].startsWith("server/storage.ts:")) {
    const lineNo = Number(sinks[0].split(":")[1]);
    const src = readFileSync("server/storage.ts", "utf8").split("\n");
    const methodStart = src.findIndex((l) => /async recordApprovalAndFinalize\(/.test(l));
    const nextMethod = src.findIndex((l, idx) => idx > methodStart && /^ {2}async \w+\(/.test(l));
    const methodEnd = nextMethod === -1 ? src.length : nextMethod;
    insideLockedDoor = methodStart !== -1 && lineNo > methodStart && lineNo <= methodEnd;
  }

  const pass = sinks.length === 1 && sinks[0].startsWith("server/storage.ts:") && insideLockedDoor;
  rec({
    id: "L2-SINK",
    scenario: "static scan: the locked-door finalizer is the ONLY APPROVED writer in server/",
    expectation:
      'exactly ONE `.set({ status: "APPROVED" })` write on kyc_customers exists across server/, and it is inside recordApprovalAndFinalize',
    observed: `sinks=[${sinks.join(", ") || "none"}]; insideLockedDoor=${insideLockedDoor}`,
    verdict: pass ? "PASS (no APPROVED writer exists outside the guarded finalizer)" : "FAIL",
    pass,
  });
}

async function cleanup(): Promise<void> {
  try {
    const a = await owner.query("DELETE FROM kyc_decision_approvals WHERE customer_id LIKE $1", [`kyc-f3-${base}-%`]);
    const o = await owner.query("DELETE FROM kyc_decision_outcomes WHERE customer_id LIKE $1", [`kyc-f3-${base}-%`]);
    const c = await owner.query("DELETE FROM kyc_customers WHERE id LIKE $1", [`kyc-f3-${base}-%`]);
    console.log(
      `\ncleanup: deleted ${a.rowCount} approval(s), ${o.rowCount} outcome(s), ${c.rowCount} customer(s).`,
    );
  } catch (e) {
    console.log(`cleanup error: ${(e as Error)?.message ?? e}`);
  }
}

async function main(): Promise<void> {
  console.log("=".repeat(80));
  console.log("KYC v1 — FEATURE 3 — DECISION-ENGINE FUNCTIONAL PROOF (Rule-18 paired controls)");
  console.log("spec AS/KYC/2026/002 | env: DEV |", new Date().toISOString());
  console.log("note: in-process fakes only; the running server (:5000) uses the real 'none' seam");
  console.log("=".repeat(80));

  void randomUUID; // (kept available for ad-hoc ids; sentinel ids are deterministic)
  try {
    leg0();
    leg1();
    await leg2a();
    await leg2b();
    await leg2c();
    await leg2d();
    await leg2e();
    await leg2f();
    await leg2g();
    leg2sink();
  } finally {
    resetFakes();
    await cleanup();
    await owner.end().catch(() => {});
  }

  console.log("");
  for (const r of results) {
    console.log("-".repeat(80));
    console.log(`[${r.id}] ${r.scenario}`);
    console.log(`     expect  : ${r.expectation}`);
    console.log(`     observe : ${r.observed}`);
    console.log(`     VERDICT : ${r.verdict}`);
  }
  console.log("-".repeat(80));
  const passed = results.filter((r) => r.pass).length;
  const total = results.length;
  const allPass = results.every((r) => r.pass);
  console.log(`ACCEPTANCE: ${passed}/${total} 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);
});
