// engine/autopilot/audit-logger.ts — SPEC-468: Persistent audit log for autopilot cascade actions
import { appendFile, readFile, mkdir, writeFile } from 'node:fs/promises';
import { join, dirname } from 'node:path';
import { hashProjectPath } from '../../storage/base-store.js';
const MAX_LINES = 1000;
/** Fire-and-forget write. Never blocks. */
export function writeAuditEntry(projectPath, entry) {
    void (async () => {
        try {
            const logPath = resolveLogPath(projectPath);
            await mkdir(dirname(logPath), { recursive: true });
            await appendFile(logPath, JSON.stringify(entry) + '\n', 'utf-8');
            await pruneIfNeeded(logPath);
        }
        catch {
            /* best-effort */
        }
    })();
}
/** Read and filter audit log. */
export async function readAuditLog(projectPath, filter = {}) {
    const logPath = resolveLogPath(projectPath);
    let raw;
    try {
        raw = await readFile(logPath, 'utf-8');
    }
    catch {
        return [];
    }
    const entries = [];
    for (const line of raw.split('\n')) {
        const trimmed = line.trim();
        if (!trimmed) {
            continue;
        }
        try {
            entries.push(JSON.parse(trimmed));
        }
        catch {
            /* skip corrupt lines */
        }
    }
    return applyFilter(entries, filter);
}
/** Helper for instrumenting cascade actions with timing. */
export async function withAudit(projectPath, trigger, action, fn, metadataExtractor) {
    const start = Date.now();
    try {
        const result = await fn();
        writeAuditEntry(projectPath, {
            ts: new Date().toISOString(),
            trigger,
            action,
            status: 'ok',
            durationMs: Date.now() - start,
            metadata: metadataExtractor ? metadataExtractor(result) : undefined,
        });
        return result;
    }
    catch (err) {
        writeAuditEntry(projectPath, {
            ts: new Date().toISOString(),
            trigger,
            action,
            status: 'fail',
            durationMs: Date.now() - start,
            error: err instanceof Error ? err.message : String(err),
        });
        throw err;
    }
}
// ---------------------------------------------------------------------------
// Private helpers
// ---------------------------------------------------------------------------
function resolveLogPath(projectPath) {
    const projectId = hashProjectPath(projectPath);
    return join(getDataDir(), projectId, 'autopilot-audit.jsonl');
}
function getDataDir() {
    return join(process.cwd(), 'data');
}
function applyFilter(entries, filter) {
    let result = entries;
    if (filter.trigger !== undefined) {
        result = result.filter((e) => e.trigger === filter.trigger);
    }
    if (filter.action !== undefined) {
        result = result.filter((e) => e.action === filter.action);
    }
    if (filter.status !== undefined) {
        result = result.filter((e) => e.status === filter.status);
    }
    if (filter.since !== undefined) {
        const since = filter.since;
        result = result.filter((e) => e.ts >= since);
    }
    const limit = filter.limit ?? 100;
    return result.slice(-limit);
}
async function pruneIfNeeded(logPath) {
    try {
        const raw = await readFile(logPath, 'utf-8');
        const lines = raw.split('\n').filter((l) => l.trim().length > 0);
        if (lines.length > MAX_LINES) {
            const pruned = lines.slice(-MAX_LINES).join('\n') + '\n';
            await writeFile(logPath, pruned, 'utf-8');
        }
    }
    catch {
        /* best-effort — don't fail on prune error */
    }
}
//# sourceMappingURL=audit-logger.js.map