/**
 * vf-w4c-incident-sla-proof.ts  (AS/PLATFORM/2026/007 — Wave W4c)
 *
 * Behavioral substrate proof for server/lib/incident-sla.ts — the incident SLA
 * tracker. Drives the REAL `incidentSlaTracker` methods through the REAL `db`
 * ALS-proxy as the RLS-subject role `aegis_app` (vf-s5 request-tx mechanism).
 * incident_slas IS an RLS-scoped table (rls-init.ts:88), so this exercises the
 * real enforcement axis.
 *
 * Proves (paired POS/NEG, Rule 18; all as aegis_app; SYNTHETIC tenants):
 *   - breach computation: fresh createIncident+ack -> isSlaBreached=false (POS);
 *     backdated (createdAt = now-2h, P1 target 15m) ack -> isSlaBreached=true (NEG).
 *   - getMetrics() COMPUTED EXACTLY from real rows (2 incidents, 1 resolved,
 *     1 open, 1 breach, slaCompliance "50.00", P1 breakdown {2,1,1}) — clean
 *     synthetic tenant => deterministic.
 *   - RLS isolation: getIncidents() (which carries NO tenant filter) returns
 *     ONLY the GUC-tenant's rows — tenant sees its rows, NOT the 2nd tenant's.
 *
 * DEFECT-CONFIRM (real finding, not a pass-by-luck): getIncidents(filters)
 *   IGNORES its filters entirely (incident-sla.ts:102-105 builds the query with
 *   no .where) — proven by asking for status='resolved' and still getting back
 *   an 'acknowledged' row. Tenant isolation therefore rides RLS ALONE.
 *
 * HOLLOW-GREEN GUARDS: owner pool (DATABASE_URL/BYPASSRLS) only seeds the
 *   backdated + foreign rows and cleans up; fixture gate confirms physical
 *   tenant_id before concluding. Fresh timestamped tenants => 0 confounders.
 *
 * WRITES: synthetic incident_slas rows, deleted + 0-residual verified (Rule 11).
 *   Requires DATABASE_URL + AEGIS_APP_DB_PASSWORD.
 * Run: cd /home/runner/workspace && timeout 115 npx tsx scripts/vf-w4c-incident-sla-proof.ts
 */
import pg from "pg";
import { randomUUID } from "crypto";
import { sql } from "drizzle-orm";
import { db, tenantContext, type DrizzleDb } from "../server/db";
import { incidentSlaTracker } from "../server/lib/incident-sla";

if (!process.env.DATABASE_URL) { console.error("FATAL: DATABASE_URL unset"); process.exit(1); }
if (!process.env.AEGIS_APP_DB_PASSWORD) { console.error("FATAL: AEGIS_APP_DB_PASSWORD unset"); process.exit(1); }

const owner = new pg.Pool({ connectionString: process.env.DATABASE_URL });
const TS = Date.now();
const T1 = `w4c-sla-${TS}`;
const T2 = `w4c-sla2-${TS}`;

async function withRequestTx<T>(tenantId: string, fn: () => Promise<T>): Promise<T> {
  return await db.transaction(async (tx) => {
    await tx.execute(sql`SELECT set_config('app.current_tenant_id', ${tenantId}, true)`);
    return await tenantContext.run({ runner: tx as unknown as DrizzleDb, tenantId }, fn);
  });
}

type Leg = { id: string; label: string; pass: boolean; detail: string };
const legs: Leg[] = [];
const rec = (id: string, label: string, pass: boolean, detail: string) => {
  legs.push({ id, label, pass, detail });
  console.log(`  [${pass ? "PASS" : "FAIL"}] ${id} — ${label}\n         ${detail}`);
};

const backdatedId = randomUUID();
const foreignId = randomUUID();

async function main() {
  console.log("=== W4c — incident-sla substrate proof (aegis_app, dev) ===");
  console.log(`  tenants: T1=${T1}  T2(foreign)=${T2}\n`);

  // ---- owner seed: backdated P1 (for NEG breach) in T1; foreign row in T2 ----
  await owner.query(
    `INSERT INTO incident_slas (id, incident_id, severity, title, status, sla_target_minutes, tenant_id, created_at)
     VALUES ($1, $2, 'P1', 'w4c-backdated', 'open', 15, $3, NOW() - INTERVAL '2 hours')`,
    [backdatedId, `INC-W4C-BD-${TS}`, T1]);
  await owner.query(
    `INSERT INTO incident_slas (id, incident_id, severity, title, status, sla_target_minutes, tenant_id)
     VALUES ($1, $2, 'P2', 'w4c-foreign', 'open', 60, $3)`,
    [foreignId, `INC-W4C-FG-${TS}`, T2]);

  // ---- fixture gate (owner; setup-only) ----
  const phys = await owner.query<{ id: string; tenant_id: string }>(
    `SELECT id, tenant_id FROM incident_slas WHERE id = ANY($1::text[])`, [[backdatedId, foreignId]]);
  const pm = new Map(phys.rows.map(r => [r.id, r.tenant_id]));
  const gateOk = pm.get(backdatedId) === T1 && pm.get(foreignId) === T2;
  rec("GATE", "physical tenant_id == expected (defeats hollow-green)", gateOk,
    `backdated=${pm.get(backdatedId)} foreign=${pm.get(foreignId)}`);
  if (!gateOk) { await cleanup(); await finish(); return; }

  // ---- tenant T1: breach computation + metrics + RLS + filter-defect ----
  await withRequestTx(T1, async () => {
    // POS: fresh incident, ack immediately -> NOT breached
    const fresh = await incidentSlaTracker.createIncident({ severity: "P1", title: "w4c-fresh", tenantId: T1 });
    const ackFresh = await incidentSlaTracker.acknowledgeIncident(fresh.id);
    rec("S1", "fresh P1 ack -> isSlaBreached=false (POS not-breached)", ackFresh?.isSlaBreached === false, `isSlaBreached=${ackFresh?.isSlaBreached}`);

    // resolve the fresh one (for the metrics breakdown)
    const resolved = await incidentSlaTracker.updateStatus(fresh.id, "resolved");
    rec("S2", "updateStatus(resolved) sets resolvedAt + status", resolved?.status === "resolved" && resolved?.resolvedAt != null,
      `status=${resolved?.status} resolvedAt=${resolved?.resolvedAt != null}`);

    // NEG: backdated incident, ack now -> breached (elapsed ~120m > 15m target)
    const ackBack = await incidentSlaTracker.acknowledgeIncident(backdatedId);
    rec("S3", "backdated P1 ack -> isSlaBreached=true + breachedAt set (NEG breached)",
      ackBack?.isSlaBreached === true && ackBack?.breachedAt != null, `isSlaBreached=${ackBack?.isSlaBreached} breachedAt=${ackBack?.breachedAt != null}`);

    // COMPUTED metrics — deterministic on the clean synthetic tenant (2 rows)
    const m = await incidentSlaTracker.getMetrics();
    const metricsOk = m.totalIncidents === 2 && m.resolvedIncidents === 1 && m.openIncidents === 1 &&
      m.slaBreaches === 1 && m.slaCompliance === "50.00" &&
      m.severityBreakdown?.P1?.total === 2 && m.severityBreakdown?.P1?.open === 1 && m.severityBreakdown?.P1?.breached === 1;
    rec("S4", "getMetrics() COMPUTED EXACTLY from real rows (2/1/1/1, 50.00, P1{2,1,1})", metricsOk,
      `total=${m.totalIncidents} resolved=${m.resolvedIncidents} open=${m.openIncidents} breaches=${m.slaBreaches} compliance=${m.slaCompliance} P1=${JSON.stringify(m.severityBreakdown?.P1)}`);

    // RLS isolation POS+NEG: getIncidents() carries NO tenant filter, yet returns only T1 rows
    const mine = await incidentSlaTracker.getIncidents();
    const ids = mine.map(i => i.id);
    const onlyMine = ids.every(i => i === fresh.id || i === backdatedId);
    rec("S5", "RLS: getIncidents() returns ONLY T1 rows (POS own + NEG not-foreign)",
      ids.includes(fresh.id) && ids.includes(backdatedId) && !ids.includes(foreignId) && onlyMine,
      `n=${ids.length} sawFresh=${ids.includes(fresh.id)} sawBackdated=${ids.includes(backdatedId)} sawForeign=${ids.includes(foreignId)} onlyMine=${onlyMine}`);

    // DEFECT-CONFIRM: getIncidents(filters) ignores filters (L102-105). Ask for
    // status='resolved' & severity='P4' yet still get the acknowledged backdated P1 row.
    const filtered = await incidentSlaTracker.getIncidents({ status: "resolved", severity: "P4" });
    const fids = filtered.map(i => i.id);
    rec("S6", "DEFECT-CONFIRM: getIncidents(filters) IGNORES filters (returns non-matching row)",
      fids.includes(backdatedId), `requested status=resolved/sev=P4 but backdated(ack'd P1) returned=${fids.includes(backdatedId)} n=${fids.length}`);
  });

  // ---- tenant T2: sees ONLY its foreign row (RLS POS from the other side) ----
  await withRequestTx(T2, async () => {
    const theirs = await incidentSlaTracker.getIncidents();
    const ids = theirs.map(i => i.id);
    rec("S7", "RLS: T2 sees ONLY its row (POS) and NOT T1's (NEG)",
      ids.includes(foreignId) && !ids.includes(backdatedId) && ids.every(i => i === foreignId),
      `n=${ids.length} sawForeign=${ids.includes(foreignId)} sawT1Backdated=${ids.includes(backdatedId)}`);
  });

  await cleanup();
  await finish();
}

async function cleanup() {
  await owner.query(`DELETE FROM incident_slas WHERE tenant_id = ANY($1::text[])`, [[T1, T2]]);
  const residual = await owner.query(`SELECT id FROM incident_slas WHERE tenant_id = ANY($1::text[])`, [[T1, T2]]);
  rec("CLEAN", "Rule 11 — 0 residual probe rows", residual.rowCount === 0, `residual=${residual.rowCount}`);
}

async function finish() {
  const ok = legs.every(l => l.pass);
  console.log(`\n${legs.filter(l => l.pass).length}/${legs.length} legs PASS — ${ok ? "ALL PASS" : "FAIL"}`);
  await owner.end();
  process.exit(ok ? 0 : 1);
}

main().catch(async (e) => { console.error("PROBE ERROR:", e); try { await owner.end(); } catch {} process.exit(1); });
