"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.STRUCTURAL_PRESSURE_THRESHOLD_DEFAULTS = exports.LOCAL_CONFIG_KEYS = exports.REPOSITORY_CONFIG_KEYS = void 0;
exports.readPersistedConfigFiles = readPersistedConfigFiles;
exports.validateStructuralPressureScanExclusionArrayValue = validateStructuralPressureScanExclusionArrayValue;
exports.validateRuntimeEvidencePathPolicyArrayValue = validateRuntimeEvidencePathPolicyArrayValue;
const node_fs_1 = __importDefault(require("node:fs"));
const node_path_1 = __importDefault(require("node:path"));
const toml = __importStar(require("@iarna/toml"));
const config_contract_1 = require("./config-contract");
const config_catalog_1 = require("./config-catalog");
const errors_1 = require("../errors");
const runtime_contract_1 = require("../runtime/runtime-contract");
const paths_1 = require("../paths");
const monorepo_projects_1 = require("./monorepo-projects");
exports.REPOSITORY_CONFIG_KEYS = [
    'planning_root',
    'spec_root',
    'spec_index',
    'agent_entrypoint',
    'default_readiness',
    'config_schema_version',
    'spec_index_schema_version',
    'template_version',
    'prompt_version',
    'prompt_root',
    'prompt_latest_root',
    'prompt_user_root',
    'cli_command',
    'cli_resolution',
    'cli_verified',
    'cli_verified_via',
    'monorepo_projects',
    'version',
];
exports.LOCAL_CONFIG_KEYS = ['version', 'editor'];
exports.STRUCTURAL_PRESSURE_THRESHOLD_DEFAULTS = {
    source: {
        watch: 300,
        pressure: 600,
        hotspot: 1000,
    },
    test: {
        watch: 600,
        pressure: 1200,
        hotspot: 2000,
    },
};
const RUNTIME_EVIDENCE_PATH_POLICY_KEYS = [
    'runtime_evidence.path_policy.documentation_only_paths',
    'runtime_evidence.path_policy.runtime_eligible_paths',
];
function readPersistedConfigFiles(repositoryContext) {
    const repositoryConfigPath = node_path_1.default.join(repositoryContext.root, runtime_contract_1.DAY_SHIFT_DIRECTORY_NAME, runtime_contract_1.CONFIG_FILE_NAME);
    const localConfigPath = node_path_1.default.join(repositoryContext.root, runtime_contract_1.DAY_SHIFT_DIRECTORY_NAME, runtime_contract_1.LOCAL_CONFIG_FILE_NAME);
    const repositoryConfig = readTomlFile({
        filePath: repositoryConfigPath,
        repositoryRoot: repositoryContext.root,
        sourceLayer: 'repository',
        stopAfterInvalidDetail: 'Later config layers were not inspected because resolution stops at the first invalid source.',
    });
    const localConfig = readTomlFile({
        filePath: localConfigPath,
        repositoryRoot: repositoryContext.root,
        sourceLayer: 'local',
    });
    return {
        repositoryConfigPath,
        localConfigPath,
        repositoryConfig,
        localConfig,
    };
}
function readTomlFile(options) {
    const displayPath = formatConfigPath(options.repositoryRoot, options.filePath);
    if (!node_fs_1.default.existsSync(options.filePath)) {
        return {};
    }
    const contents = node_fs_1.default.readFileSync(options.filePath, 'utf8');
    if (contents.trim() === '') {
        return {};
    }
    let parsed;
    try {
        parsed = toml.parse(contents);
    }
    catch (error) {
        throw createLayerInvalidConfigError({
            displayPath,
            details: [getTomlErrorMessage(error)],
            findings: [
                {
                    code: 'INVALID_TOML',
                    message: getTomlErrorMessage(error),
                    severity: 'error',
                    path: displayPath,
                },
            ],
            stopAfterInvalidDetail: options.stopAfterInvalidDetail,
        });
    }
    const invalidFindings = validatePersistedConfig({
        parsed,
        contents,
        displayPath,
        sourceLayer: options.sourceLayer,
        repositoryRoot: options.repositoryRoot,
    });
    if (invalidFindings.length > 0) {
        throw createLayerInvalidConfigError({
            displayPath,
            details: invalidFindings.map((finding) => finding.message),
            findings: invalidFindings,
            stopAfterInvalidDetail: options.stopAfterInvalidDetail,
        });
    }
    return parsed;
}
function validatePersistedConfig(options) {
    const findings = [];
    findings.push(...validateStructuralPressureThresholdTomlSurface({
        parsed: options.parsed,
        contents: options.contents,
        displayPath: options.displayPath,
    }));
    for (const entry of flattenConfigLeaves(options.parsed)) {
        if (entry.keyPath === 'monorepo_projects') {
            if (options.sourceLayer !== 'repository') {
                findings.push({
                    code: 'WRONG_CONFIG_SCOPE',
                    message: `Config key '${entry.keyPath}' is not allowed in ${options.displayPath}; allowed source layers: defaults, repository.`,
                    severity: 'error',
                    path: options.displayPath,
                });
                continue;
            }
            findings.push(...(0, monorepo_projects_1.validateMonorepoProjectsSync)({
                repositoryRoot: options.repositoryRoot,
                projects: entry.value,
                configPath: options.displayPath,
            }).findings);
            continue;
        }
        if (isStructuralPressureScanExclusionKey(entry.keyPath)) {
            if (options.sourceLayer !== 'repository') {
                findings.push({
                    code: 'WRONG_CONFIG_SCOPE',
                    message: `Config key '${entry.keyPath}' is not allowed in ${options.displayPath}; allowed source layers: defaults, repository.`,
                    severity: 'error',
                    path: options.displayPath,
                });
                continue;
            }
            const typeFinding = validateStructuralPressureScanExclusionValueType({
                keyPath: entry.keyPath,
                value: entry.value,
                displayPath: options.displayPath,
            });
            if (typeFinding !== null) {
                findings.push(typeFinding);
            }
            continue;
        }
        if (isRuntimeEvidencePathPolicyKey(entry.keyPath)) {
            if (options.sourceLayer !== 'repository') {
                findings.push({
                    code: 'WRONG_CONFIG_SCOPE',
                    message: `Config key '${entry.keyPath}' is not allowed in ${options.displayPath}; allowed source layers: defaults, repository.`,
                    severity: 'error',
                    path: options.displayPath,
                });
                continue;
            }
            const typeFinding = validateRuntimeEvidencePathPolicyValueType({
                keyPath: entry.keyPath,
                value: entry.value,
                displayPath: options.displayPath,
            });
            if (typeFinding !== null) {
                findings.push(typeFinding);
            }
            continue;
        }
        if (isContractRevisionImpactBudgetKey(entry.keyPath)) {
            if (options.sourceLayer !== 'repository') {
                findings.push({
                    code: 'WRONG_CONFIG_SCOPE',
                    message: `Config key '${entry.keyPath}' is not allowed in ${options.displayPath}; allowed source layers: defaults, repository, project, explicit.`,
                    severity: 'error',
                    path: options.displayPath,
                });
                continue;
            }
            if (!Number.isSafeInteger(entry.value) || Number(entry.value) < 0) {
                findings.push(createInvalidTypeFinding({
                    keyPath: entry.keyPath,
                    displayPath: options.displayPath,
                    expectedTypeDescription: 'a non-negative safe integer',
                }));
            }
            continue;
        }
        const catalogEntry = config_catalog_1.CONFIG_CATALOG_BY_KEY[entry.keyPath];
        if (catalogEntry === undefined) {
            findings.push(createUnknownOrSensitiveUnsupportedFinding(entry.keyPath, options.displayPath));
            continue;
        }
        if (!isPersistedTomlConfigKey(entry.keyPath, catalogEntry)) {
            findings.push(createUnsupportedSurfaceFinding(entry.keyPath, catalogEntry, options.displayPath));
            continue;
        }
        if (!catalogEntry.allowedSourceLayers.includes(options.sourceLayer)) {
            findings.push(createWrongScopeFinding({
                keyPath: entry.keyPath,
                catalogEntry,
                displayPath: options.displayPath,
                sourceLayer: options.sourceLayer,
            }));
            continue;
        }
        const typeFinding = validatePersistedValueType({
            keyPath: entry.keyPath,
            value: entry.value,
            displayPath: options.displayPath,
        });
        if (typeFinding !== null) {
            findings.push(typeFinding);
        }
    }
    if (options.sourceLayer === 'repository') {
        findings.push(...validateStructuralPressureThresholdOrdering(options.parsed, options.displayPath));
    }
    return findings;
}
function validateStructuralPressureThresholdTomlSurface(options) {
    const acceptedTableAssignments = new Set();
    let currentTablePath = null;
    let currentTableAcceptsThresholdLeaves = false;
    for (const line of options.contents.split(/\r?\n/)) {
        const content = stripTomlComment(line).trim();
        if (content === '') {
            continue;
        }
        const tableHeaderMatch = /^\[([^\]]+)\]$/.exec(content);
        if (tableHeaderMatch !== null) {
            currentTablePath = normalizeTomlDottedPath(tableHeaderMatch[1]);
            currentTableAcceptsThresholdLeaves =
                tableHeaderMatch[1].trim() === 'structural_pressure.source' ||
                    tableHeaderMatch[1].trim() === 'structural_pressure.test';
            continue;
        }
        const assignmentIndex = findTomlAssignmentIndex(content);
        if (assignmentIndex === -1) {
            continue;
        }
        const assignmentKeyPath = normalizeTomlDottedPath(content.slice(0, assignmentIndex));
        const candidatePath = currentTablePath === null ? assignmentKeyPath : `${currentTablePath}.${assignmentKeyPath}`;
        if (isStructuralPressureThresholdKey(candidatePath) &&
            currentTableAcceptsThresholdLeaves &&
            isStructuralPressureThresholdFamilyTable(currentTablePath) &&
            isStructuralPressureThresholdLeaf(assignmentKeyPath)) {
            acceptedTableAssignments.add(candidatePath);
        }
    }
    return flattenConfigLeaves(options.parsed)
        .filter((entry) => isStructuralPressureThresholdKey(entry.keyPath))
        .filter((entry) => !acceptedTableAssignments.has(entry.keyPath))
        .map((entry) => createUnsupportedThresholdTomlSurfaceFinding(entry.keyPath, options.displayPath));
}
function stripTomlComment(line) {
    let quote = null;
    let escaped = false;
    for (let index = 0; index < line.length; index += 1) {
        const char = line[index];
        if (quote === '"') {
            if (escaped) {
                escaped = false;
                continue;
            }
            if (char === '\\') {
                escaped = true;
                continue;
            }
            if (char === quote) {
                quote = null;
            }
            continue;
        }
        if (quote === "'") {
            if (char === quote) {
                quote = null;
            }
            continue;
        }
        if (char === '"' || char === "'") {
            quote = char;
            continue;
        }
        if (char === '#') {
            return line.slice(0, index);
        }
    }
    return line;
}
function findTomlAssignmentIndex(content) {
    let quote = null;
    let escaped = false;
    for (let index = 0; index < content.length; index += 1) {
        const char = content[index];
        if (quote === '"') {
            if (escaped) {
                escaped = false;
                continue;
            }
            if (char === '\\') {
                escaped = true;
                continue;
            }
            if (char === quote) {
                quote = null;
            }
            continue;
        }
        if (quote === "'") {
            if (char === quote) {
                quote = null;
            }
            continue;
        }
        if (char === '"' || char === "'") {
            quote = char;
            continue;
        }
        if (char === '=') {
            return index;
        }
    }
    return -1;
}
function normalizeTomlDottedPath(pathSource) {
    return splitTomlDottedPath(pathSource)
        .map((segment) => normalizeTomlKeySegment(segment))
        .filter((segment) => segment !== '')
        .join('.');
}
function splitTomlDottedPath(pathSource) {
    const segments = [];
    let currentSegment = '';
    let quote = null;
    let escaped = false;
    for (const char of pathSource) {
        currentSegment += char;
        if (quote === '"') {
            if (escaped) {
                escaped = false;
                continue;
            }
            if (char === '\\') {
                escaped = true;
                continue;
            }
            if (char === quote) {
                quote = null;
            }
            continue;
        }
        if (quote === "'") {
            if (char === quote) {
                quote = null;
            }
            continue;
        }
        if (char === '"' || char === "'") {
            quote = char;
            continue;
        }
        if (char === '.') {
            segments.push(currentSegment.slice(0, -1));
            currentSegment = '';
        }
    }
    segments.push(currentSegment);
    return segments;
}
function normalizeTomlKeySegment(segment) {
    const trimmedSegment = segment.trim();
    if (trimmedSegment.length >= 2 &&
        ((trimmedSegment.startsWith('"') && trimmedSegment.endsWith('"')) ||
            (trimmedSegment.startsWith("'") && trimmedSegment.endsWith("'")))) {
        return trimmedSegment.slice(1, -1).trim();
    }
    return trimmedSegment;
}
function flattenConfigLeaves(value, prefix = []) {
    const leaves = [];
    for (const [key, childValue] of Object.entries(value)) {
        const childPath = [...prefix, key];
        if (isPlainObject(childValue) && Object.keys(childValue).length > 0) {
            leaves.push(...flattenConfigLeaves(childValue, childPath));
            continue;
        }
        leaves.push({
            keyPath: childPath.join('.'),
            value: childValue,
        });
    }
    return leaves;
}
function isPersistedTomlConfigKey(keyPath, catalogEntry) {
    return (catalogEntry.persistedConfigContract !== undefined ||
        isStructuralPressureThresholdKey(keyPath) ||
        isStructuralPressureScanExclusionKey(keyPath));
}
function isContractRevisionImpactBudgetKey(keyPath) {
    return (keyPath === 'lifecycle.contract_revision.impact_budget.max_invalidated_entries' ||
        keyPath === 'lifecycle.contract_revision.impact_budget.max_evidence_bytes');
}
function validateStructuralPressureScanExclusionValueType(options) {
    const expectedTypeDescription = validateStructuralPressureScanExclusionArrayValue({
        keyPath: options.keyPath,
        value: options.value,
    });
    if (expectedTypeDescription === null) {
        return null;
    }
    return createInvalidTypeFinding({
        keyPath: options.keyPath,
        displayPath: options.displayPath,
        expectedTypeDescription,
    });
}
function validateStructuralPressureScanExclusionArrayValue(options) {
    if (!Array.isArray(options.value)) {
        return 'an array of non-empty strings';
    }
    const values = options.value;
    if (values.some((entry) => typeof entry !== 'string' || entry.trim().length === 0)) {
        return 'an array of non-empty strings';
    }
    if (options.keyPath === 'structural_pressure.excluded_extensions') {
        if (values.some((entry) => !entry.trim().startsWith('.'))) {
            return 'an array of dot-prefixed file extensions';
        }
        return null;
    }
    if (options.keyPath === 'structural_pressure.blocked_segments') {
        if (values.some((entry) => /[\\/]/.test(entry.trim()))) {
            return "an array of single path segments (no '/' separators)";
        }
        return null;
    }
    if (options.keyPath === 'structural_pressure.excluded_basenames') {
        if (values.some((entry) => /[\\/]/.test(entry.trim()))) {
            return "an array of file basenames (no '/' separators)";
        }
        return null;
    }
    return 'a supported structural-pressure exclusion config key';
}
function validateRuntimeEvidencePathPolicyValueType(options) {
    const expectedTypeDescription = validateRuntimeEvidencePathPolicyArrayValue(options.value);
    if (expectedTypeDescription !== null) {
        return createInvalidTypeFinding({
            keyPath: options.keyPath,
            displayPath: options.displayPath,
            expectedTypeDescription,
        });
    }
    return null;
}
function validateRuntimeEvidencePathPolicyArrayValue(value) {
    if (!Array.isArray(value)) {
        return 'an array of non-empty repository-relative paths';
    }
    for (const entry of value) {
        if (typeof entry !== 'string' || entry.trim().length === 0) {
            return 'an array of non-empty repository-relative paths';
        }
        try {
            (0, paths_1.parseRepositoryRelativePath)(entry.trim().replace(/\/+$/, ''));
        }
        catch {
            return 'an array of non-empty repository-relative paths';
        }
    }
    return null;
}
function validatePersistedValueType(options) {
    if (isStructuralPressureThresholdKey(options.keyPath)) {
        return isPositiveInteger(options.value)
            ? null
            : createInvalidTypeFinding({
                keyPath: options.keyPath,
                displayPath: options.displayPath,
                expectedTypeDescription: 'a positive integer',
            });
    }
    const persistedKey = options.keyPath;
    if (!Object.hasOwn(config_contract_1.CONFIG_FIELD_CONTRACTS, persistedKey)) {
        return null;
    }
    const expectedType = config_catalog_1.CONFIG_CATALOG_BY_KEY[persistedKey]?.valueType;
    const valid = expectedType === 'integer'
        ? Number.isInteger(options.value)
        : expectedType === 'boolean'
            ? typeof options.value === 'boolean'
            : expectedType === 'array'
                ? Array.isArray(options.value)
                : typeof options.value === 'string';
    return valid
        ? null
        : createInvalidTypeFinding({
            keyPath: options.keyPath,
            displayPath: options.displayPath,
            expectedTypeDescription: expectedType === 'integer'
                ? 'an integer'
                : expectedType === 'boolean'
                    ? 'a boolean'
                    : expectedType === 'array'
                        ? 'an array'
                        : 'a string',
        });
}
function validateStructuralPressureThresholdOrdering(parsed, displayPath) {
    const structuralPressure = parsed.structural_pressure;
    if (!isPlainObject(structuralPressure)) {
        return [];
    }
    const findings = [];
    for (const family of ['source', 'test']) {
        const configuredFamily = structuralPressure[family];
        if (!isPlainObject(configuredFamily)) {
            continue;
        }
        const thresholds = {
            ...exports.STRUCTURAL_PRESSURE_THRESHOLD_DEFAULTS[family],
            ...configuredFamily,
        };
        if (!isPositiveInteger(thresholds.watch) ||
            !isPositiveInteger(thresholds.pressure) ||
            !isPositiveInteger(thresholds.hotspot)) {
            continue;
        }
        if (thresholds.watch <= thresholds.pressure && thresholds.pressure <= thresholds.hotspot) {
            continue;
        }
        findings.push({
            code: 'INVALID_CONFIG_THRESHOLD_ORDERING',
            message: `Invalid structural-pressure threshold ordering in [structural_pressure.${family}]: expected watch <= pressure <= hotspot.`,
            severity: 'error',
            path: displayPath,
        });
    }
    return findings;
}
function createUnknownOrSensitiveUnsupportedFinding(keyPath, displayPath) {
    if (looksSensitiveUnsupportedKey(keyPath)) {
        return {
            code: 'SENSITIVE_UNSUPPORTED_CONFIG_KEY',
            message: `Unsupported sensitive config key '${keyPath}' in ${displayPath}; remove it from persisted config.`,
            severity: 'error',
            path: displayPath,
        };
    }
    return {
        code: 'UNKNOWN_CONFIG_KEY',
        message: `Unknown config key '${keyPath}' in ${displayPath}.`,
        severity: 'error',
        path: displayPath,
    };
}
function createWrongScopeFinding(options) {
    const allowedLayers = options.catalogEntry.allowedSourceLayers.join(', ');
    return {
        code: 'WRONG_CONFIG_SCOPE',
        message: `Config key '${options.keyPath}' is not allowed in ${options.displayPath}; allowed source layers: ${allowedLayers}.`,
        severity: 'error',
        path: options.displayPath,
    };
}
function createUnsupportedSurfaceFinding(keyPath, catalogEntry, displayPath) {
    if (isSensitiveCatalogEntry(catalogEntry) || looksSensitiveUnsupportedKey(keyPath)) {
        return {
            code: 'SENSITIVE_UNSUPPORTED_CONFIG_KEY',
            message: `Unsupported sensitive config key '${keyPath}' in ${displayPath}; remove it from persisted config.`,
            severity: 'error',
            path: displayPath,
        };
    }
    return {
        code: 'UNSUPPORTED_CONFIG_SURFACE',
        message: `Config key '${keyPath}' is catalog-visible but is not accepted as persisted TOML config.`,
        severity: 'error',
        path: displayPath,
    };
}
function createUnsupportedThresholdTomlSurfaceFinding(keyPath, displayPath) {
    const match = /^structural_pressure\.(source|test)\.(watch|pressure|hotspot)$/.exec(keyPath);
    const tableName = match === null ? '[structural_pressure.source] or [structural_pressure.test]' : `[structural_pressure.${match[1]}]`;
    return {
        code: 'UNSUPPORTED_CONFIG_SURFACE',
        message: `Config key '${keyPath}' must be stored as a leaf under ${tableName}; dotted or inline structural-pressure threshold TOML is not accepted.`,
        severity: 'error',
        path: displayPath,
    };
}
function createInvalidTypeFinding(options) {
    return {
        code: 'INVALID_CONFIG_VALUE_TYPE',
        message: `Invalid value for config key '${options.keyPath}' in ${options.displayPath}; expected ${options.expectedTypeDescription}.`,
        severity: 'error',
        path: options.displayPath,
    };
}
function isStructuralPressureThresholdKey(keyPath) {
    return /^structural_pressure\.(source|test)\.(watch|pressure|hotspot)$/.test(keyPath);
}
function isStructuralPressureScanExclusionKey(keyPath) {
    return (keyPath === 'structural_pressure.excluded_extensions' ||
        keyPath === 'structural_pressure.blocked_segments' ||
        keyPath === 'structural_pressure.excluded_basenames');
}
function isRuntimeEvidencePathPolicyKey(keyPath) {
    return RUNTIME_EVIDENCE_PATH_POLICY_KEYS.includes(keyPath);
}
function isStructuralPressureThresholdFamilyTable(keyPath) {
    return keyPath === 'structural_pressure.source' || keyPath === 'structural_pressure.test';
}
function isStructuralPressureThresholdLeaf(keyPath) {
    return keyPath === 'watch' || keyPath === 'pressure' || keyPath === 'hotspot';
}
function isPositiveInteger(value) {
    return Number.isInteger(value) && Number(value) > 0;
}
function isPlainObject(value) {
    return typeof value === 'object' && value !== null && !Array.isArray(value);
}
function isSensitiveCatalogEntry(catalogEntry) {
    return (catalogEntry.sensitivity === 'sensitive' ||
        catalogEntry.sensitivity === 'secret' ||
        catalogEntry.key.startsWith('license.') ||
        catalogEntry.key.startsWith('reminder.'));
}
function looksSensitiveUnsupportedKey(keyPath) {
    return (0, config_catalog_1.isSensitiveUnsupportedConfigKeyPath)(keyPath);
}
function createLayerInvalidConfigError(options) {
    const details = [...options.details];
    if (options.stopAfterInvalidDetail !== undefined) {
        details.push(options.stopAfterInvalidDetail);
    }
    return (0, errors_1.createInvalidConfigError)(`Invalid configuration in ${options.displayPath}.`, details, options.findings);
}
function formatConfigPath(repositoryRoot, filePath) {
    const relativePath = node_path_1.default.relative(repositoryRoot, filePath);
    return relativePath === '' ? filePath : relativePath.split(node_path_1.default.sep).join('/');
}
function getTomlErrorMessage(error) {
    return error instanceof Error ? error.message : 'Could not parse TOML content.';
}
