// Audit Class 1 Gate C — requirePlatformRole role-matrix [DIRECT-DEV] verification.
//
// requirePlatformRole(...roles) is the parameterized global-role gate applied across the
// 160 platform-scoped routes in routes.ts. It preserves each route's existing role allowlist
// but evaluates it against the GLOBAL users.role (never the tenant-scoped resolveEffectiveRole).
// This suite proves BOTH halves of the Class 1 bar at once:
//   (1) the admin-trap is closed — a tenant-scoped admin (whose GLOBAL role is non-admin) is
//       denied even a platform READ gate; and
//   (2) NO over-lock — a GLOBAL auditor still passes a platform evidence-read gate
//       requirePlatformRole("admin","risk_manager","auditor") (examiners keep reading evidence),
//       while being correctly denied an admin-only mutation gate requirePlatformRole("admin").
//
// Requires DATABASE_URL + boot secrets (runs under test:integration).

import { test, describe, before, after } from "node:test";
import assert from "node:assert/strict";
import { eq } from "drizzle-orm";
import { db } from "../server/db.js";
import { users } from "../shared/schema.js";
import { requirePlatformRole } from "../server/lib/rbac-middleware.js";

// user_role enum is exactly [admin, risk_manager, auditor]. The dangerous platform routes are
// MUTATIONS (admin-only gate): the trap closes there — a tenant-scoped admin whose GLOBAL role
// is risk_manager/auditor is denied. Reads legitimately admit all three privileged global roles.
const RUN = `pr-test-${Date.now()}`;
const ADMIN_ID = `${RUN}-admin`;
const AUDITOR_ID = `${RUN}-auditor`;
const RISK_ID = `${RUN}-risk`; // models a "tenant-scoped admin" whose GLOBAL role is non-admin

async function invoke(roles: string[], userId: string | undefined): Promise<{ status: number; nexted: boolean }> {
  const req: any = { session: { userId }, traceId: "test", path: "/api/test-platform" };
  let status = 200;
  let nexted = false;
  const res: any = { status(c: number) { status = c; return this; }, json(_: unknown) { return this; } };
  await requirePlatformRole(...roles)(req, res, () => { nexted = true; });
  return { status, nexted };
}

const READ_GATE = ["admin", "risk_manager", "auditor"]; // platform evidence read
const MUT_GATE = ["admin"];                              // platform mutation

describe("Audit Class 1 Gate C — requirePlatformRole global-role gate", () => {
  before(async () => {
    for (const [id, role] of [[ADMIN_ID, "admin"], [AUDITOR_ID, "auditor"], [RISK_ID, "risk_manager"]] as const) {
      await db.delete(users).where(eq(users.id, id));
      await db.insert(users).values({ id, username: id, password: "x", role, fullName: id });
    }
  });
  after(async () => {
    for (const id of [ADMIN_ID, AUDITOR_ID, RISK_ID]) await db.delete(users).where(eq(users.id, id));
  });

  test("global admin passes a platform READ gate -> 200", async () => {
    const r = await invoke(READ_GATE, ADMIN_ID);
    assert.equal(r.nexted, true); assert.equal(r.status, 200);
  });

  test("global admin passes a platform MUTATION gate -> 200", async () => {
    const r = await invoke(MUT_GATE, ADMIN_ID);
    assert.equal(r.nexted, true); assert.equal(r.status, 200);
  });

  test("NO over-lock: global auditor passes a platform READ gate (evidence access preserved) -> 200", async () => {
    const r = await invoke(READ_GATE, AUDITOR_ID);
    assert.equal(r.nexted, true, "a global auditor must still read platform evidence");
    assert.equal(r.status, 200);
  });

  test("global auditor is denied an admin-only MUTATION gate -> 403", async () => {
    const r = await invoke(MUT_GATE, AUDITOR_ID);
    assert.equal(r.nexted, false, "auditor must not pass an admin-only platform mutation");
    assert.equal(r.status, 403);
  });

  test("trap closed: tenant-scoped admin (global role 'risk_manager') denied a platform MUTATION gate -> 403", async () => {
    const r = await invoke(MUT_GATE, RISK_ID);
    assert.equal(r.nexted, false, "a tenant-scoped admin's non-admin GLOBAL role must NOT reach a platform mutation");
    assert.equal(r.status, 403);
  });

  test("global risk_manager still passes a platform READ gate (reads are not the danger) -> 200", async () => {
    const r = await invoke(READ_GATE, RISK_ID);
    assert.equal(r.nexted, true); assert.equal(r.status, 200);
  });

  test("unauthenticated (no session) -> 401", async () => {
    const r = await invoke(READ_GATE, undefined);
    assert.equal(r.nexted, false); assert.equal(r.status, 401);
  });
});
