import { readFile } from 'node:fs/promises';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
function configPath() {
    return join(dirname(fileURLToPath(import.meta.url)), '../../config/token-waste-autopilot.json');
}
function isRecord(value) {
    return typeof value === 'object' && value !== null && !Array.isArray(value);
}
function mergeDeep(base, override) {
    const result = { ...base };
    for (const [key, value] of Object.entries(override)) {
        const current = result[key];
        result[key] = isRecord(current) && isRecord(value) ? mergeDeep(current, value) : value;
    }
    return result;
}
function assertStringArray(value, path) {
    if (!Array.isArray(value) || value.some((item) => typeof item !== 'string')) {
        throw new Error(`Invalid token waste policy at ${path}: expected string[]`);
    }
}
function validatePolicy(value) {
    if (!isRecord(value)) {
        throw new Error('Invalid token waste policy: expected object');
    }
    if (value.version !== 1) {
        throw new Error('Invalid token waste policy: version must be 1');
    }
    const policy = value;
    assertStringArray(policy.context.generatedPatterns, 'context.generatedPatterns');
    assertStringArray(policy.context.stalePatterns, 'context.stalePatterns');
    assertStringArray(policy.context.summarizePatterns, 'context.summarizePatterns');
    if (!Number.isFinite(policy.context.staleSessionMinutes) ||
        policy.context.staleSessionMinutes < 1) {
        throw new Error('Invalid token waste policy at context.staleSessionMinutes: expected positive number');
    }
    assertStringArray(policy.context.safetyOverrideActions, 'context.safetyOverrideActions');
    if (!isRecord(policy.outputs.strategies)) {
        throw new Error('Invalid token waste policy at outputs.strategies: expected object');
    }
    if (!isRecord(policy.tools.groups)) {
        throw new Error('Invalid token waste policy at tools.groups: expected object');
    }
    for (const [group, tools] of Object.entries(policy.tools.groups)) {
        assertStringArray(tools, `tools.groups.${group}`);
    }
    if (!Number.isFinite(policy.tools.maxRecommended) || policy.tools.maxRecommended < 1) {
        throw new Error('Invalid token waste policy at tools.maxRecommended: expected positive number');
    }
    if (!isRecord(policy.loops.thresholds)) {
        throw new Error('Invalid token waste policy at loops.thresholds: expected object');
    }
    assertStringArray(policy.loops.safetyOverrideActions, 'loops.safetyOverrideActions');
    assertStringArray(policy.redaction.redactPatterns, 'redaction.redactPatterns');
    return policy;
}
async function readJson(path) {
    return JSON.parse(await readFile(path, 'utf-8'));
}
export async function loadTokenWastePolicy(projectPath, options = {}) {
    const defaultPolicyPath = options.defaultPolicyPath ?? configPath();
    const defaultPolicy = validatePolicy(await readJson(defaultPolicyPath));
    const evidence = [
        { source: 'policy', key: defaultPolicyPath },
    ];
    let merged = defaultPolicy;
    if (projectPath !== undefined) {
        const planuConfigPath = join(projectPath, 'planu.json');
        try {
            const config = await readJson(planuConfigPath);
            if (isRecord(config) && isRecord(config.tokenWasteAutopilot)) {
                merged = validatePolicy(mergeDeep(defaultPolicy, config.tokenWasteAutopilot));
                evidence.push({ source: 'project-config', key: 'planu.json.tokenWasteAutopilot' });
            }
        }
        catch {
            // Missing or malformed project config does not block default policy loading.
        }
    }
    return { policy: merged, evidence };
}
//# sourceMappingURL=policy-loader.js.map