/**
 * Security Verification Runner
 *
 *     npm run verify:security
 *
 * Runs every check for the two-factor authentication and backup-notification
 * features and reports a single pass/fail summary.
 *
 * These are plain tsx scripts rather than Jest specs because this workspace's
 * Jest setup names a `ts-jest` preset that is not installed, so `npm test` does
 * not currently run at all. Keeping the checks executable was worth more than
 * matching a harness that cannot execute; if Jest is repaired later, each file
 * here converts to a spec with little work.
 *
 * Nothing touches a real database or sends a real email — the model layer and
 * the mailer are stubbed in-process, so this is safe to run anywhere, including
 * CI and a developer machine pointed at production config.
 */

import path from 'path';
import { spawnSync } from 'child_process';

interface Suite {
  file: string;
  title: string;
}

const SUITES: Suite[] = [
  { file: 'totp.ts', title: 'TOTP (RFC 6238 test vectors, window, replay input handling)' },
  { file: 'primitives.ts', title: 'Secret box, recovery codes, challenge tokens' },
  { file: 'authGuard.ts', title: 'Challenge tokens cannot authenticate a request' },
  { file: 'twoFactorFlow.ts', title: 'End-to-end 2FA login, enrolment, devices, policy modes' },
  { file: 'verificationMethods.ts', title: 'Authenticator App / Email OTP / Both, and policy transitions' },
  { file: 'policyPrecedence.ts', title: 'Policy precedence — the Super Admin floor' },
  { file: 'orgAdminScope.ts', title: 'Org-admin scope isolation (no privilege escalation)' },
  { file: 'dynamicRoles.ts', title: 'Role targeting reads live RBAC data (ids, renames, scope)' },
  { file: 'roleSelector.ts', title: 'Policy role picker — one chip per role, no duplicates' },
  { file: 'settingsPersistence.ts', title: 'Settings save/reload round-trip on both model backends' },
  { file: 'backupNotifications.ts', title: 'Backup notification decisions, recipients, retry, logging' },
  { file: 'backupFileLifecycle.ts', title: 'Backup files survive a restart; orphan detection intact' },
  { file: 'restoreSessionIndependence.ts', title: 'Restore is session-independent; it depends on the ZIP, not the browser' },
];

let totalPassed = 0;
let totalFailed = 0;
const failedSuites: string[] = [];

for (const suite of SUITES) {
  console.log(`\n${'='.repeat(72)}\n${suite.title}\n${'='.repeat(72)}`);

  const result = spawnSync(
    process.execPath,
    [require.resolve('tsx/cli'), path.join(__dirname, suite.file)],
    { encoding: 'utf8', stdio: 'pipe' },
  );

  const output = `${result.stdout || ''}${result.stderr || ''}`;
  const passed = (output.match(/^PASS/gm) || []).length;
  const failed = (output.match(/^FAIL/gm) || []).length;

  totalPassed += passed;
  totalFailed += failed;

  // Only the failures are worth printing in full — a green suite just needs its count.
  if (failed > 0 || result.status !== 0) {
    console.log(output.trim());
    failedSuites.push(suite.file);
  } else {
    console.log(`  ${passed} checks passed.`);
  }
}

console.log(`\n${'='.repeat(72)}`);
console.log(`Total: ${totalPassed} passed, ${totalFailed} failed`);
if (failedSuites.length) {
  console.log(`Failing suites: ${failedSuites.join(', ')}`);
}
console.log('='.repeat(72));

process.exit(totalFailed > 0 || failedSuites.length > 0 ? 1 : 0);
