/**
 * olam memory stats — operator-facing formatter over `/bridge/stats`.
 *
 *   $ olam memory stats
 *     # human-readable counters + derived cache_error_rate
 *
 *   $ olam memory stats --json
 *     # raw /bridge/stats response (for jq / monitoring scripts)
 *
 * Config (env vars):
 *   AGENTMEMORY_BRIDGE_URL    bridge base URL
 *                             (default: https://olam-agent-memory.ernestcodes.workers.dev)
 *   AGENTMEMORY_BRIDGE_SECRET bearer token
 *
 * Derived metric: cache_error_rate =
 *     (cache_busted_hash + cache_busted_version) / max(writes_observed, 1)
 *
 *   Interpretation: high rates (>5%) mean operators have been forced to
 *   reclassify a lot of recent writes — usually a sign that the prompt
 *   template / classifier_version is churning. The denominator is
 *   classifier_writes_observed (NOT bridge writes_observed).
 *
 * Plan reference:
 *   docs/plans/agentmemory-classifier-and-regret/phase-b-tasks.md (B5/B6)
 */
import { printError, printHeader, printInfo } from '../../output.js';
const DEFAULT_BRIDGE_URL = 'https://olam-agent-memory.ernestcodes.workers.dev';
const STATS_TIMEOUT_MS = 10_000;
/**
 * Pure helper — separated so it's unit-testable without HTTP.
 * Guards divide-by-zero (returns 0 when writes_observed === 0, even
 * if busts are nonzero — Infinity / NaN would be operator-hostile).
 */
export function computeCacheErrorRate(counters) {
    if (counters.writes_observed <= 0)
        return 0;
    const busted = counters.cache_busted_hash + counters.cache_busted_version;
    return busted / counters.writes_observed;
}
function resolveBridgeUrl(opts) {
    return opts.bridgeUrl
        ?? process.env['AGENTMEMORY_BRIDGE_URL']
        ?? DEFAULT_BRIDGE_URL;
}
function resolveBearer(opts) {
    const fromOpt = opts.bearer;
    if (fromOpt && fromOpt.length > 0)
        return fromOpt;
    const fromEnv = process.env['AGENTMEMORY_BRIDGE_SECRET'];
    if (fromEnv && fromEnv.length > 0)
        return fromEnv;
    return null;
}
function intOrZero(v) {
    if (typeof v !== 'number' || !Number.isFinite(v))
        return 0;
    return Math.trunc(v);
}
function extractCounters(stats) {
    return {
        writes_observed: intOrZero(stats.classifier_writes_observed),
        writes_deduped_hash: intOrZero(stats.classifier_writes_deduped_hash),
        writes_deduped_semantic: intOrZero(stats.classifier_writes_deduped_semantic),
        recalls_observed: intOrZero(stats.classifier_recalls_observed),
        cache_busted_hash: intOrZero(stats.classifier_cache_busted_hash),
        cache_busted_version: intOrZero(stats.classifier_cache_busted_version),
    };
}
/**
 * Run the stats call. Pure-ish: takes opts + fetch (so tests can stub) +
 * returns a structured result. Mirrors `runReclassify` for symmetry.
 */
export async function runStats(opts, fetchImpl = fetch) {
    const bridgeUrl = resolveBridgeUrl(opts);
    const bearer = resolveBearer(opts);
    if (!bearer) {
        return {
            ok: false,
            error: 'AGENTMEMORY_BRIDGE_SECRET not set (or --bearer not passed)',
        };
    }
    try {
        const res = await fetchImpl(`${bridgeUrl}/bridge/stats`, {
            method: 'GET',
            headers: {
                authorization: `Bearer ${bearer}`,
            },
            signal: AbortSignal.timeout(STATS_TIMEOUT_MS),
        });
        if (!res.ok) {
            const detail = await res.text().catch(() => '');
            return {
                ok: false,
                error: `bridge returned ${res.status} ${res.statusText}${detail ? `: ${detail.slice(0, 200)}` : ''}`,
            };
        }
        const payload = (await res.json());
        if (!payload || typeof payload !== 'object' || !payload.stats || typeof payload.stats !== 'object') {
            return {
                ok: false,
                error: 'bridge response missing stats envelope',
            };
        }
        const counters = extractCounters(payload.stats);
        const cacheErrorRate = computeCacheErrorRate(counters);
        return {
            ok: true,
            counters,
            cacheErrorRate,
            raw: payload,
        };
    }
    catch (err) {
        return {
            ok: false,
            error: err instanceof Error ? err.message : String(err),
        };
    }
}
function formatPct(numerator, denominator) {
    if (denominator <= 0)
        return '0.0%';
    return `${((numerator / denominator) * 100).toFixed(1)}%`;
}
/** Run + render. Returns the process exit code. */
export async function runStatsCli(opts) {
    const result = await runStats(opts);
    if (opts.json) {
        // Preserve the raw bridge envelope when possible so jq pipelines
        // see the same shape they'd get from curl. Fall back to the
        // structured result on error so callers can still inspect.
        const out = result.ok && result.raw !== undefined ? result.raw : result;
        process.stdout.write(JSON.stringify(out, null, 2) + '\n');
        return result.ok ? 0 : 1;
    }
    printHeader('olam memory stats — bridge counters');
    if (!result.ok) {
        printError(`stats failed: ${result.error ?? '(unknown error)'}`);
        return 1;
    }
    const c = result.counters;
    printInfo('writes_observed', String(c.writes_observed));
    printInfo('writes_deduped_hash', `${c.writes_deduped_hash}  (${formatPct(c.writes_deduped_hash, c.writes_observed)})`);
    printInfo('writes_deduped_semantic', `${c.writes_deduped_semantic}  (${formatPct(c.writes_deduped_semantic, c.writes_observed)})`);
    printInfo('recalls_observed', String(c.recalls_observed));
    printInfo('cache_busted_hash', String(c.cache_busted_hash));
    printInfo('cache_busted_version', String(c.cache_busted_version));
    printInfo('cache_error_rate', `${(result.cacheErrorRate * 100).toFixed(1)}%`);
    return 0;
}
export function registerMemoryStats(cmd) {
    cmd
        .command('stats')
        .description('Fetch /bridge/stats from the agent-memory bridge and format classifier counters (writes_observed, dedup rates, recalls, cache busts, derived cache_error_rate)')
        .option('--bridge-url <url>', 'Override AGENTMEMORY_BRIDGE_URL')
        .option('--bearer <token>', 'Override AGENTMEMORY_BRIDGE_SECRET')
        .option('--json', 'Machine-readable JSON output (raw bridge envelope)', false)
        .action(async (opts) => {
        const rc = await runStatsCli(opts);
        if (rc !== 0)
            process.exitCode = rc;
    });
}
//# sourceMappingURL=stats.js.map