// engine/heal/recipes/out-of-range.ts — SPEC-745
// Schema-driven range checks for planu config files.
// Produces a proposal diff (tier-2) rather than auto-fixing.
import { readFileSync } from 'node:fs';
// Schema definitions for known planu config files
const RANGE_SCHEMAS = {
    'conventions.json': {
        'telemetry.retentionDays': { min: 1, max: 365, default: 90 },
        'budget.maxCostPerRun': { min: 0, max: 50, default: 20 },
        'budget.defaultBudget': { min: 0, max: 100, default: 20 },
    },
};
/**
 * Check a JSON file for out-of-range values against the known schema.
 * Returns violations and a proposed corrected content string.
 */
export function checkOutOfRange(filePath) {
    const fileName = filePath.split('/').pop() ?? '';
    const schema = RANGE_SCHEMAS[fileName];
    if (!schema) {
        return { hasViolations: false, violations: [] };
    }
    let data;
    try {
        const raw = readFileSync(filePath, 'utf-8');
        data = JSON.parse(raw);
    }
    catch {
        // Broken JSON — handled by json-syntax recipe
        return { hasViolations: false, violations: [] };
    }
    const violations = [];
    for (const [fieldPath, range] of Object.entries(schema)) {
        const value = getNestedValue(data, fieldPath);
        if (typeof value !== 'number') {
            continue;
        }
        const tooLow = range.min !== undefined && value < range.min;
        const tooHigh = range.max !== undefined && value > range.max;
        if (tooLow || tooHigh) {
            violations.push({
                field: fieldPath,
                value,
                proposed: range.default,
                reason: tooHigh
                    ? `${fieldPath} = ${value} exceeds max ${range.max?.toString() ?? '∞'}`
                    : `${fieldPath} = ${value} is below min ${range.min?.toString() ?? '0'}`,
            });
            setNestedValue(data, fieldPath, range.default);
        }
    }
    if (violations.length === 0) {
        return { hasViolations: false, violations: [] };
    }
    return {
        hasViolations: true,
        violations,
        proposedContent: JSON.stringify(data, null, 2),
    };
}
function getNestedValue(obj, path) {
    const parts = path.split('.');
    let current = obj;
    for (const part of parts) {
        if (current === null || typeof current !== 'object') {
            return undefined;
        }
        current = current[part];
    }
    return current;
}
function setNestedValue(obj, path, value) {
    const parts = path.split('.');
    let current = obj;
    for (let i = 0; i < parts.length - 1; i++) {
        // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style -- parts always has length >= 1 here
        const part = parts[i];
        if (typeof current[part] !== 'object' || current[part] === null) {
            current[part] = {};
        }
        current = current[part];
    }
    // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style -- parts always non-empty
    const lastKey = parts[parts.length - 1];
    current[lastKey] = value;
}
//# sourceMappingURL=out-of-range.js.map