"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.readMilestoneTopLevelEvidence = readMilestoneTopLevelEvidence;
exports.readNestedMilestoneTasks = readNestedMilestoneTasks;
exports.singleSemanticValue = singleSemanticValue;
exports.semanticValuesPreservingDelimiters = semanticValuesPreservingDelimiters;
exports.semanticListValues = semanticListValues;
exports.parseAuthoredTask = parseAuthoredTask;
exports.taskEvidenceFromMilestone = taskEvidenceFromMilestone;
exports.inspectTaskCriterionOwnership = inspectTaskCriterionOwnership;
exports.concreteTaskDescription = concreteTaskDescription;
exports.acceptanceCriterionId = acceptanceCriterionId;
exports.sameOrderedValues = sameOrderedValues;
exports.createPhaseAuthoringFinding = createPhaseAuthoringFinding;
exports.phaseAuthoringErrorFindings = phaseAuthoringErrorFindings;
exports.deduplicatePhaseAuthoringFindings = deduplicatePhaseAuthoringFindings;
exports.isRegularRepositoryFile = isRegularRepositoryFile;
exports.asRecord = asRecord;
exports.phaseAuthoringReceiptError = phaseAuthoringReceiptError;
exports.uniqueStrings = uniqueStrings;
const node_fs_1 = __importDefault(require("node:fs"));
const node_path_1 = __importDefault(require("node:path"));
const errors_1 = require("../../errors");
const output_1 = require("../../output");
const paths_1 = require("../../paths");
const authoring_policy_1 = require("../authoring-policy");
const scaffold_target_path_policy_1 = require("../scaffold-target-path-policy");
const acceptance_criterion_label_1 = require("../acceptance-criterion-label");
function readMilestoneTopLevelEvidence(markdownText, milestoneSlug) {
    const lines = markdownText.split(/\r?\n/u);
    const sectionStart = lines.findIndex((line) => line.trim() === "## Scaffold Evidence");
    if (sectionStart === -1)
        return [];
    const scoped = [];
    let active = false;
    for (let index = sectionStart + 1; index < lines.length; index += 1) {
        const line = lines[index];
        if (/^##\s+/u.test(line))
            break;
        const scope = line.trim().match(/^-\s+(?:`([^`]+)`|\[([^\]]+)\])\s*:\s*(.*)$/u);
        if (scope !== null) {
            active = (scope[1] ?? scope[2]) === milestoneSlug;
            if (active && scope[3] !== "")
                scoped.push(scope[3]);
            continue;
        }
        if (active && (/^\s{2}-\s+/u.test(line) || /^\s{2}[A-Za-z0-9_-]+:/u.test(line))) {
            scoped.push(line.trim().replace(/^-\s+/u, ""));
            continue;
        }
        if (line.trim() !== "")
            active = false;
    }
    return scoped;
}
function readNestedMilestoneTasks(markdownText, milestoneSlug, issues, runtimeEvidencePathPolicy = {}) {
    const lines = markdownText.split(/\r?\n/u);
    const sectionStart = lines.findIndex((line) => line.trim() === "## Scaffold Evidence");
    if (sectionStart === -1)
        return [];
    const tasks = [];
    let milestoneActive = false;
    let currentTask = null;
    const flushTask = () => {
        if (currentTask === null)
            return;
        const values = (key) => semanticValuesPreservingDelimiters(currentTask?.lines ?? [], key);
        const lists = (key) => semanticListValues(currentTask?.lines ?? [], key);
        const desiredBehavior = values("desired_behavior")[0]
            ?? currentTask.description;
        let candidate = {
            slug: currentTask.slug,
            description: currentTask.description,
            criterionIds: lists("criterion_ids"),
            sharedCriterionIds: lists("shared_criterion_ids"),
            acceptanceCriteria: values("acceptance"),
            currentBehavior: values("current_behavior")[0] ?? null,
            desiredBehavior,
            ownership: values("ownership"),
            dependencies: values("dependency"),
            targetPaths: values("target"),
            adapterPaths: values("adapter"),
            validationCommands: values("validation"),
            rollback: values("rollback")
        };
        const adapterClassification = (0, authoring_policy_1.classifyScaffoldAdapterRequirement)({
            prose: [
                candidate.description,
                ...candidate.acceptanceCriteria,
                candidate.currentBehavior ?? "",
                candidate.desiredBehavior,
                ...candidate.ownership
            ],
            targetPaths: candidate.targetPaths,
            explicitAdapterPaths: candidate.adapterPaths
        });
        candidate = {
            ...candidate,
            adapterPaths: adapterClassification.adapterPaths
        };
        const missing = [
            candidate.currentBehavior === null ? "current_behavior" : null,
            candidate.desiredBehavior.trim() === "" ? "desired_behavior" : null,
            candidate.dependencies.length === 0 ? "dependency" : null,
            candidate.targetPaths.length === 0 ? "target" : null,
            candidate.validationCommands.length === 0 ? "validation" : null,
            candidate.rollback.length === 0 ? "rollback" : null
        ].filter((entry) => entry !== null);
        if (missing.length > 0) {
            issues.push(`Milestone '${milestoneSlug}' task '${candidate.slug}' is missing child-specific ${missing.join(", ")} evidence.`);
        }
        const invalidTargets = candidate.targetPaths.filter((entry) => !(0, scaffold_target_path_policy_1.isPlanningScaffoldTargetPath)(entry, runtimeEvidencePathPolicy));
        if (invalidTargets.length > 0) {
            issues.push(`Milestone '${milestoneSlug}' task '${candidate.slug}' has non-executable target evidence: ${invalidTargets.join(", ")}.`);
        }
        tasks.push(candidate);
        currentTask = null;
    };
    for (let index = sectionStart + 1; index < lines.length; index += 1) {
        const line = lines[index];
        if (/^##\s+/u.test(line))
            break;
        const milestoneScope = line.match(/^-\s+(?:`([^`]+)`|\[([^\]]+)\])\s*:\s*(.*)$/u);
        if (milestoneScope !== null) {
            flushTask();
            milestoneActive = (milestoneScope[1] ?? milestoneScope[2]) === milestoneSlug;
            continue;
        }
        if (!milestoneActive)
            continue;
        const taskMatch = line.match(/^\s{2}-\s+task:\s+`?([A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)`?\s*(?:\||—|--?)\s*(.+?)\s*$/u);
        if (taskMatch !== null) {
            flushTask();
            if (!(0, paths_1.isCanonicalSlug)(taskMatch[1])) {
                issues.push(`Milestone '${milestoneSlug}' task '${taskMatch[1]}' must use a canonical slug.`);
                continue;
            }
            currentTask = {
                slug: taskMatch[1],
                description: taskMatch[2].trim(),
                lines: []
            };
            continue;
        }
        if (currentTask !== null && /^\s{4,}-\s+[A-Za-z0-9_-]+\s*:/u.test(line)) {
            currentTask.lines.push(line.trim().replace(/^-\s+/u, ""));
            continue;
        }
        if (/^\s{2}-\s+/u.test(line))
            flushTask();
    }
    flushTask();
    return tasks;
}
function singleSemanticValue(lines, key) {
    return semanticValuesPreservingDelimiters(lines, key)[0] ?? null;
}
function semanticValuesPreservingDelimiters(lines, key) {
    const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
    return uniqueStrings(lines.flatMap((line) => {
        const match = line.match(new RegExp(`^${escapedKey}\\s*:\\s*(.+)$`, "iu"));
        return match === null ? [] : [match[1].trim()];
    }));
}
function semanticListValues(lines, key) {
    return uniqueStrings(semanticValuesPreservingDelimiters(lines, key)
        .flatMap((value) => value.split(/\s*[;,]\s*/u))
        .map((value) => value.trim().replace(/^`|`$/gu, "").replace(/^\[|\]$/gu, ""))
        .filter((value) => value !== ""));
}
function parseAuthoredTask(value, milestoneSlug, issues) {
    const [rawSlug, ...descriptionParts] = value.split(/\s+\|\s+/u);
    const slug = rawSlug?.trim() ?? "";
    const description = descriptionParts.join(" | ").trim();
    if (!(0, paths_1.isCanonicalSlug)(slug)) {
        issues.push(`Milestone '${milestoneSlug}' task '${slug || value}' must use a canonical slug in 'task: <slug> | <description>'.`);
        return null;
    }
    if (description === "") {
        issues.push(`Milestone '${milestoneSlug}' task '${slug}' is missing its authored description after '|'.`);
        return null;
    }
    return {
        slug,
        description,
        criterionIds: [],
        sharedCriterionIds: [],
        acceptanceCriteria: [],
        currentBehavior: null,
        desiredBehavior: description,
        ownership: [],
        dependencies: [],
        targetPaths: [],
        adapterPaths: [],
        validationCommands: [],
        rollback: []
    };
}
function taskEvidenceFromMilestone(evidence) {
    return {
        criterionIds: [],
        sharedCriterionIds: [],
        acceptanceCriteria: [],
        currentBehavior: null,
        desiredBehavior: "",
        ownership: evidence.ownership,
        dependencies: evidence.dependencies,
        targetPaths: evidence.targetPaths,
        adapterPaths: evidence.adapterPaths,
        validationCommands: evidence.validationCommands,
        rollback: evidence.rollback
    };
}
function inspectTaskCriterionOwnership(options) {
    if (options.milestoneCriterionIds.length === 0)
        return [];
    const issues = [];
    const authoritative = new Set(options.milestoneCriterionIds);
    const orderedUnion = [];
    for (const task of options.tasks) {
        if (task.criterionIds.length === 0) {
            issues.push(`Milestone '${options.milestoneSlug}' task '${task.slug}' must declare a non-empty nested criterion_ids subset.`);
        }
        const duplicates = task.criterionIds.filter((id, index, all) => all.indexOf(id) !== index);
        if (duplicates.length > 0) {
            issues.push(`Milestone '${options.milestoneSlug}' task '${task.slug}' repeats criterion_ids: ${uniqueStrings(duplicates).join(", ")}.`);
        }
        const unknown = task.criterionIds.filter((id) => !authoritative.has(id));
        if (unknown.length > 0) {
            issues.push(`Milestone '${options.milestoneSlug}' task '${task.slug}' owns criterion_ids outside the milestone subset: ${uniqueStrings(unknown).join(", ")}.`);
        }
        const invalidShared = task.sharedCriterionIds.filter((id) => !task.criterionIds.includes(id));
        if (invalidShared.length > 0) {
            issues.push(`Milestone '${options.milestoneSlug}' task '${task.slug}' marks shared criterion IDs it does not own: ${uniqueStrings(invalidShared).join(", ")}.`);
        }
        for (const criterionId of task.criterionIds) {
            if (!orderedUnion.includes(criterionId))
                orderedUnion.push(criterionId);
        }
    }
    const omitted = options.milestoneCriterionIds.filter((id) => !orderedUnion.includes(id));
    if (omitted.length > 0) {
        issues.push(`Milestone '${options.milestoneSlug}' task criterion assignments omit: ${omitted.join(", ")}.`);
    }
    if (!sameOrderedValues(orderedUnion, options.milestoneCriterionIds)) {
        issues.push(`Milestone '${options.milestoneSlug}' ordered task criterion union must preserve: ${options.milestoneCriterionIds.join(", ")}.`);
    }
    for (const criterionId of options.milestoneCriterionIds) {
        const owners = options.tasks.filter((task) => task.criterionIds.includes(criterionId));
        if (owners.length < 2)
            continue;
        const unreviewed = owners.filter((task) => !task.sharedCriterionIds.includes(criterionId));
        if (unreviewed.length > 0) {
            issues.push(`Milestone '${options.milestoneSlug}' criterion '${criterionId}' overlaps across ${owners.map((task) => task.slug).join(", ")} without shared_criterion_ids review from: ${unreviewed.map((task) => task.slug).join(", ")}.`);
        }
    }
    return uniqueStrings(issues);
}
function concreteTaskDescription(milestoneDescription, ownership) {
    const candidate = milestoneDescription.trim() || ownership[0]?.trim() || "";
    return candidate === "" ? null : candidate;
}
function acceptanceCriterionId(value) {
    return (0, acceptance_criterion_label_1.normalizeAcceptanceCriterionId)(value);
}
function sameOrderedValues(left, right) {
    return left.length === right.length
        && left.every((value, index) => value === right[index]);
}
function createPhaseAuthoringFinding(code, message, artifactPath) {
    return (0, output_1.createBlockingValidationFinding)({
        code,
        message,
        path: artifactPath,
        blockingReason: "invalid-command-input",
        recovery: "Revise the selected phase contract, then rerun the read-only phase review.",
        evidence: {
            category: "planning-review",
            details: {
                reviewBoundary: "phase-authoring-readiness",
                requiresRefinement: true,
                noWriteRequired: true
            }
        }
    });
}
function phaseAuthoringErrorFindings(error, artifactPath, fallbackCode) {
    if (error instanceof errors_1.RuntimeError && error.findings.length > 0) {
        return [...error.findings];
    }
    const message = error instanceof errors_1.RuntimeError
        ? [error.message, ...error.details].join(" ")
        : error instanceof Error
            ? error.message
            : String(error);
    return [createPhaseAuthoringFinding(fallbackCode, message, artifactPath)];
}
function deduplicatePhaseAuthoringFindings(findings) {
    const seen = new Set();
    return findings.filter((finding) => {
        const key = JSON.stringify([
            finding.code,
            finding.path ?? finding.artifactPath ?? "",
            finding.message
        ]);
        if (seen.has(key))
            return false;
        seen.add(key);
        return true;
    });
}
function isRegularRepositoryFile(repositoryRoot, repositoryPath) {
    const stats = node_fs_1.default.lstatSync(node_path_1.default.join(repositoryRoot, repositoryPath), {
        throwIfNoEntry: false
    });
    return stats !== undefined && stats.isFile() && !stats.isSymbolicLink();
}
function asRecord(value) {
    return value !== null && typeof value === "object" && !Array.isArray(value)
        ? value
        : null;
}
function phaseAuthoringReceiptError(code, message, findings = []) {
    const failureFindings = findings.length > 0
        ? [...findings]
        : [createPhaseAuthoringFinding(code, message, ".day-shift/planning")];
    return new errors_1.RuntimeError({
        message,
        code,
        exitCode: code.endsWith("_STALE")
            ? errors_1.RuntimeExitCode.STALE_EVIDENCE
            : errors_1.RuntimeExitCode.INVALID_CONFIG,
        details: [
            "No milestone artifact write was attempted.",
            "Run day-shift phase review again after the phase contract is complete, then pass that exact current JSON result."
        ],
        findings: failureFindings
    });
}
function uniqueStrings(values) {
    return [...new Set(values.map((value) => value.trim()).filter((value) => value !== ""))];
}
