/**
 * @fileoverview Append-only JSONL audit log for session lifecycle events.
 *
 * Records every session creation, start, exit, deletion, recovery, and server
 * start/stop to ~/.codeman/session-lifecycle.jsonl. Survives server restarts
 * (unlike RunSummary which is in-memory only).
 *
 * @module session-lifecycle-log
 */
import { appendFile, readFile, writeFile } from 'node:fs/promises';
import { existsSync, mkdirSync } from 'node:fs';
import { dirname } from 'node:path';
import { dataPath } from './config/instance.js';
const MAX_LINES = 10_000;
const TRIM_TO = 8_000;
export class SessionLifecycleLog {
    filePath;
    writeQueue = Promise.resolve();
    constructor(filePath) {
        this.filePath = filePath || dataPath('session-lifecycle.jsonl');
        const dir = dirname(this.filePath);
        if (!existsSync(dir)) {
            mkdirSync(dir, { recursive: true, mode: 0o700 });
        }
    }
    /**
     * Append a lifecycle event. Fire-and-forget — errors are logged but never thrown.
     */
    log(entry) {
        const line = JSON.stringify({ ts: Date.now(), ...entry }) + '\n';
        // Chain writes to prevent interleaving
        this.writeQueue = this.writeQueue
            .then(() => appendFile(this.filePath, line, 'utf-8'))
            .catch((err) => {
            console.error('[LifecycleLog] Failed to write:', err);
        });
    }
    /**
     * Query the log file with optional filters.
     */
    async query(opts) {
        const limit = opts?.limit ?? 200;
        let raw;
        try {
            raw = await readFile(this.filePath, 'utf-8');
        }
        catch (err) {
            if (err.code === 'ENOENT')
                return [];
            throw err;
        }
        const lines = raw.trim().split('\n').filter(Boolean);
        const entries = [];
        // Parse in reverse (newest first) for efficiency with limit
        for (let i = lines.length - 1; i >= 0 && entries.length < limit; i--) {
            try {
                const entry = JSON.parse(lines[i]);
                if (opts?.sessionId && entry.sessionId !== opts.sessionId)
                    continue;
                if (opts?.event && entry.event !== opts.event)
                    continue;
                if (opts?.since && entry.ts < opts.since)
                    continue;
                entries.push(entry);
            }
            catch {
                // Skip malformed lines
            }
        }
        return entries;
    }
    /**
     * Trim the log file if it exceeds MAX_LINES. Called on server start.
     */
    async trimIfNeeded() {
        let raw;
        try {
            raw = await readFile(this.filePath, 'utf-8');
        }
        catch (err) {
            if (err.code === 'ENOENT')
                return;
            throw err;
        }
        const lines = raw.trim().split('\n').filter(Boolean);
        if (lines.length <= MAX_LINES)
            return;
        const trimmed = lines.slice(-TRIM_TO);
        await writeFile(this.filePath, trimmed.join('\n') + '\n', 'utf-8');
        console.log(`[LifecycleLog] Trimmed from ${lines.length} to ${trimmed.length} entries`);
    }
}
// Singleton
let instance = null;
export function getLifecycleLog(filePath) {
    if (!instance) {
        instance = new SessionLifecycleLog(filePath);
    }
    return instance;
}
//# sourceMappingURL=session-lifecycle-log.js.map