/**
 * NERF Event Journal — structured JSONL event log for auto-mode iterations.
 *
 * Writes daily-rotated JSONL files to `.nerf/journal/YYYY-MM-DD.jsonl`.
 * Zero imports from `auto/` — depends only on node:fs, node:path, and paths.ts.
 *
 * Observability:
 * - Each line in the JSONL file is a self-contained JournalEntry
 * - Events are grouped by flowId (one per iteration) with monotonic seq numbers
 * - causedBy references enable causal chain reconstruction
 * - queryJournal() enables programmatic filtering by flowId, eventType, unitId, time range
 * - Silent failure: journal writes never throw — absence of events is the failure signal
 */
import { appendFileSync, mkdirSync, readdirSync, readFileSync } from "node:fs";
import { join } from "node:path";
import { nerfRoot } from "./paths.js";
import { nativeQueryJsonlFiles } from "./native-parser-bridge.js";
// ─── Emit ─────────────────────────────────────────────────────────────────────
/**
 * Append a journal event to the daily JSONL file.
 *
 * File path: `<nerfRoot>/journal/<YYYY-MM-DD>.jsonl`
 * where the date is extracted from `entry.ts.slice(0, 10)`.
 *
 * Never throws — all errors are silently caught.
 */
export function emitJournalEvent(basePath, entry) {
    try {
        const journalDir = join(nerfRoot(basePath), "journal");
        mkdirSync(journalDir, { recursive: true });
        const dateStr = entry.ts.slice(0, 10);
        const filePath = join(journalDir, `${dateStr}.jsonl`);
        appendFileSync(filePath, JSON.stringify(entry) + "\n");
    }
    catch {
        // Silent failure — journal must never break auto-mode
    }
}
// ─── Query ────────────────────────────────────────────────────────────────────
/**
 * Read and filter journal entries from all daily JSONL files.
 *
 * Returns an empty array on any error (missing directory, corrupt files, etc.).
 */
export function queryJournal(basePath, filters) {
    try {
        const journalDir = join(nerfRoot(basePath), "journal");
        // Try native Rust path first — reads and filters all JSONL files in one call
        const nativeFilters = {};
        if (filters) {
            if (filters.flowId)
                nativeFilters.flowId = filters.flowId;
            if (filters.eventType)
                nativeFilters.eventType = filters.eventType;
            if (filters.rule)
                nativeFilters.rule = filters.rule;
            if (filters.unitId)
                nativeFilters.unitId = filters.unitId;
            if (filters.after)
                nativeFilters.after = filters.after;
            if (filters.before)
                nativeFilters.before = filters.before;
        }
        const nativeResult = nativeQueryJsonlFiles(journalDir, nativeFilters);
        if (nativeResult !== null) {
            return nativeResult;
        }
        // JS fallback — read files, apply filters inline, early-exit on limit
        // M1: filter inside the read loop instead of load-all-then-filter
        const files = readdirSync(journalDir).filter(f => f.endsWith(".jsonl")).sort();
        const limit = filters?.limit ?? Infinity;
        const entries = [];
        outer: for (const file of files) {
            const raw = readFileSync(join(journalDir, file), "utf-8");
            for (const line of raw.split("\n")) {
                if (!line.trim())
                    continue;
                try {
                    const entry = JSON.parse(line);
                    if (filters) {
                        if (filters.flowId && entry.flowId !== filters.flowId)
                            continue;
                        if (filters.eventType && entry.eventType !== filters.eventType)
                            continue;
                        if (filters.rule && entry.rule !== filters.rule)
                            continue;
                        if (filters.unitId && entry.data?.unitId !== filters.unitId)
                            continue;
                        if (filters.after && entry.ts < filters.after)
                            continue;
                        if (filters.before && entry.ts > filters.before)
                            continue;
                    }
                    entries.push(entry);
                    if (entries.length >= limit)
                        break outer;
                }
                catch {
                    // Skip malformed lines
                }
            }
        }
        return entries;
    }
    catch {
        // Missing directory, permission errors, etc. — return empty
        return [];
    }
}
