"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.readRuntimeDeltaEvidence = readRuntimeDeltaEvidence;
exports.readChangedPathEvidence = readChangedPathEvidence;
exports.readChangedPaths = readChangedPaths;
const paths_1 = require("../paths");
const RUNTIME_DELTA_HEADINGS = new Set([
    "runtime delta",
    "runtime change",
    "runtime changes",
    "runtime impact",
    "execution delta"
]);
const RUNTIME_DELTA_LABELS = {
    target: new Set([
        "runtime target file",
        "runtime target files",
        "runtime target path",
        "runtime target paths",
        "runtime file group",
        "runtime file groups",
        "target file",
        "target files",
        "target path",
        "target paths",
        "affected runtime file",
        "affected runtime files"
    ]),
    behavior: new Set([
        "expected behavior change",
        "behavior change",
        "expected runtime behavior",
        "runtime behavior change"
    ]),
    validation: new Set([
        "verification command",
        "verification commands",
        "verification command with expected evidence",
        "verification commands with expected evidence",
        "validation command with expected evidence",
        "validation commands with expected evidence"
    ]),
    rollback: new Set([
        "rollback command",
        "rollback commands",
        "rollback path",
        "rollback scope",
        "revert scope",
        "recovery path"
    ])
};
const PLACEHOLDER_CHANGED_PATH = /^(?:none(?: yet)?\.?|n\/a\.?|not applicable\.?|tbd|todo|pending|placeholder)$/i;
const GLOB_META = /[*?[\]{}]/;
const EXTERNAL_OR_FRAGMENT = /^(?:[a-z][a-z0-9+.-]*:|\/\/|#)/i;
function readRuntimeDeltaEvidence(sections) {
    const selected = [...sections.entries()].find(([heading]) => RUNTIME_DELTA_HEADINGS.has(normalizeSemanticLabel(heading)));
    if (selected === undefined) {
        return {
            heading: null,
            sectionText: null,
            entries: [],
            missingKinds: ["target", "behavior", "validation", "rollback"]
        };
    }
    const entries = [];
    for (const [index, line] of selected[1].split(/\r?\n/).entries()) {
        const match = line.match(/^-\s+(?:\*\*)?(.+?)(?:\*\*)?\s*(?::|—|–)\s*(.+)$/);
        if (match === null)
            continue;
        const label = normalizeSemanticLabel(match[1]);
        const kind = runtimeDeltaKindForLabel(label);
        if (kind === null)
            continue;
        const value = match[2].trim();
        if (value === "")
            continue;
        if (kind === "validation"
            && !label.includes("expected evidence")
            && !/\b(?:expected|expectation|exits?|asserts?|passes?|produces?|returns?|output|observable|evidence)\b/i.test(value)) {
            continue;
        }
        entries.push({ kind, label: match[1].trim(), value, line: index + 1 });
    }
    const presentKinds = new Set(entries.map((entry) => entry.kind));
    return {
        heading: selected[0],
        sectionText: selected[1],
        entries,
        missingKinds: ["target", "behavior", "validation", "rollback"]
            .filter((kind) => !presentKinds.has(kind))
    };
}
function readChangedPathEvidence(sectionText) {
    const evidence = [];
    const lines = sectionText.split(/\r?\n/);
    for (const [index, line] of lines.entries()) {
        if (/^\s+-\s+/.test(line) && !line.startsWith("- ")) {
            evidence.push({
                state: "prose",
                rawText: line.trim(),
                line: index + 1,
                column: line.search(/\S/) + 1,
                message: `Changed Paths line ${index + 1} is nested; only top-level path bullets are admissible.`
            });
            continue;
        }
        const bullet = line.match(/^-\s+(.+?)\s*$/);
        if (bullet === null)
            continue;
        const rawText = bullet[1].trim();
        const candidate = extractChangedPathCandidate(rawText);
        if (candidate === null) {
            evidence.push({
                state: PLACEHOLDER_CHANGED_PATH.test(rawText) ? "placeholder" : "prose",
                rawText,
                line: index + 1,
                column: 3,
                message: PLACEHOLDER_CHANGED_PATH.test(rawText)
                    ? `Changed Paths entry '${rawText}' is placeholder evidence (line ${index + 1}).`
                    : `Changed Paths entry '${rawText}' is not an exact path bullet or supported Markdown link target (line ${index + 1}).`
            });
            continue;
        }
        if (GLOB_META.test(candidate) || EXTERNAL_OR_FRAGMENT.test(candidate) || candidate.includes("\0")) {
            evidence.push({
                state: "invalid",
                rawText,
                line: index + 1,
                column: 3,
                message: `Changed Paths line ${index + 1} uses a glob, external reference, fragment, or otherwise unsafe path shape.`
            });
            continue;
        }
        try {
            const normalizedPath = (0, paths_1.parseRepositoryRelativePath)(candidate).normalizedPath;
            evidence.push({
                state: "path",
                rawText,
                line: index + 1,
                column: 3,
                path: normalizedPath,
                message: `Changed Paths line ${index + 1} resolves to repository path '${normalizedPath}'.`
            });
        }
        catch (error) {
            evidence.push({
                state: "invalid",
                rawText,
                line: index + 1,
                column: 3,
                message: `Changed Paths line ${index + 1} is invalid: ${error instanceof Error ? error.message : String(error)}`
            });
        }
    }
    if (evidence.length === 0 && sectionText.trim() !== "") {
        evidence.push({
            state: "prose",
            rawText: sectionText.trim(),
            line: 1,
            column: 1,
            message: "Changed Paths contains prose-only evidence instead of top-level repository path bullets."
        });
    }
    return evidence;
}
function readChangedPaths(sectionText) {
    return unique(readChangedPathEvidence(sectionText)
        .filter((entry) => entry.state === "path" && entry.path !== undefined)
        .map((entry) => entry.path));
}
function runtimeDeltaKindForLabel(label) {
    for (const kind of ["target", "behavior", "validation", "rollback"]) {
        if (RUNTIME_DELTA_LABELS[kind].has(label))
            return kind;
    }
    return null;
}
function normalizeSemanticLabel(value) {
    return value
        .replace(/[*_`]/g, "")
        .replace(/[/:_-]+/g, " ")
        .replace(/\s+/g, " ")
        .trim()
        .toLowerCase();
}
function extractChangedPathCandidate(rawText) {
    const codeSpan = rawText.match(/^`([^`\n]+)`$/);
    if (codeSpan !== null) {
        const value = codeSpan[1].trim();
        return /\s/.test(value) ? null : value;
    }
    const markdownLink = rawText.match(/^\[[^\]\r\n]+\]\(\s*(?:<([^>\r\n]+)>|([^\s)]+))(?:\s+["'][^"'\r\n]*["'])?\s*\)$/);
    if (markdownLink !== null)
        return (markdownLink[1] ?? markdownLink[2]).trim();
    if (PLACEHOLDER_CHANGED_PATH.test(rawText)
        || /\s/.test(rawText)
        || rawText.includes("`")
        || rawText.includes("[")
        || rawText.includes("]")) {
        return null;
    }
    return rawText;
}
function unique(values) {
    return [...new Set(values)];
}
