#!/usr/bin/env node
/**
 * check-disk-encryption.mjs — Boot-time disk-encryption health check (RD-2).
 *
 * Best-effort OS detection of full-disk encryption (FileVault / BitLocker / LUKS).
 * Always exits 0 — this is a health check, not a hard gate. An audit row is
 * written to audit_logs on each run so compliance reports reflect boot state.
 *
 * Usage:
 *   npm run check:disk-enc
 *   node --no-warnings scripts/check-disk-encryption.mjs
 */

import { execFileSync } from 'node:child_process';
import { readFileSync, readdirSync } from 'node:fs';

// ---------------------------------------------------------------------------
// Resolve DB handle without triggering seedDefaults (we only need appendAudit).
// Import getDb + appendAudit from runtime to keep the audit path consistent.
// ---------------------------------------------------------------------------

// Load env file if present (mirrors the daemon pattern).
const ENV_FILE = process.env.MADARX_ENV_FILE ||
  new URL('../.env.local', import.meta.url).pathname;
try {
  const content = readFileSync(ENV_FILE, 'utf8');
  for (const line of content.split('\n')) {
    const trimmed = line.trim();
    if (!trimmed || trimmed.startsWith('#')) continue;
    const eqIdx = trimmed.indexOf('=');
    if (eqIdx < 0) continue;
    const key = trimmed.slice(0, eqIdx).trim();
    const val = trimmed.slice(eqIdx + 1).trim().replace(/^["']|["']$/g, '');
    if (key && !(key in process.env)) process.env[key] = val;
  }
} catch {
  // .env.local absent — fine, env vars may already be set in the shell.
}

// ---------------------------------------------------------------------------
// OS detection helpers
// ---------------------------------------------------------------------------

function detectMacOS() {
  try {
    const out = execFileSync('fdesetup', ['status'], {
      stdio: ['ignore', 'pipe', 'ignore'],
      timeout: 5000,
    }).toString().trim();
    const on = /FileVault is On\./i.test(out);
    return {
      detected_state: on ? 'on' : 'off',
      tool: 'fdesetup',
      raw: out.slice(0, 200),
    };
  } catch (err) {
    return {
      detected_state: 'unknown',
      tool: 'fdesetup',
      error: err?.message?.slice(0, 100) || String(err),
    };
  }
}

function detectLinux() {
  // Strategy 1: check lsblk for 'crypt' type rows (LUKS-mapped devices).
  try {
    const lsblk = execFileSync('lsblk', ['-o', 'TYPE,MOUNTPOINT,FSTYPE', '--noheadings', '--raw'], {
      stdio: ['ignore', 'pipe', 'ignore'],
      timeout: 5000,
    }).toString();

    const hasCrypt = lsblk.split('\n').some(line => line.trim().startsWith('crypt'));
    if (hasCrypt) {
      return { detected_state: 'on', tool: 'lsblk', detail: 'crypt device found' };
    }
  } catch {
    // lsblk unavailable — fall through.
  }

  // Strategy 2: scan /sys/class/block/*/dm/uuid for LUKS prefix.
  try {
    const blockDevs = readdirSync('/sys/class/block', { withFileTypes: true });
    for (const dev of blockDevs) {
      try {
        const uuidPath = `/sys/class/block/${dev.name}/dm/uuid`;
        const uuid = readFileSync(uuidPath, 'utf8').trim();
        if (uuid.startsWith('CRYPT-LUKS')) {
          return { detected_state: 'on', tool: 'sysfs-dm-uuid', device: dev.name };
        }
      } catch { /* not all block devices have dm/uuid */ }
    }
  } catch {
    // /sys/class/block unavailable.
  }

  return {
    detected_state: 'off',
    tool: 'lsblk+sysfs',
    detail: 'no crypt/LUKS devices detected',
  };
}

function detectWindows() {
  try {
    const out = execFileSync('manage-bde', ['-status', 'C:'], {
      stdio: ['ignore', 'pipe', 'ignore'],
      timeout: 10000,
    }).toString();
    const on = /Protection\s+On/i.test(out);
    return {
      detected_state: on ? 'on' : 'off',
      tool: 'manage-bde',
      raw: out.slice(0, 200),
    };
  } catch (err) {
    return {
      detected_state: 'unknown',
      tool: 'manage-bde',
      error: err?.message?.slice(0, 100) || String(err),
    };
  }
}

function detectEncryption(os) {
  if (os === 'darwin') return detectMacOS();
  if (os === 'linux') return detectLinux();
  if (os === 'win32') return detectWindows();
  return { detected_state: 'unknown', detail: 'unsupported platform' };
}

// ---------------------------------------------------------------------------
// Main
// ---------------------------------------------------------------------------

async function main() {
  const os = process.platform;
  const result = detectEncryption(os);
  const { detected_state } = result;

  const statusLabel =
    detected_state === 'on' ? 'enabled' :
    detected_state === 'off' ? 'disabled' :
    'unknown';

  const summary = `Disk encryption check: ${statusLabel} (${os})`;
  const status = detected_state === 'on' ? 'ok' : detected_state === 'off' ? 'warning' : 'unknown';

  // Write audit row via runtime if available. If runtime fails to load (e.g.,
  // circular deps or missing env vars), fall back to a direct SQLite write.
  let auditWritten = false;
  try {
    const { appendAudit } = await import('../lib/runtime.js');
    appendAudit({
      type: 'disk_encryption.health',
      summary,
      status,
      meta: { os, detected_state, ...result },
    });
    auditWritten = true;
  } catch {
    // Fallback: write directly to SQLite without going through runtime.
    try {
      const { getDb, newId, nowIso } = await import('../lib/db.js');
      const db = getDb();
      const id = newId('audit');
      const at = nowIso();
      db.prepare(
        'INSERT INTO audit_logs (id, at, type, summary, status, meta) VALUES (?, ?, ?, ?, ?, ?)'
      ).run(
        id, at,
        'disk_encryption.health',
        summary,
        status,
        JSON.stringify({ os, detected_state, ...result })
      );
      auditWritten = true;
    } catch (dbErr) {
      // Non-fatal — health check must not block boot.
      console.error('[check-disk-enc] Could not write audit row:', dbErr?.message || dbErr);
    }
  }

  // Human-readable output.
  const icon = detected_state === 'on' ? '[OK]' : detected_state === 'off' ? '[WARN]' : '[?]';
  console.log(`${icon} ${summary}`);
  if (auditWritten) console.log('     Audit row written to audit_logs.');

  // Production warning when encryption is off.
  if (detected_state !== 'on' && process.env.NODE_ENV === 'production') {
    process.stderr.write(
      '\n[SECURITY WARNING] Full-disk encryption appears to be ' + statusLabel +
      ' on this host. In production, enable FileVault (macOS), BitLocker (Windows),' +
      ' or LUKS (Linux) to protect artifact data at rest.\n\n'
    );
  }

  // Always exit 0 — this is a health check, not a hard gate.
  process.exit(0);
}

main().catch(err => {
  console.error('[check-disk-enc] Unexpected error:', err?.message || err);
  process.exit(0); // still 0 — never block boot
});
