// Make-real compliance checks — availability_monitoring + change_management.
//
// Two NEW substrate checks (server/lib/compliance-checks.ts) that DERIVE a real verdict from platform
// state (health_checks / change_requests — both GLOBAL, non-RLS tables). This proves both the verdict
// LOGIC (paired-proof, injected snapshots) and the REAL DB derivation (seed throwaway rows → default
// snapshot reads them). Discipline held: fail-closed (no records ⇒ NOT_ASSESSED, never a fabricated PASS),
// and a real adverse fact (a down component / an implemented change with no approver) ⇒ FAIL.
//
// Set SECRET_GUARD_DEV_MODE=warn BEFORE the guard loads (it self-audits REQUIRED secrets at import and
// would otherwise throw in the test env), then dynamic-import the modules. Process-isolated per file.
// Throwaway-DB lane (see tests/README.md): `npm run test:integration`.

process.env.SECRET_GUARD_DEV_MODE = "warn";

import { test, describe, before, afterEach } from "node:test";
import assert from "node:assert/strict";
import type { CheckDeps, AvailabilitySnapshot, ChangeMgmtSnapshot, VulnScanArtifact, AuditCoverageSnapshot, BackupVerifiedSnapshot, StrongAuthSnapshot } from "../server/lib/compliance-checks.js";

type Check = { id: string; run: () => Promise<{ status: string; detail: string; evidence?: Record<string, unknown> }> };
let buildChecks: (deps?: CheckDeps) => Check[];
let defaultDeps: CheckDeps;
let CHECK_IDS: Record<string, string>;
let db: any;
let healthChecks: any;
let changeRequests: any;

before(async () => {
  const cc = await import("../server/lib/compliance-checks.js");
  buildChecks = cc.buildChecks as unknown as (deps?: CheckDeps) => Check[];
  defaultDeps = cc.defaultDeps;
  CHECK_IDS = cc.CHECK_IDS as unknown as Record<string, string>;
  ({ db } = await import("../server/db.js"));
  const schema = await import("../shared/schema.js");
  healthChecks = schema.healthChecks;
  changeRequests = schema.changeRequests;
});

async function runCheck(id: string, deps: Partial<CheckDeps>) {
  const merged = { ...defaultDeps, ...deps } as CheckDeps;
  const check = buildChecks(merged).find((c) => c.id === id);
  assert.ok(check, `check ${id} must be registered`);
  return check!.run();
}

const avail = (s: AvailabilitySnapshot) => ({ availabilitySnapshot: async () => s });
const change = (s: ChangeMgmtSnapshot) => ({ changeMgmtSnapshot: async () => s });
const vuln = (a: VulnScanArtifact) => ({ vulnScanArtifact: () => a });
const cover = (s: AuditCoverageSnapshot) => ({ auditCoverageSnapshot: async () => s });
const backup = (s: BackupVerifiedSnapshot) => ({ backupVerifiedSnapshot: async () => s });
const strongAuth = (s: StrongAuthSnapshot) => ({ strongAuthSnapshot: async () => s });

describe("compliance-checks: availability_monitoring (paired-proof)", () => {
  test("NOT_ASSESSED — no health-check records (fail-closed, never fabricated PASS)", async () => {
    const r = await runCheck(CHECK_IDS.AVAILABILITY_MONITORING, avail({ monitored: false, components: [], unhealthy: [] }));
    assert.equal(r.status, "NOT_ASSESSED");
  });
  test("PASS — monitored and every component's latest status healthy", async () => {
    const r = await runCheck(CHECK_IDS.AVAILABILITY_MONITORING, avail({
      monitored: true, components: [{ name: "cbs", status: "healthy", lastCheckedAt: new Date() }], unhealthy: [],
    }));
    assert.equal(r.status, "PASS");
  });
  test("FAIL — a component's latest status is not healthy", async () => {
    const r = await runCheck(CHECK_IDS.AVAILABILITY_MONITORING, avail({
      monitored: true, components: [{ name: "cbs", status: "down", lastCheckedAt: new Date() }], unhealthy: ["cbs"],
    }));
    assert.equal(r.status, "FAIL");
    assert.deepEqual((r.evidence as { unhealthy: string[] }).unhealthy, ["cbs"]);
  });
});

describe("compliance-checks: change_management (paired-proof)", () => {
  test("NOT_ASSESSED — no change records (fail-closed)", async () => {
    const r = await runCheck(CHECK_IDS.CHANGE_MANAGEMENT, change({ total: 0, implemented: 0, implementedWithoutApproval: [] }));
    assert.equal(r.status, "NOT_ASSESSED");
  });
  test("PASS — every implemented change has a recorded approver", async () => {
    const r = await runCheck(CHECK_IDS.CHANGE_MANAGEMENT, change({ total: 3, implemented: 2, implementedWithoutApproval: [] }));
    assert.equal(r.status, "PASS");
  });
  test("FAIL — an implemented change reached production without a recorded approver", async () => {
    const r = await runCheck(CHECK_IDS.CHANGE_MANAGEMENT, change({ total: 3, implemented: 2, implementedWithoutApproval: ["CHG-2"] }));
    assert.equal(r.status, "FAIL");
    assert.ok((r.evidence as { implementedWithoutApproval: string[] }).implementedWithoutApproval.includes("CHG-2"));
  });
});

describe("compliance-checks: vulnerability_scan (baked build-time artifact, fail-closed)", () => {
  const fresh = new Date().toISOString();
  test("NOT_ASSESSED — no baked artifact present (fail-closed, never fabricated PASS)", async () => {
    const r = await runCheck(CHECK_IDS.VULNERABILITY_SCAN, vuln({ present: false, available: false, capturedAt: null, staleDays: null, vulnerabilities: null }));
    assert.equal(r.status, "NOT_ASSESSED");
  });
  test("NOT_ASSESSED — scan could not run at build (available:false)", async () => {
    const r = await runCheck(CHECK_IDS.VULNERABILITY_SCAN, vuln({ present: true, available: false, capturedAt: fresh, staleDays: 0, vulnerabilities: null }));
    assert.equal(r.status, "NOT_ASSESSED");
  });
  test("NOT_ASSESSED — artifact too stale (staleness gate; an old baked verdict is not a current PASS)", async () => {
    const r = await runCheck(CHECK_IDS.VULNERABILITY_SCAN, vuln({ present: true, available: true, capturedAt: "2020-01-01T00:00:00.000Z", staleDays: 999, vulnerabilities: { high: 0, critical: 0, moderate: 0, low: 0, total: 0 } }));
    assert.equal(r.status, "NOT_ASSESSED");
  });
  test("PASS — fresh scan, 0 high/critical advisories (the current real state)", async () => {
    const r = await runCheck(CHECK_IDS.VULNERABILITY_SCAN, vuln({ present: true, available: true, capturedAt: fresh, staleDays: 1, vulnerabilities: { info: 0, low: 2, moderate: 1, high: 0, critical: 0, total: 3 } }));
    assert.equal(r.status, "PASS");
  });
  test("FAIL — fresh scan with high/critical advisories", async () => {
    const r = await runCheck(CHECK_IDS.VULNERABILITY_SCAN, vuln({ present: true, available: true, capturedAt: fresh, staleDays: 1, vulnerabilities: { info: 0, low: 0, moderate: 0, high: 2, critical: 1, total: 3 } }));
    assert.equal(r.status, "FAIL");
    assert.match(r.detail, /high\/critical/);
  });
});

describe("compliance-checks: audit_log_coverage (reconciliation-derived, fail-closed)", () => {
  test("NOT_ASSESSED — chain not active for audit_logs (no watermark; never fabricated PASS)", async () => {
    const r = await runCheck(CHECK_IDS.AUDIT_LOG_COVERAGE, cover({
      active: false, chainWatermark: null, auditLogCount: 10, chainEntryCount: 0,
      chainedSinceWatermark: 0, unchainedSinceWatermark: 0, unchainedSample: [],
    }));
    assert.equal(r.status, "NOT_ASSESSED");
  });
  test("PASS — every audit event since the cutover is chained (0 gap)", async () => {
    const r = await runCheck(CHECK_IDS.AUDIT_LOG_COVERAGE, cover({
      active: true, chainWatermark: "2026-06-01T00:00:00.000Z", auditLogCount: 100, chainEntryCount: 100,
      chainedSinceWatermark: 80, unchainedSinceWatermark: 0, unchainedSample: [],
    }));
    assert.equal(r.status, "PASS");
  });
  test("FAIL — a coverage gap exists (unchainedSinceWatermark > 0)", async () => {
    const r = await runCheck(CHECK_IDS.AUDIT_LOG_COVERAGE, cover({
      active: true, chainWatermark: "2026-06-01T00:00:00.000Z", auditLogCount: 100, chainEntryCount: 95,
      chainedSinceWatermark: 75, unchainedSinceWatermark: 5,
      unchainedSample: [{ id: "a1", action: "LOGIN", timestamp: "2026-06-02T00:00:00.000Z" }],
    }));
    assert.equal(r.status, "FAIL");
    assert.equal((r.evidence as { unchainedSinceWatermark: number }).unchainedSinceWatermark, 5);
  });
});

describe("compliance-checks: backup_verified (backup_records-derived, fail-closed)", () => {
  test("NOT_ASSESSED — no backup records (fail-closed, never fabricated PASS)", async () => {
    const r = await runCheck(CHECK_IDS.BACKUP_VERIFIED, backup({ hasRecords: false, latestStatus: null, latestCompleted: null }));
    assert.equal(r.status, "NOT_ASSESSED");
  });
  test("PASS — recent completed backup with a checksum", async () => {
    const r = await runCheck(CHECK_IDS.BACKUP_VERIFIED, backup({
      hasRecords: true, latestStatus: "completed",
      latestCompleted: { id: "bk-1", checksum: "deadbeefdeadbeef", completedAt: new Date(), ageDays: 1 },
    }));
    assert.equal(r.status, "PASS");
  });
  test("FAIL — latest backup failed and no completed backup exists", async () => {
    const r = await runCheck(CHECK_IDS.BACKUP_VERIFIED, backup({ hasRecords: true, latestStatus: "failed", latestCompleted: null }));
    assert.equal(r.status, "FAIL");
  });
  test("FAIL — most recent completed backup has no checksum (unverifiable)", async () => {
    const r = await runCheck(CHECK_IDS.BACKUP_VERIFIED, backup({
      hasRecords: true, latestStatus: "completed",
      latestCompleted: { id: "bk-2", checksum: null, completedAt: new Date(), ageDays: 0 },
    }));
    assert.equal(r.status, "FAIL");
  });
  test("FAIL — most recent completed+checksummed backup is stale (beyond freshness window)", async () => {
    const r = await runCheck(CHECK_IDS.BACKUP_VERIFIED, backup({
      hasRecords: true, latestStatus: "completed",
      latestCompleted: { id: "bk-3", checksum: "deadbeefdeadbeef", completedAt: new Date(0), ageDays: 999 },
    }));
    assert.equal(r.status, "FAIL");
  });
});

describe("compliance-checks: account_lockout_enforced (config + totp_secrets, honest scope)", () => {
  test("NOT_ASSESSED — lockout config not inspectable", async () => {
    const r = await runCheck(CHECK_IDS.ACCOUNT_LOCKOUT_ENFORCED, strongAuth({
      lockoutConfigReadable: false, lockoutEnforced: false, maxAttempts: null, lockoutDurationMs: null, strongAuthEnabledCount: 0,
    }));
    assert.equal(r.status, "NOT_ASSESSED");
  });
  test("FAIL — lockout NOT enforced", async () => {
    const r = await runCheck(CHECK_IDS.ACCOUNT_LOCKOUT_ENFORCED, strongAuth({
      lockoutConfigReadable: true, lockoutEnforced: false, maxAttempts: 0, lockoutDurationMs: 0, strongAuthEnabledCount: 3,
    }));
    assert.equal(r.status, "FAIL");
  });
  test("FAIL — lockout enforced but NO strong-auth enrolled (honest, not softened to PASS)", async () => {
    const r = await runCheck(CHECK_IDS.ACCOUNT_LOCKOUT_ENFORCED, strongAuth({
      lockoutConfigReadable: true, lockoutEnforced: true, maxAttempts: 5, lockoutDurationMs: 900000, strongAuthEnabledCount: 0,
    }));
    assert.equal(r.status, "FAIL");
  });
  test("PASS — lockout enforced AND >=1 strong-auth enrolment enabled", async () => {
    const r = await runCheck(CHECK_IDS.ACCOUNT_LOCKOUT_ENFORCED, strongAuth({
      lockoutConfigReadable: true, lockoutEnforced: true, maxAttempts: 5, lockoutDurationMs: 900000, strongAuthEnabledCount: 2,
    }));
    assert.equal(r.status, "PASS");
    assert.match(r.detail, /opt-in/);
  });
});

describe("compliance-checks: real-DB derivation (default snapshots read live rows)", () => {
  afterEach(async () => {
    await db.delete(healthChecks);
    await db.delete(changeRequests);
  });

  test("availability default snapshot uses the LATEST status per component", async () => {
    await db.delete(healthChecks);
    await db.insert(healthChecks).values([
      { componentName: "cbs", status: "down", lastCheckedAt: new Date(Date.now() - 60_000) }, // older
      { componentName: "cbs", status: "healthy", lastCheckedAt: new Date() }, // latest → healthy
      { componentName: "kyt", status: "healthy", lastCheckedAt: new Date() },
    ]);
    const r = await runCheck(CHECK_IDS.AVAILABILITY_MONITORING, {}); // default deps → real DB
    assert.equal(r.status, "PASS", "latest-per-component all healthy ⇒ PASS");
  });

  test("change-mgmt default snapshot FAILS on a real implemented-without-approval row", async () => {
    await db.delete(changeRequests);
    await db.insert(changeRequests).values([
      { changeId: "CHG-OK", title: "approved change", changeType: "config", status: "implemented", requestedBy: "u1", approvedBy: "mgr", implementedBy: "u2", implementedAt: new Date() },
      { changeId: "CHG-BAD", title: "unapproved change", changeType: "config", status: "implemented", requestedBy: "u3", implementedAt: new Date() }, // no approvedBy
    ]);
    const r = await runCheck(CHECK_IDS.CHANGE_MANAGEMENT, {}); // default deps → real DB
    assert.equal(r.status, "FAIL");
    assert.ok((r.evidence as { implementedWithoutApproval: string[] }).implementedWithoutApproval.includes("CHG-BAD"));
  });
});
