"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;
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");
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",
    "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
    });
    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 (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;
        }
        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 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) {
    if (!Array.isArray(options.value)) {
        return createInvalidTypeFinding({
            keyPath: options.keyPath,
            displayPath: options.displayPath,
            expectedTypeDescription: "an array of non-empty repository-relative paths"
        });
    }
    for (const entry of options.value) {
        if (typeof entry !== "string" || entry.trim().length === 0) {
            return createInvalidTypeFinding({
                keyPath: options.keyPath,
                displayPath: options.displayPath,
                expectedTypeDescription: "an array of non-empty repository-relative paths"
            });
        }
        try {
            (0, paths_1.parseRepositoryRelativePath)(entry.trim().replace(/\/+$/, ""));
        }
        catch {
            return createInvalidTypeFinding({
                keyPath: options.keyPath,
                displayPath: options.displayPath,
                expectedTypeDescription: "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"
            : typeof options.value === "string";
    return valid
        ? null
        : createInvalidTypeFinding({
            keyPath: options.keyPath,
            displayPath: options.displayPath,
            expectedTypeDescription: expectedType === "integer"
                ? "an integer"
                : expectedType === "boolean"
                    ? "a boolean"
                    : "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.";
}
