/**
 * substrate-audit-log.ts — `olam substrate audit-log` subcommand.
 *
 * Phase 1b C3 of olam-host-suite-phase-1b-k3s-beta-flavour (plan
 * ~/.claude/plans/olam-host-suite-phase-1b-k3s-beta-flavour.md).
 *
 * Decisions consumed:
 *   D19 — audit-log inspection subcommand ships in PR3 (not deferred).
 *
 * Reads `~/.olam/state/manifest-refresh-audit.jsonl` and renders entries.
 *
 * Subcommand: `olam substrate audit-log`
 *   (registered under the existing `substrate` parent command via registerSubstrate)
 *
 * Flags:
 *   --json               Emit raw JSONL on stdout (one entry per line).
 *   --since <ISO-date>   Filter entries with ts >= <ISO-date>.
 *   --limit <N>          Max entries (default 50; applied AFTER --since filter).
 *
 * No force-overwrite power — read-only. Absent log file prints "no entries"
 * without error (exit 0).
 */
import * as fs from 'node:fs';
import pc from 'picocolors';
import { MANIFEST_REFRESH_AUDIT_LOG } from '../lib/manifest-refresh.js';
import { PERIPHERALS } from '../lib/peripheral-registry.js';
/** Sorted list of valid peripheral names from the canonical registry. */
const VALID_PERIPHERAL_NAMES = PERIPHERALS.map((p) => p.name).sort();
const DEFAULT_LIMIT = 50;
/**
 * Parse the JSONL audit log file into entries. Skips malformed lines.
 */
function parseAuditLog(raw) {
    const lines = raw.split('\n').filter((l) => l.trim().length > 0);
    const entries = [];
    for (const line of lines) {
        try {
            const entry = JSON.parse(line);
            entries.push(entry);
        }
        catch {
            // Skip malformed lines silently.
        }
    }
    return entries;
}
/**
 * Format a single entry as a pretty-printed table row.
 *
 * Table columns: ts | security_regression | accepted | changed_fields
 */
function formatEntryRow(entry, index) {
    const ts = entry.ts ?? '(no timestamp)';
    const regression = entry.security_regression ? pc.red('yes') : pc.green('no');
    const accepted = entry.accepted ? pc.yellow('yes') : '—';
    const fields = (entry.changed_fields ?? []).join(', ') || '—';
    return `  ${String(index + 1).padStart(3)}  ${ts}  regression=${regression}  accepted=${accepted}  fields=[${fields}]`;
}
function formatHeader() {
    return (`  ${'#'.padStart(3)}  ${'timestamp'.padEnd(27)}  regression  accepted  fields\n` +
        `  ${'─'.repeat(3)}  ${'─'.repeat(27)}  ${'─'.repeat(10)}  ${'─'.repeat(8)}  ${'─'.repeat(20)}`);
}
/**
 * Implementation of `olam substrate audit-log`. Exported for testability.
 *
 * @returns process exit code (0 = success, 1 = error)
 */
export function handleAuditLog(opts, deps = {}) {
    const auditLogPath = deps.auditLogPath ?? MANIFEST_REFRESH_AUDIT_LOG;
    const stdout = deps.stdout ?? process.stdout;
    const stderr = deps.stderr ?? process.stderr;
    const readFileSyncImpl = deps.readFileSync ?? fs.readFileSync;
    const existsSyncImpl = deps.existsSync ?? fs.existsSync;
    const rawLimit = opts.limit;
    const limit = typeof rawLimit === 'number'
        ? rawLimit
        : typeof rawLimit === 'string'
            ? Number.parseInt(rawLimit, 10)
            : DEFAULT_LIMIT;
    const effectiveLimit = Number.isFinite(limit) && limit > 0 ? limit : DEFAULT_LIMIT;
    if (!existsSyncImpl(auditLogPath)) {
        stdout.write('no entries\n');
        return 0;
    }
    let raw;
    try {
        raw = readFileSyncImpl(auditLogPath, 'utf8');
    }
    catch (err) {
        stderr.write(`${pc.red('error:')} could not read audit log at ${auditLogPath}: ${err instanceof Error ? err.message : String(err)}\n`);
        return 1;
    }
    let entries = parseAuditLog(raw);
    // --peripheral filter (C4/D19): validate name against PERIPHERALS registry; reject unknown.
    if (opts.peripheral) {
        if (!VALID_PERIPHERAL_NAMES.includes(opts.peripheral)) {
            stderr.write(`${pc.red('error:')} unknown peripheral "${opts.peripheral}". Valid names: ${VALID_PERIPHERAL_NAMES.join(', ')}\n`);
            return 1;
        }
        entries = entries.filter((e) => e.peripheral === opts.peripheral);
    }
    // --since filter
    if (opts.since) {
        const sinceMs = Date.parse(opts.since);
        if (Number.isNaN(sinceMs)) {
            stderr.write(`${pc.red('error:')} invalid --since date "${opts.since}". Expected ISO-8601 (e.g. 2026-05-20T00:00:00Z).\n`);
            return 1;
        }
        entries = entries.filter((e) => {
            const ts = Date.parse(e.ts ?? '');
            return !Number.isNaN(ts) && ts >= sinceMs;
        });
    }
    // --limit (applied after filter, from the END of the list — most recent N)
    if (entries.length > effectiveLimit) {
        entries = entries.slice(-effectiveLimit);
    }
    if (entries.length === 0) {
        stdout.write('no entries\n');
        return 0;
    }
    if (opts.json) {
        for (const entry of entries) {
            stdout.write(JSON.stringify(entry) + '\n');
        }
        return 0;
    }
    // Pretty-printed table.
    stdout.write(formatHeader() + '\n');
    entries.forEach((entry, i) => {
        stdout.write(formatEntryRow(entry, i) + '\n');
    });
    stdout.write(`\n${entries.length} entr${entries.length === 1 ? 'y' : 'ies'} shown\n`);
    return 0;
}
/**
 * Register `olam substrate audit-log` under the `substrate` parent command.
 *
 * Called from registerSubstrate() in substrate.ts.
 */
export function registerSubstrateAuditLog(substrateCmdRef) {
    substrateCmdRef
        .command('audit-log')
        .description('Inspect the manifest-refresh audit log (~/.olam/state/manifest-refresh-audit.jsonl). ' +
        'Read-only. No force-overwrite power.')
        .option('--json', 'Emit raw JSONL on stdout instead of a pretty table')
        .option('--since <ISO-date>', 'Filter entries with timestamp >= <ISO-date>')
        .option('--limit <N>', `Max entries to show (default ${DEFAULT_LIMIT})`)
        .option('--peripheral <name>', 'Filter entries by peripheral name (C4/D19; e.g. auth-service)')
        .action((opts) => {
        const code = handleAuditLog({
            json: opts.json,
            since: opts.since,
            limit: opts.limit,
            peripheral: opts.peripheral,
        });
        if (code !== 0)
            process.exitCode = code;
    });
}
//# sourceMappingURL=substrate-audit-log.js.map