// engine/runtime-security/audit-logger.ts — SPEC-084 AC-07
// Append-only JSON audit log with automatic rotation.
import { createHash } from 'node:crypto';
import { readFileSync, writeFileSync, existsSync, mkdirSync, renameSync, statSync } from 'node:fs';
import { resolve, dirname } from 'node:path';
const MAX_AUDIT_SIZE = 5 * 1024 * 1024; // 5MB
function hashInputs(inputs) {
    const json = JSON.stringify(inputs, Object.keys(inputs).sort());
    return createHash('sha256').update(json).digest('hex');
}
function getAuditPath(projectPath) {
    return resolve(projectPath, 'audit.json');
}
function ensureDir(filePath) {
    const dir = dirname(filePath);
    if (!existsSync(dir)) {
        mkdirSync(dir, { recursive: true });
    }
}
function rotateIfNeeded(auditPath) {
    if (!existsSync(auditPath)) {
        return;
    }
    try {
        const stats = statSync(auditPath);
        if (stats.size >= MAX_AUDIT_SIZE) {
            const dateStr = new Date().toISOString().slice(0, 10);
            const rotatedPath = auditPath.replace('.json', `-${dateStr}.json`);
            renameSync(auditPath, rotatedPath);
        }
    }
    catch {
        // Ignore rotation errors
    }
}
function readAuditLog(auditPath) {
    if (!existsSync(auditPath)) {
        return [];
    }
    try {
        const raw = readFileSync(auditPath, 'utf-8');
        return JSON.parse(raw);
    }
    catch {
        return [];
    }
}
function writeAuditLog(auditPath, entries) {
    ensureDir(auditPath);
    writeFileSync(auditPath, JSON.stringify(entries, null, 2), 'utf-8');
}
export function logAuditEntry(projectPath, toolName, inputs, threats, action, userId) {
    const auditPath = getAuditPath(projectPath);
    rotateIfNeeded(auditPath);
    const maxSeverity = threats.length > 0
        ? threats.reduce((max, t) => {
            const order = { critical: 3, warning: 2, info: 1 };
            return order[t.severity] > order[max] ? t.severity : max;
        }, 'info')
        : null;
    const entry = {
        timestamp: new Date().toISOString(),
        toolName,
        inputsHash: hashInputs(inputs),
        threatDetected: threats.length > 0,
        severity: maxSeverity,
        action,
        threats: [...new Set(threats.map((t) => t.type))],
        ...(userId ? { userId } : {}),
    };
    const entries = readAuditLog(auditPath);
    entries.push(entry);
    writeAuditLog(auditPath, entries);
}
function filterByTimeRange(entries, timeRange) {
    if (timeRange === 'all') {
        return entries;
    }
    const now = Date.now();
    const ranges = {
        last24h: 24 * 60 * 60 * 1000,
        last7d: 7 * 24 * 60 * 60 * 1000,
        last30d: 30 * 24 * 60 * 60 * 1000,
    };
    const cutoff = ranges[timeRange];
    if (!cutoff) {
        return entries;
    }
    return entries.filter((e) => now - new Date(e.timestamp).getTime() < cutoff);
}
export function getAuditSummary(projectPath, options) {
    const auditPath = getAuditPath(projectPath);
    const allEntries = readAuditLog(auditPath);
    const timeRange = options.timeRange ?? 'all';
    const entries = filterByTimeRange(allEntries, timeRange);
    const threatsByType = {};
    const toolThreats = new Map();
    for (const entry of entries) {
        if (entry.threatDetected) {
            for (const t of entry.threats) {
                threatsByType[t] = (threatsByType[t] ?? 0) + 1;
            }
            toolThreats.set(entry.toolName, (toolThreats.get(entry.toolName) ?? 0) + 1);
        }
    }
    const topToolsByThreat = [...toolThreats.entries()]
        .sort((a, b) => b[1] - a[1])
        .slice(0, 5)
        .map(([tool, count]) => ({ tool, count }));
    return {
        totalCalls: entries.length,
        threatsDetected: entries.filter((e) => e.threatDetected).length,
        blockedCalls: entries.filter((e) => e.action === 'blocked').length,
        threatsByType,
        topToolsByThreat,
        securityLevel: options.securityLevel ?? 'standard',
        timeRange,
    };
}
//# sourceMappingURL=audit-logger.js.map