/**
 * AS/PLATFORM/2026/007 — W5b verification harness (ML governance cluster).
 *
 * Modules under test (all DEV, Rule 17):
 *   server/lib/drift-detection.ts      (REAL-candidate — real ai_thresholds read + deterministic severity)
 *   server/lib/model-drift.ts          (FABRICATED — hardcoded champion + Math.random drift)
 *   server/lib/adaptive-thresholds.ts  (DEMO + FABRICATED metrics over a REAL update/auto-tune algorithm)
 *   server/lib/pilot-readiness-extras-2.runModelDriftCheck  (DB twin — random-as-data, persisted)
 *   server/lib/wave5-services.{recordModelDrift,getDriftHistory,saveAdaptiveThreshold,loadAdaptiveThresholds}
 *                                      (DB-backed persistence twins; adaptive_threshold_configs is RLS-enrolled)
 *
 * Role discipline (plan §Discipline / Rule 17): every DB-touching leg runs as
 * the RLS-subject role aegis_app via runAsTenant() — which pins an appPool
 * client, sets the tenant GUC, and activates tenantContext so the module-level
 * `db` Proxy resolves to the GUC-set client (mirrors the request middleware).
 * In-process pure-compute legs (model-drift, adaptive-thresholds algorithm)
 * have no role axis. Paired POS/NEG (Rule 18). Synthetic, self-cleaning rows.
 *
 * NO secret values are printed. Run: npx tsx scripts/vf-w5b-ml-governance-proof.ts
 */
import { eq } from "drizzle-orm";
import { appPool, ddlPool, tenantContext, makeClientRunner, db } from "../server/db";
import { aiThresholds } from "../shared/schema";
import { adaptiveThresholdConfigs } from "../shared/schema-wave5";
import { modelDriftRuns } from "../shared/schema-pilot-extras-2";
import {
  detectDrift,
  captureConfigSnapshot,
  getDriftStats,
  getDriftHistory as getDriftDetHistory,
} from "../server/lib/drift-detection";
import {
  getModelHealth,
  getModelDriftStatus,
  simulateDriftCheck,
} from "../server/lib/model-drift";
import {
  getThreshold,
  recordOutcome,
  getThresholdDashboard,
  getAllThresholds,
} from "../server/lib/adaptive-thresholds";
import { runModelDriftCheck } from "../server/lib/pilot-readiness-extras-2";
import {
  recordModelDrift,
  getDriftHistory as getDbDriftHistory,
  saveAdaptiveThreshold,
  loadAdaptiveThresholds,
} from "../server/lib/wave5-services";

let pass = 0;
let fail = 0;
const lines: string[] = [];
function check(name: string, cond: boolean, detail = "") {
  if (cond) {
    pass++;
    lines.push(`  PASS  ${name}${detail ? ` — ${detail}` : ""}`);
  } else {
    fail++;
    lines.push(`  FAIL  ${name}${detail ? ` — ${detail}` : ""}`);
  }
}
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));

// Replicate the request path so module-`db`-proxy code runs as aegis_app under
// the tenant GUC (withTenantRls does NOT activate tenantContext; this does).
async function runAsTenant<T>(tenantId: string, fn: () => Promise<T>): Promise<T> {
  const client = await appPool.connect();
  try {
    await client.query("BEGIN");
    await client.query("SELECT set_config('app.current_tenant_id', $1, true)", [tenantId]);
    const runner = makeClientRunner(client);
    const out = await tenantContext.run({ runner, tenantId }, fn);
    await client.query("COMMIT");
    return out;
  } catch (e) {
    try { await client.query("ROLLBACK"); } catch { /* noop */ }
    throw e;
  } finally {
    client.release();
  }
}

async function main() {
  const tRows = (await appPool.query("SELECT id, slug FROM tenants ORDER BY slug")).rows as Array<{ id: string; slug: string }>;
  const tenantA = tRows.find((r) => r.slug === "aegis-sovereign")?.id;
  const tenantB = tRows.find((r) => r.slug === "stanbic-ug")?.id;
  if (!tenantA || !tenantB) throw new Error(`could not resolve tenants A/B from ${JSON.stringify(tRows)}`);

  // Capture demo-seed evidence BEFORE any recordOutcome mutates adaptive-thresholds state.
  const bootSamples = getThresholdDashboard().overallMetrics.totalSamples;

  lines.push("== A. drift-detection.ts — REAL-candidate (real DB read + deterministic severity), as aegis_app ==");

  // A1 (DB read, aegis_app): captureConfigSnapshot reads the real ai_thresholds table.
  const PROBE_THR = "vf_w5b_probe_x";
  await runAsTenant(tenantA, async () => {
    await db.delete(aiThresholds).where(eq(aiThresholds.thresholdName, PROBE_THR));
    await db.insert(aiThresholds).values({ thresholdName: PROBE_THR, thresholdValue: "0.99" });
  });
  const snap = await runAsTenant(tenantA, async () => captureConfigSnapshot());
  check(
    "A1 captureConfigSnapshot real read (aegis_app) returns seeded row [POS]",
    snap.settings[PROBE_THR] !== undefined && Math.abs(parseFloat(snap.settings[PROBE_THR]) - 0.99) < 1e-6,
    `settings['${PROBE_THR}']=${snap.settings[PROBE_THR]}`,
  );
  // A1b: hash is base64 of the real (non-empty) settings JSON, NOT the catch-block "" fallback.
  check(
    "A1b read hit the try-path not the swallow-catch fallback [POS]",
    snap.hash !== "" && Buffer.from(snap.hash, "base64").toString().includes(PROBE_THR),
    `hash decodes to settings JSON containing probe`,
  );
  await runAsTenant(tenantA, async () => {
    await db.delete(aiThresholds).where(eq(aiThresholds.thresholdName, PROBE_THR));
  });

  // Deterministic severity classification. Each call in its own aegis_app tx.
  // CRITICAL legs attempt appendToChain (broken W4b forensic-chain, F-W4b-1); the
  // in-mem configHistory.push (drift-detection.ts:103) precedes that write
  // (drift-detection.ts:116), so the classification is verifiable even if the
  // forensic write rolls back — we read it back from getDriftHistory/getDriftStats.
  async function detectLeg(
    name: string,
    settings: Record<string, string>,
    expSeverity: "LOW" | "MEDIUM" | "HIGH" | "CRITICAL" | null,
    expAuthorized: boolean | null,
  ) {
    const before = getDriftStats().total;
    let threw = false;
    try {
      await runAsTenant(tenantA, async () => { await detectDrift("vf-w5b", settings); });
    } catch { threw = true; }
    const after = getDriftStats().total;
    const delta = after - before;
    if (expSeverity === null) {
      check(name, delta === 0, `events added=${delta} (expected 0); threw=${threw}`);
      return;
    }
    const hist = getDriftDetHistory(Math.max(delta, 1));
    const ev = hist[hist.length - 1];
    check(
      name,
      delta === 1 && ev?.severity === expSeverity && ev?.authorized === expAuthorized,
      `delta=${delta} sev=${ev?.severity} authorized=${ev?.authorized} threw=${threw}`,
    );
  }

  await detectLeg("A2 anomaly_threshold=0.3 → CRITICAL + authorized=false [POS]", { anomaly_threshold: "0.3" }, "CRITICAL", false);
  await detectLeg("A3 auto_neutralize=false → CRITICAL [POS]", { auto_neutralize: "false" }, "CRITICAL", false);
  await detectLeg("A4 behavioral_weight=0.9 → HIGH + authorized=true [POS]", { behavioral_weight: "0.9" }, "HIGH", true);
  await detectLeg("A5 geo_weight=0.7 → MEDIUM [POS]", { geo_weight: "0.7" }, "MEDIUM", true);
  await detectLeg("A6 vf_unknown_setting → LOW (default bucket) [POS]", { vf_unknown_setting: "x" }, "LOW", true);
  await detectLeg("A7 behavioral_weight=0.9 unchanged → NO drift event [NEG]", { behavioral_weight: "0.9" }, null, null);

  lines.push("== B. model-drift.ts — FABRICATED (hardcoded champion + Math.random drift) ==");
  const h = getModelHealth();
  check(
    "B1 champion accuracy/FPR/FNR are hardcoded constants [fabrication confirmed]",
    h.accuracy === 0.967 && h.falsePositiveRate === 0.023 && h.falseNegativeRate === 0.008 && h.modelId === "sentinel-v4.2.1",
    `acc=${h.accuracy} fpr=${h.falsePositiveRate} fnr=${h.falseNegativeRate} id=${h.modelId}`,
  );
  const drift1 = JSON.stringify(getModelHealth().featureDrifts.map((d: any) => d.psi));
  simulateDriftCheck();
  const drift2 = JSON.stringify(getModelHealth().featureDrifts.map((d: any) => d.psi));
  check(
    "B2 featureDrifts mutate via Math.random random-walk (non-deterministic) [fabrication confirmed]",
    drift1 !== drift2,
    "psi vector changed across simulateDriftCheck() with no real input",
  );
  const status = getModelDriftStatus();
  const champKeys = Object.keys(status.champion);
  const hasDisclosure = champKeys.some((k) => /sim|demo|mock|fake|synthetic/i.test(k));
  check(
    "B3 getModelDriftStatus() surfaces fabricated champion with NO simulation disclosure [fabrication confirmed]",
    status.champion.accuracy === 0.967 && !hasDisclosure && /HEALTHY|DEGRADED|CRITICAL/.test(status.champion.status),
    `status=${status.champion.status} disclosureKey=${hasDisclosure}`,
  );

  lines.push("== C. adaptive-thresholds.ts — REAL algorithm over DEMO/FABRICATED inputs ==");
  // C3: demo-seed evidence (captured pre-mutation): metrics populated at init by
  // generateRandomMetrics with no real outcomes ever recorded.
  check(
    "C3 thresholds carry metrics at boot with zero real outcomes recorded → demo-seeded [DEMO confirmed]",
    bootSamples > 0,
    `boot totalSamples=${bootSamples}`,
  );
  // C1: the recordOutcome accounting + precision recompute IS a real deterministic algorithm.
  const thr0 = getAllThresholds()[0];
  const id = thr0.id;
  const m0 = { ...getThreshold(id)!.metrics };
  recordOutcome(id, true, true); // true positive
  const m1 = { ...getThreshold(id)!.metrics };
  const precOk1 = Math.abs(m1.precision - m1.truePositives / (m1.truePositives + m1.falsePositives)) < 1e-9;
  check(
    "C1 recordOutcome(TP) increments TP and recomputes precision deterministically [REAL algo POS]",
    m1.truePositives === m0.truePositives + 1 && precOk1,
    `tp ${m0.truePositives}→${m1.truePositives}, precision=${m1.precision}`,
  );
  recordOutcome(id, true, false); // false positive
  const m2 = { ...getThreshold(id)!.metrics };
  const precOk2 = Math.abs(m2.precision - m2.truePositives / (m2.truePositives + m2.falsePositives)) < 1e-9;
  check(
    "C1b recordOutcome(FP) increments FP and lowers precision (formula holds) [REAL algo NEG]",
    m2.falsePositives === m1.falsePositives + 1 && m2.precision < m1.precision && precOk2,
    `fp ${m1.falsePositives}→${m2.falsePositives}, precision ${m1.precision}→${m2.precision}`,
  );
  // C2: lastAutoTuneRun is a hardcoded (now - 2h), NOT a real last-run timestamp.
  const d1 = getThresholdDashboard().lastAutoTuneRun!;
  const expected = Date.now() - 2 * 60 * 60 * 1000;
  const hardcodedNear = Math.abs(d1.getTime() - expected) < 5000;
  await sleep(1100);
  const d2 = getThresholdDashboard().lastAutoTuneRun!;
  check(
    "C2 lastAutoTuneRun is hardcoded (now − 2h) and advances with wall-clock, never reflecting a real tune [FABRICATED freshness]",
    hardcodedNear && d2.getTime() > d1.getTime(),
    `d1≈now-2h=${hardcodedNear}, d2>d1=${d2.getTime() > d1.getTime()}`,
  );

  lines.push("== D. DB-backed twins — as aegis_app ==");
  // D1: runModelDriftCheck persists a real row but driftScore derives from a
  // Math.random "synthetic current distribution" (FABRICATED-but-persisted).
  const r1 = await runAsTenant(tenantA, async () => runModelDriftCheck("vf-w5b-probe", "vf-w5b-model"));
  const r2 = await runAsTenant(tenantA, async () => runModelDriftCheck("vf-w5b-probe", "vf-w5b-model"));
  check(
    "D1 runModelDriftCheck currentMean varies randomly across runs (no real scoring) [fabrication confirmed]",
    r1.currentMean !== r2.currentMean && r1.id !== r2.id,
    `currentMean ${r1.currentMean} vs ${r2.currentMean}`,
  );
  await runAsTenant(tenantA, async () => { await db.delete(modelDriftRuns).where(eq(modelDriftRuns.triggeredBy, "vf-w5b-probe")); });

  // D2: recordModelDrift / getDriftHistory — persistence CRUD (round-trip).
  // AS/PLATFORM/2026/008 Tenant-Sep Chunk A: model_drift_history is now tenant_id
  // (NOT NULL) + RLS-enrolled and recordModelDrift is fail-closed → tenantId required.
  const PM = "vf-w5b-pm";
  await runAsTenant(tenantA, async () => {
    await recordModelDrift({
      modelId: PM, modelVersion: "9.9.9", psiScore: 0.11, ksScore: 0.13,
      featureDrifts: [], status: "TEST", tenantId: tenantA,
    });
  });
  const hist = await runAsTenant(tenantA, async () => getDbDriftHistory(PM));
  const none = await runAsTenant(tenantA, async () => getDbDriftHistory("vf-w5b-nonexistent"));
  check(
    "D2 recordModelDrift→getDriftHistory round-trips the persisted row [REAL persistence POS]",
    hist.length >= 1 && hist[0].modelId === PM && hist[0].status === "TEST",
    `rows=${hist.length}`,
  );
  check(
    "D2b getDriftHistory filters by modelId (unknown id → 0 rows) [REAL persistence NEG]",
    none.length === 0,
    `rows=${none.length}`,
  );
  // D2c model_drift_history — SUPERSEDED by AS/PLATFORM/2026/008 Tenant-Sep Chunk A.
  // This leg ORIGINALLY proved the F-W5b-1 gap (no tenant_id → cross-tenant visible).
  // Chunk A added tenant_id (NOT NULL) + RLS-enrolled the table, so A's row is now
  // INVISIBLE to B. Leg polarity inverted to assert the fix.
  // (Dedicated Chunk-A proof: scripts/vf-tsa4-tenant-sep-chunkA-proof.ts MD-L2.)
  const histFromB = await runAsTenant(tenantB, async () => getDbDriftHistory(PM));
  check(
    "D2c model_drift_history NOT cross-tenant readable — RLS-enrolled (AS/2026/008 Chunk A) [F-W5b-1 REMEDIATED]",
    histFromB.length === 0,
    `tenantB sees rows=${histFromB.length} (expect 0)`,
  );
  await runAsTenant(tenantA, async () => { await db.execute(`DELETE FROM model_drift_history WHERE model_id = '${PM}'`); });

  // D3: adaptive_threshold_configs IS RLS-enrolled (tenant_id = current_tenant_id()).
  const THRA = "vf-w5b-thrA";
  await runAsTenant(tenantA, async () => {
    await saveAdaptiveThreshold({
      id: THRA, name: "vf-probe", currentValue: 1, minValue: 0, maxValue: 10, defaultValue: 1,
      autoTuneEnabled: false, autoTuneSettings: {}, metrics: {}, history: [],
      tenantId: tenantA, updatedBy: "vf-w5b",
    });
  });
  const loadedA = await runAsTenant(tenantA, async () => loadAdaptiveThresholds(tenantA));
  check(
    "D3 saveAdaptiveThreshold→load within owner tenant returns the row [RLS POS]",
    loadedA.some((t: any) => t.id === THRA),
    `tenantA rows=${loadedA.length}`,
  );
  const loadedB = await runAsTenant(tenantB, async () => loadAdaptiveThresholds(tenantB));
  check(
    "D3b cross-tenant: tenant B does NOT see tenant A's threshold (RLS USING) [RLS NEG]",
    !loadedB.some((t: any) => t.id === THRA),
    `tenantB sees A-row=${loadedB.some((t: any) => t.id === THRA)}`,
  );
  // D3c: a NULL-tenant row is UNWRITABLE under RLS (NULL = current_tenant_id() → not true),
  // so the loadAdaptiveThresholds JS filter `!t.tenantId || ...` (global-NULL leak) is dead defense-in-depth.
  let nullRejected = false;
  try {
    await runAsTenant(tenantA, async () => {
      await saveAdaptiveThreshold({
        id: "vf-w5b-thrNULL", name: "vf-null", currentValue: 1, minValue: 0, maxValue: 10, defaultValue: 1,
        autoTuneEnabled: false, autoTuneSettings: {}, metrics: {}, history: [],
        tenantId: undefined, updatedBy: "vf-w5b",
      });
    });
  } catch { nullRejected = true; }
  check(
    "D3c NULL-tenant write rejected by RLS WITH CHECK → JS NULL-global-leak path is unreachable [RLS POS]",
    nullRejected,
    "INSERT with tenant_id=NULL violates WITH CHECK",
  );
  await runAsTenant(tenantA, async () => { await db.delete(adaptiveThresholdConfigs).where(eq(adaptiveThresholdConfigs.id, THRA)); });

  lines.push("");
  lines.push(`RESULT: ${pass} PASS / ${fail} FAIL  (total ${pass + fail})`);
  console.log(lines.join("\n"));

  await appPool.end().catch(() => {});
  await ddlPool.end().catch(() => {});
  process.exit(fail === 0 ? 0 : 1);
}

main().catch((e) => {
  console.error(lines.join("\n"));
  console.error("HARNESS ERROR:", e);
  process.exit(2);
});
