import * as fs from 'fs';
import * as path from 'path';
import { get4RunrConfigDir } from '@4runr/shared';
const FILE_NAME = 'sentinel-limits.json';
const ENV_FIELD_KEYS = [
    { field: 'enabled', envKeys: ['SENTINEL_ENABLED'] },
    { field: 'runMaxDurationMs', envKeys: ['RUN_MAX_DURATION_MS'] },
    { field: 'runMaxTokens', envKeys: ['RUN_MAX_TOKENS'] },
    { field: 'runIdleMs', envKeys: ['SENTINEL_IDLE_MS', 'RUN_IDLE_MS'] },
    { field: 'loopWindow', envKeys: ['SENTINEL_LOOP_WINDOW'] },
    { field: 'loopMax', envKeys: ['SENTINEL_LOOP_MAX'] },
    { field: 'runMaxCost', envKeys: ['RUN_MAX_COST', 'SENTINEL_MAX_COST'] },
];
export function getSentinelLimitsFilePath() {
    return path.join(get4RunrConfigDir(), FILE_NAME);
}
function envDefinesField(field) {
    const entry = ENV_FIELD_KEYS.find((e) => e.field === field);
    if (!entry)
        return false;
    return entry.envKeys.some((k) => {
        const v = process.env[k];
        return v !== undefined && v.trim() !== '';
    });
}
function parseSavedPayload(raw) {
    if (!raw || typeof raw !== 'object')
        return null;
    const o = raw;
    const num = (key) => {
        const v = o[key];
        if (typeof v === 'number' && Number.isFinite(v))
            return v;
        return undefined;
    };
    const enabled = o['enabled'];
    if (typeof enabled !== 'boolean')
        return null;
    const runMaxDurationMs = num('runMaxDurationMs');
    const runMaxTokens = num('runMaxTokens');
    const runIdleMs = num('runIdleMs');
    const loopWindow = num('loopWindow');
    const loopMax = num('loopMax');
    if (runMaxDurationMs === undefined ||
        runMaxTokens === undefined ||
        runIdleMs === undefined ||
        loopWindow === undefined ||
        loopMax === undefined) {
        return null;
    }
    const runMaxCost = num('runMaxCost');
    return {
        enabled,
        runMaxDurationMs,
        runMaxTokens,
        runIdleMs,
        loopWindow,
        loopMax,
        ...(runMaxCost !== undefined ? { runMaxCost } : {}),
    };
}
export function loadSentinelLimitsFromDisk() {
    const filePath = getSentinelLimitsFilePath();
    try {
        if (!fs.existsSync(filePath))
            return null;
        const text = fs.readFileSync(filePath, 'utf8');
        const parsed = JSON.parse(text);
        const inner = parsed && typeof parsed === 'object' && 'config' in parsed
            ? parsed.config
            : parsed;
        return parseSavedPayload(inner);
    }
    catch {
        return null;
    }
}
export function saveSentinelLimitsToDisk(config) {
    const filePath = getSentinelLimitsFilePath();
    const payload = {
        version: 1,
        updatedAt: new Date().toISOString(),
        config: {
            enabled: config.enabled,
            runMaxDurationMs: config.runMaxDurationMs,
            runMaxTokens: config.runMaxTokens,
            runIdleMs: config.runIdleMs,
            loopWindow: config.loopWindow,
            loopMax: config.loopMax,
            runMaxCost: config.runMaxCost ?? 1.0,
        },
    };
    fs.writeFileSync(filePath, JSON.stringify(payload, null, 2), { mode: 0o600 });
}
export function mergePersistedSentinelConfig(sentinel) {
    const saved = loadSentinelLimitsFromDisk();
    if (!saved)
        return false;
    const current = sentinel.getConfig();
    const merged = { ...current };
    for (const { field } of ENV_FIELD_KEYS) {
        if (envDefinesField(field))
            continue;
        merged[field] = saved[field];
    }
    try {
        sentinel.updateConfig(merged);
        return true;
    }
    catch {
        return false;
    }
}
export function applyPersistedSentinelConfig(sentinel, logger) {
    const filePath = getSentinelLimitsFilePath();
    const saved = loadSentinelLimitsFromDisk();
    if (!saved) {
        logger?.info('No Sentinel limits file found on disk (will use env defaults)', {
            expectedPath: filePath,
        });
        return false;
    }
    const envOverrides = [];
    const diskApplied = [];
    for (const { field, envKeys } of ENV_FIELD_KEYS) {
        if (envDefinesField(field)) {
            const activeEnv = envKeys.find(k => process.env[k] !== undefined && process.env[k]?.trim() !== '');
            if (activeEnv) {
                envOverrides.push(`${field} (via ${activeEnv}=${process.env[activeEnv]})`);
            }
        }
        else {
            diskApplied.push(`${field}=${saved[field]}`);
        }
    }
    const ok = mergePersistedSentinelConfig(sentinel);
    if (ok) {
        const finalConfig = sentinel.getConfig();
        logger?.info('✅ Sentinel limits loaded from disk and merged', {
            diskPath: filePath,
            diskValues: saved,
            envOverrides: envOverrides.length > 0 ? envOverrides : 'none',
            diskAppliedFields: diskApplied.length > 0 ? diskApplied : 'none (all env-overridden)',
            finalActiveConfig: finalConfig,
        });
    }
    else {
        logger?.warn('Failed to apply Sentinel limits from disk', {
            diskPath: filePath,
            savedConfig: saved,
        });
    }
    return ok;
}
export function hydrateSentinelConfigFromDisk(sentinel) {
    mergePersistedSentinelConfig(sentinel);
}
export function persistSentinelConfigAfterApply(sentinel, config, logger) {
    const filePath = getSentinelLimitsFilePath();
    saveSentinelLimitsToDisk(config);
    logger?.info('✅ Sentinel limits saved to disk', {
        path: filePath,
        config: config,
        note: 'Survives Gateway restart. Env vars in docker-compose override individual fields when set.',
    });
    return filePath;
}
//# sourceMappingURL=sentinel-config-store.js.map