// Unified configuration facade (minimal, focused on context budget for Phase 1)
import fs from 'fs';
import path from 'path';
import { resolveHostPaths } from './enhanced-path-resolver.js';
const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
const hasProviders = (value) => isRecord(value) && typeof value.providers === 'object' && value.providers !== null;
const readJsonSync = (file) => {
    try {
        return JSON.parse(fs.readFileSync(file, 'utf-8'));
    }
    catch {
        return null;
    }
};
const listMergedConfigs = (configDir) => {
    try {
        const files = fs
            .readdirSync(configDir)
            .filter((name) => /merged-config.*\.json$/i.test(name))
            .map((name) => path.join(configDir, name));
        files.sort((a, b) => {
            try {
                const sa = fs.statSync(a);
                const sb = fs.statSync(b);
                return (sb.mtimeMs || 0) - (sa.mtimeMs || 0);
            }
            catch {
                return 0;
            }
        });
        return files;
    }
    catch {
        return [];
    }
};
const toNumber = (value) => {
    const parsed = Number(value);
    return Number.isFinite(parsed) ? parsed : null;
};
const extractProviders = (cfg) => {
    if (!isRecord(cfg.virtualrouter) || !hasProviders(cfg.virtualrouter)) {
        return null;
    }
    return cfg.virtualrouter.providers ?? null;
};
const extractModulesConfig = (modulesRoot) => {
    if (!isRecord(modulesRoot.modules)) {
        return null;
    }
    const virtualRouterModule = modulesRoot.modules.virtualrouter;
    if (!isRecord(virtualRouterModule)) {
        return null;
    }
    const config = virtualRouterModule.config;
    return isRecord(config) ? config : null;
};
const readContextBudgetNode = (configNode) => {
    if (!configNode) {
        return { defaultBytes: null, safetyRatio: null };
    }
    const contextBudgetNode = configNode.contextBudget;
    if (!isRecord(contextBudgetNode)) {
        return { defaultBytes: null, safetyRatio: null };
    }
    const defaultBytes = toNumber(contextBudgetNode.defaultMaxContextBytes ?? contextBudgetNode.defaultMaxContext);
    const safetyRatio = toNumber(contextBudgetNode.safetyRatio);
    return { defaultBytes, safetyRatio };
};
function findMaxContextInConfig(cfg, modelId) {
    const providers = extractProviders(cfg);
    if (!providers) {
        return null;
    }
    for (const provider of Object.values(providers)) {
        if (!isRecord(provider.models)) {
            continue;
        }
        const models = provider.models;
        if (!Object.prototype.hasOwnProperty.call(models, modelId)) {
            continue;
        }
        const target = models[modelId];
        if (!target) {
            continue;
        }
        const maxContext = toNumber(target.maxContext);
        if (maxContext && maxContext > 0) {
            return maxContext;
        }
    }
    return null;
}
export class UnifiedConfigFacade {
    static _instance = null;
    static getInstance() {
        if (!this._instance) {
            this._instance = new UnifiedConfigFacade();
        }
        return this._instance;
    }
    watchers = [];
    fsWatcher = null;
    // Phase 1: only context budget API
    getContextBudgetForModel(modelId) {
        const envDirect = Number(process.env.ROUTECODEX_CONTEXT_BUDGET_BYTES || process.env.RCC_CONTEXT_BUDGET_BYTES || '');
        const envSafety = Number(process.env.RCC_CONTEXT_BUDGET_SAFETY || 'NaN');
        const safetyFromEnv = (Number.isFinite(envSafety) && envSafety >= 0 && envSafety < 1) ? envSafety : NaN;
        if (Number.isFinite(envDirect) && envDirect > 0) {
            const safety = Number.isFinite(safetyFromEnv) ? safetyFromEnv : 0.1;
            return { maxBytes: envDirect, safetyRatio: safety, allowedBytes: Math.floor(envDirect * (1 - safety)), source: 'env' };
        }
        const { configDir } = resolveHostPaths();
        // modules.json defaults
        let modulesDefault = null;
        let modulesSafety = null;
        try {
            const modules = readJsonSync(path.join(configDir, 'modules.json'));
            if (modules) {
                const modulesConfig = extractModulesConfig(modules);
                const { defaultBytes, safetyRatio } = readContextBudgetNode(modulesConfig);
                if (defaultBytes && defaultBytes > 0) {
                    modulesDefault = defaultBytes;
                }
                if (typeof safetyRatio === 'number' && safetyRatio >= 0 && safetyRatio < 1) {
                    modulesSafety = safetyRatio;
                }
            }
        }
        catch { /* ignore */ }
        // merged-config*.json
        try {
            const merged = listMergedConfigs(configDir);
            for (const f of merged) {
                const cfg = readJsonSync(f);
                if (!cfg) {
                    continue;
                }
                const v = findMaxContextInConfig(cfg, modelId);
                if (v && v > 0) {
                    const safety = Number.isFinite(safetyFromEnv) ? safetyFromEnv : (modulesSafety ?? 0.1);
                    return { maxBytes: v, safetyRatio: safety, allowedBytes: Math.floor(v * (1 - safety)), source: `merged:${path.basename(f)}` };
                }
            }
        }
        catch {
            /* ignore */
        }
        // config.json
        try {
            const cfg = readJsonSync(path.join(configDir, 'config.json'));
            if (cfg) {
                const v = findMaxContextInConfig(cfg, modelId);
                if (v && v > 0) {
                    const safety = Number.isFinite(safetyFromEnv) ? safetyFromEnv : (modulesSafety ?? 0.1);
                    return { maxBytes: v, safetyRatio: safety, allowedBytes: Math.floor(v * (1 - safety)), source: 'config.json' };
                }
            }
        }
        catch { /* ignore */ }
        // fallback
        const def = modulesDefault && modulesDefault > 0 ? modulesDefault : 200_000;
        const safety = Number.isFinite(safetyFromEnv) ? safetyFromEnv : (modulesSafety ?? 0.1);
        return { maxBytes: def, safetyRatio: safety, allowedBytes: Math.floor(def * (1 - safety)), source: (modulesDefault ? 'modules.json' : 'default') };
    }
    // Minimal schema check for contextBudget fields in modules.json (Phase 1 scope)
    validateConfig() {
        const issues = [];
        try {
            const { configDir } = resolveHostPaths();
            const modules = readJsonSync(path.join(configDir, 'modules.json'));
            if (modules) {
                const modulesConfig = extractModulesConfig(modules);
                if (modulesConfig && isRecord(modulesConfig.contextBudget)) {
                    const contextBudget = modulesConfig.contextBudget;
                    const defaultBytes = toNumber(contextBudget.defaultMaxContextBytes ?? contextBudget.defaultMaxContext);
                    const safetyRatio = toNumber(contextBudget.safetyRatio);
                    if (!(defaultBytes && defaultBytes > 0)) {
                        issues.push('contextBudget.defaultMaxContextBytes must be positive number');
                    }
                    if (!(typeof safetyRatio === 'number' && safetyRatio >= 0 && safetyRatio < 1)) {
                        issues.push('contextBudget.safetyRatio must be in [0,1)');
                    }
                }
            }
        }
        catch (error) {
            const err = error;
            issues.push(`validateConfig error: ${err?.message ?? String(error)}`);
        }
        return { ok: issues.length === 0, issues };
    }
    watchConfig(cb) {
        this.watchers.push(cb);
        if (this.fsWatcher) {
            return;
        }
        try {
            const { configDir } = resolveHostPaths();
            this.fsWatcher = fs.watch(configDir, { persistent: false }, (event, file) => {
                try {
                    this.watchers.forEach((fn) => fn({ file: file || '', event }));
                }
                catch {
                    /* ignore */
                }
            });
        }
        catch {
            /* ignore */
        }
    }
    unwatchConfig(cb) {
        if (!cb) {
            this.watchers = [];
        }
        else {
            this.watchers = this.watchers.filter((fn) => fn !== cb);
        }
        if (this.watchers.length === 0 && this.fsWatcher) {
            try {
                this.fsWatcher.close();
            }
            catch {
                /* ignore */
            }
            this.fsWatcher = null;
        }
    }
}
export const UnifiedConfig = UnifiedConfigFacade.getInstance();
