/**
 * perf-history.ts — Lightweight per-tool performance recorder.
 *
 * Appends { tool, ms, at } rows to ~/.nexus-prime/perf-history.jsonl.
 * Reads them back to compute p50 / p90 / p99 per tool for nexus_doctor.
 * File-backed so stats survive daemon restarts without a full SQLite migration.
 */
import { promises as fsPromises } from 'fs';
import * as path from 'path';
import { resolveNexusStateDir } from './runtime-registry.js';
/** Per-tool performance budgets (fail thresholds per plan spec). */
const PERF_BUDGETS = {
    'nexus_session_bootstrap': { warn: 30_000, fail: 45_000 },
    'nexus_optimize_tokens': { warn: 2_000, fail: 3_000 },
    'nexus_recall_memory': { warn: 200, fail: 300 },
    'nexus_store_memory': { warn: 300, fail: 500 },
    'nexus_orchestrate': { warn: 20_000, fail: 30_000 },
};
const MAX_ROWS_PER_TOOL = 200;
let _filePath = null;
function getFilePath() {
    if (!_filePath) {
        _filePath = path.join(resolveNexusStateDir(), 'perf-history.jsonl');
    }
    return _filePath;
}
/**
 * Append a single timing row (fire-and-forget — never throws).
 * Called from runHandler after each successful tool invocation.
 */
export function recordPerf(tool, ms) {
    const line = JSON.stringify({ tool, ms, at: Date.now() }) + '\n';
    fsPromises.appendFile(getFilePath(), line, 'utf8').catch(() => { });
}
/** Parse the JSONL file, returning rows in insertion order. */
async function readAllRows() {
    try {
        const text = await fsPromises.readFile(getFilePath(), 'utf8');
        return text
            .trim()
            .split('\n')
            .filter(Boolean)
            .map((l) => {
            try {
                return JSON.parse(l);
            }
            catch {
                return null;
            }
        })
            .filter((r) => r !== null &&
            typeof r.tool === 'string' &&
            typeof r.ms === 'number' &&
            typeof r.at === 'number');
    }
    catch {
        return [];
    }
}
function percentile(sorted, p) {
    if (sorted.length === 0)
        return 0;
    const idx = Math.min(Math.ceil((p / 100) * sorted.length) - 1, sorted.length - 1);
    return sorted[Math.max(0, idx)];
}
function buildStats(tool, allMs) {
    const sorted = allMs.slice(-MAX_ROWS_PER_TOOL).sort((a, b) => a - b);
    return {
        tool,
        count: sorted.length,
        p50: percentile(sorted, 50),
        p90: percentile(sorted, 90),
        p99: percentile(sorted, 99),
        budget: PERF_BUDGETS[tool],
    };
}
/** Compute p50/p90/p99 for a specific tool from recent history. */
export async function getPerfStats(tool) {
    const rows = await readAllRows();
    const ms = rows.filter((r) => r.tool === tool).map((r) => r.ms);
    return buildStats(tool, ms);
}
/** Get stats for every tool that has recorded data. */
export async function getAllPerfStats() {
    const rows = await readAllRows();
    const byTool = new Map();
    for (const r of rows) {
        const arr = byTool.get(r.tool) ?? [];
        arr.push(r.ms);
        byTool.set(r.tool, arr);
    }
    return Array.from(byTool.entries()).map(([tool, ms]) => buildStats(tool, ms));
}
