"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DECLARED_TARGET_PATH_SECTION = void 0;
exports.readTaskDeclaredTargetPathEvidence = readTaskDeclaredTargetPathEvidence;
exports.readMilestoneDeclaredTargetPathEvidence = readMilestoneDeclaredTargetPathEvidence;
exports.resolveDeclaredTargetPathRole = resolveDeclaredTargetPathRole;
const node_fs_1 = __importDefault(require("node:fs"));
const node_path_1 = __importDefault(require("node:path"));
const planning_document_utils_1 = require("../planning/planning-document-utils");
const paths_1 = require("../paths");
exports.DECLARED_TARGET_PATH_SECTION = "Implementation Target Paths";
function readTaskDeclaredTargetPathEvidence(taskDefinitionPath, markdownText) {
    const normalizedTaskDefinitionPath = (0, planning_document_utils_1.toRepositoryLikePath)(taskDefinitionPath);
    const sections = (0, planning_document_utils_1.parseMarkdownSections)(markdownText);
    const sectionText = sections.get(exports.DECLARED_TARGET_PATH_SECTION);
    if (sectionText === undefined) {
        return {
            scopeKind: "task",
            taskDefinitionPath: normalizedTaskDefinitionPath,
            sectionName: exports.DECLARED_TARGET_PATH_SECTION,
            evidence: [
                {
                    state: "missing-section",
                    scopeKind: "task",
                    sourceArtifactPath: normalizedTaskDefinitionPath,
                    sourceSection: exports.DECLARED_TARGET_PATH_SECTION,
                    rawText: "",
                    message: `Task definition '${normalizedTaskDefinitionPath}' is missing '## ${exports.DECLARED_TARGET_PATH_SECTION}'.`
                }
            ],
            explicitTargetPaths: []
        };
    }
    const evidence = classifyDeclaredTargetPathSection({
        scopeKind: "task",
        sourceArtifactPath: normalizedTaskDefinitionPath,
        sectionText
    });
    return {
        scopeKind: "task",
        taskDefinitionPath: normalizedTaskDefinitionPath,
        sectionName: exports.DECLARED_TARGET_PATH_SECTION,
        evidence,
        explicitTargetPaths: explicitTargetPathUnion(evidence)
    };
}
function readMilestoneDeclaredTargetPathEvidence(repositoryRoot, milestoneOverviewPath) {
    const normalizedMilestoneOverviewPath = (0, planning_document_utils_1.toRepositoryLikePath)(milestoneOverviewPath);
    const tasksDirectoryPath = (0, planning_document_utils_1.toRepositoryLikePath)(node_path_1.default.join(node_path_1.default.dirname(normalizedMilestoneOverviewPath), "tasks"));
    const absoluteTasksDirectoryPath = node_path_1.default.join(repositoryRoot, tasksDirectoryPath);
    const evidence = [];
    const childTaskDefinitionPaths = [];
    const milestoneSeenPaths = new Map();
    if (!node_fs_1.default.existsSync(absoluteTasksDirectoryPath)) {
        return {
            scopeKind: "milestone",
            milestoneOverviewPath: normalizedMilestoneOverviewPath,
            tasksDirectoryPath,
            childTaskDefinitionPaths,
            evidence,
            explicitTargetPaths: []
        };
    }
    const childTaskDirectories = node_fs_1.default.readdirSync(absoluteTasksDirectoryPath, { withFileTypes: true })
        .filter((entry) => entry.isDirectory())
        .map((entry) => entry.name)
        .sort((a, b) => a.localeCompare(b, "en"));
    for (const childTaskDirectory of childTaskDirectories) {
        const childTaskDefinitionPath = (0, planning_document_utils_1.toRepositoryLikePath)(node_path_1.default.join(tasksDirectoryPath, childTaskDirectory, "task-definition.md"));
        childTaskDefinitionPaths.push(childTaskDefinitionPath);
        const absoluteChildTaskDefinitionPath = node_path_1.default.join(repositoryRoot, childTaskDefinitionPath);
        let childText;
        try {
            childText = node_fs_1.default.readFileSync(absoluteChildTaskDefinitionPath, "utf8");
        }
        catch (error) {
            evidence.push({
                state: "unreadable-child-artifact",
                scopeKind: "milestone",
                sourceArtifactPath: childTaskDefinitionPath,
                sourceSection: "milestone-child-task",
                rawText: childTaskDefinitionPath,
                message: `Child task definition '${childTaskDefinitionPath}' could not be read: ${error instanceof Error ? error.message : String(error)}.`
            });
            continue;
        }
        const childResult = readTaskDeclaredTargetPathEvidence(childTaskDefinitionPath, childText);
        for (const childEvidence of childResult.evidence) {
            if (childEvidence.state !== "explicit") {
                evidence.push({
                    ...childEvidence,
                    scopeKind: "milestone"
                });
                continue;
            }
            const targetPath = childEvidence.path;
            if (targetPath === undefined) {
                continue;
            }
            const firstSource = milestoneSeenPaths.get(targetPath);
            if (firstSource !== undefined) {
                evidence.push({
                    ...childEvidence,
                    state: "duplicate",
                    scopeKind: "milestone",
                    duplicateOf: firstSource,
                    message: `Declared target path '${targetPath}' duplicates earlier milestone target evidence from '${firstSource}'.`
                });
                continue;
            }
            milestoneSeenPaths.set(targetPath, childTaskDefinitionPath);
            evidence.push({
                ...childEvidence,
                scopeKind: "milestone"
            });
        }
    }
    return {
        scopeKind: "milestone",
        milestoneOverviewPath: normalizedMilestoneOverviewPath,
        tasksDirectoryPath,
        childTaskDefinitionPaths,
        evidence,
        explicitTargetPaths: explicitTargetPathUnion(evidence)
    };
}
function classifyDeclaredTargetPathSection(options) {
    const evidence = [];
    const seenPaths = new Map();
    const bulletLines = options.sectionText
        .split(/\r?\n/)
        .map((line) => line.trim())
        .filter((line) => line.startsWith("- "));
    if (bulletLines.length === 0 && options.sectionText.trim() !== "") {
        evidence.push({
            state: "prose",
            scopeKind: options.scopeKind,
            sourceArtifactPath: options.sourceArtifactPath,
            sourceSection: exports.DECLARED_TARGET_PATH_SECTION,
            rawText: options.sectionText.trim(),
            message: `Section '## ${exports.DECLARED_TARGET_PATH_SECTION}' in '${options.sourceArtifactPath}' contains prose-only target-path evidence.`
        });
        return evidence;
    }
    for (const bulletLine of bulletLines) {
        const rawItem = bulletLine.slice(2).trim();
        const parsedItem = parseDeclaredTargetPathItem(rawItem);
        if (parsedItem.issue !== null) {
            evidence.push({
                state: "prose",
                scopeKind: options.scopeKind,
                sourceArtifactPath: options.sourceArtifactPath,
                sourceSection: exports.DECLARED_TARGET_PATH_SECTION,
                rawText: rawItem,
                message: `${parsedItem.issue} in '${options.sourceArtifactPath}'.`
            });
            continue;
        }
        const rawPath = unwrapSingleCodeSpan(parsedItem.pathText);
        if (isPlaceholderTargetPath(rawPath)) {
            evidence.push({
                state: "placeholder",
                scopeKind: options.scopeKind,
                sourceArtifactPath: options.sourceArtifactPath,
                sourceSection: exports.DECLARED_TARGET_PATH_SECTION,
                rawText: rawItem,
                message: `Declared target path entry '${rawItem}' in '${options.sourceArtifactPath}' is placeholder evidence.`
            });
            continue;
        }
        const normalizedPath = normalizeDeclaredTargetPath(rawPath);
        if (normalizedPath === null) {
            evidence.push({
                state: "prose",
                scopeKind: options.scopeKind,
                sourceArtifactPath: options.sourceArtifactPath,
                sourceSection: exports.DECLARED_TARGET_PATH_SECTION,
                rawText: rawItem,
                message: `Declared target path entry '${rawItem}' in '${options.sourceArtifactPath}' is not an explicit repository-relative path.`
            });
            continue;
        }
        const firstRole = seenPaths.get(normalizedPath);
        if (firstRole !== undefined) {
            const conflictingRole = firstRole !== (parsedItem.role ?? null);
            evidence.push({
                state: "duplicate",
                scopeKind: options.scopeKind,
                sourceArtifactPath: options.sourceArtifactPath,
                sourceSection: exports.DECLARED_TARGET_PATH_SECTION,
                rawText: rawItem,
                path: normalizedPath,
                ...(parsedItem.role === null ? {} : { role: parsedItem.role }),
                duplicateOf: options.sourceArtifactPath,
                message: conflictingRole
                    ? `Declared target path '${normalizedPath}' has conflicting role metadata ('${firstRole ?? "path-default"}' and '${parsedItem.role ?? "path-default"}') in '${options.sourceArtifactPath}'.`
                    : `Declared target path '${normalizedPath}' is duplicated in '${options.sourceArtifactPath}'.`
            });
            continue;
        }
        seenPaths.set(normalizedPath, parsedItem.role);
        evidence.push({
            state: "explicit",
            scopeKind: options.scopeKind,
            sourceArtifactPath: options.sourceArtifactPath,
            sourceSection: exports.DECLARED_TARGET_PATH_SECTION,
            rawText: rawItem,
            path: normalizedPath,
            ...(parsedItem.role === null ? {} : { role: parsedItem.role }),
            message: parsedItem.role === null
                ? `Declared target path '${normalizedPath}' was read from '## ${exports.DECLARED_TARGET_PATH_SECTION}' in '${options.sourceArtifactPath}'.`
                : `Declared target path '${normalizedPath}' with role '${parsedItem.role}' was read from '## ${exports.DECLARED_TARGET_PATH_SECTION}' in '${options.sourceArtifactPath}'.`
        });
    }
    if (evidence.length === 0) {
        evidence.push({
            state: "placeholder",
            scopeKind: options.scopeKind,
            sourceArtifactPath: options.sourceArtifactPath,
            sourceSection: exports.DECLARED_TARGET_PATH_SECTION,
            rawText: "",
            message: `Section '## ${exports.DECLARED_TARGET_PATH_SECTION}' in '${options.sourceArtifactPath}' has no declared target path entries.`
        });
    }
    return evidence;
}
function resolveDeclaredTargetPathRole(evidence, candidatePath) {
    const matchingTargets = evidence
        .filter((entry) => entry.state === "explicit"
        && entry.path !== undefined
        && (0, paths_1.pathsOverlap)(candidatePath, entry.path))
        .sort((left, right) => right.path.length - left.path.length);
    return matchingTargets[0]?.role ?? null;
}
function explicitTargetPathUnion(evidence) {
    const explicitPaths = [];
    const seenPaths = new Set();
    for (const entry of evidence) {
        if (entry.state !== "explicit" || entry.path === undefined || seenPaths.has(entry.path)) {
            continue;
        }
        seenPaths.add(entry.path);
        explicitPaths.push(entry.path);
    }
    return explicitPaths;
}
function unwrapSingleCodeSpan(value) {
    const match = value.match(/^`([^`]+)`$/);
    return match === null ? value : match[1].trim();
}
function parseDeclaredTargetPathItem(rawItem) {
    const roleMarkers = rawItem.match(/\{role(?:=|:)/g) ?? [];
    if (roleMarkers.length > 1) {
        return {
            pathText: rawItem,
            role: null,
            issue: `Declared target path entry '${rawItem}' has conflicting role metadata; use exactly one optional '{role=runtime}' or '{role=documentation}' suffix`
        };
    }
    const roleSuffix = rawItem.match(/^(.*?)\s+\{role=([^{}\s]+)\}$/);
    if (roleMarkers.length === 1 && roleSuffix === null) {
        return {
            pathText: rawItem,
            role: null,
            issue: `Declared target path entry '${rawItem}' has malformed role metadata; use exactly one optional '{role=runtime}' or '{role=documentation}' suffix`
        };
    }
    if (roleSuffix === null) {
        return {
            pathText: rawItem,
            role: null,
            issue: null
        };
    }
    const role = roleSuffix[2];
    if (role !== "runtime" && role !== "documentation") {
        return {
            pathText: roleSuffix[1].trim(),
            role: null,
            issue: `Declared target path entry '${rawItem}' uses unsupported role '${role}'; expected 'runtime' or 'documentation'`
        };
    }
    return {
        pathText: roleSuffix[1].trim(),
        role,
        issue: null
    };
}
function isPlaceholderTargetPath(value) {
    return value.trim() === ""
        || /^none(?: currently)?\.?$/i.test(value)
        || /^pending\b/i.test(value)
        || /^tbd\.?$/i.test(value)
        || /^todo\b/i.test(value)
        || /^placeholder$/i.test(value);
}
function normalizeDeclaredTargetPath(value) {
    if (/[,\s;]/.test(value)) {
        return null;
    }
    try {
        return (0, paths_1.parseRepositoryRelativePath)(value).normalizedPath;
    }
    catch {
        return null;
    }
}
