"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.evaluateValidationLinksResolution = evaluateValidationLinksResolution;
const node_fs_1 = __importDefault(require("node:fs"));
const node_path_1 = __importDefault(require("node:path"));
const output_1 = require("../output");
const paths_1 = require("../paths");
const path_resolution_1 = require("../spec-registry/path-resolution");
function evaluateValidationLinksResolution(discovery) {
    const parsedArtifacts = discovery.artifacts.filter((artifact) => artifact.parseStatus === "parsed");
    const valueResolutions = parsedArtifacts.flatMap((artifact) => artifact.traceFields.flatMap((field) => evaluateTraceFieldValues(discovery.repositoryRoot, artifact, field)));
    const hierarchyChecks = valueResolutions.flatMap((evidence) => evaluateHierarchyCheck(discovery.repositoryRoot, evidence.sourceArtifactPath, evidence.family, evidence.field, evidence.relationship, evidence.originalValue));
    const pairingChecks = parsedArtifacts.flatMap((artifact) => evaluatePairingCheck(discovery.repositoryRoot, artifact));
    return {
        localOnly: true,
        readOnly: true,
        traversalBoundaries: output_1.TRACE_LINK_TRAVERSAL_BOUNDARIES,
        valueResolutions,
        hierarchyChecks,
        pairingChecks,
        summary: summarizeResolution(valueResolutions, hierarchyChecks, pairingChecks)
    };
}
function evaluateTraceFieldValues(repositoryRoot, artifact, field) {
    const specPathLookup = field.valueShape === "multi-spec-list"
        ? readSpecPathLookup(repositoryRoot)
        : new Map();
    return field.values.flatMap((value) => {
        const traceValues = extractTraceValues(value, field.valueShape);
        const sourceText = node_fs_1.default.readFileSync(node_path_1.default.join(repositoryRoot, stripFragment(artifact.path)), "utf8");
        return traceValues.map((traceValue) => {
            const resolvableTraceValue = resolveTraceValueForLocalLinksCheck(field, value, traceValue, specPathLookup, artifact.path);
            const resolution = resolvableTraceValue !== null
                ? (0, output_1.resolveLocalTraceLinkValue)({
                    repositoryRoot,
                    value: resolvableTraceValue,
                    traversalBoundaries: output_1.TRACE_LINK_TRAVERSAL_BOUNDARIES
                })
                : {
                    status: "unsupported-external-reference",
                    originalValue: traceValue,
                    message: "Trace value is an explicit non-local source reference outside local links-check coverage."
                };
            return {
                sourceArtifactPath: artifact.path,
                family: artifact.family,
                field: field.field,
                relationship: field.relationship,
                requirement: field.requirement,
                valueShape: field.valueShape,
                originalValue: traceValue,
                resolution,
                severity: classifyValueResolutionSeverity({
                    requirement: field.requirement,
                    status: resolution.status,
                    field,
                    normalizedPath: resolution.normalizedPath,
                    sourceText
                }),
                noWriteRequired: true
            };
        });
    });
}
function extractTraceValues(value, valueShape) {
    if (typeof value === "string") {
        return [value];
    }
    if (valueShape === "multi-spec-list" && isRecord(value)) {
        const ref = value.ref;
        return typeof ref === "string" && ref !== "" ? [ref] : [];
    }
    return [JSON.stringify(value)];
}
function resolveTraceValueForLocalLinksCheck(field, value, traceValue, specPathLookup, sourceArtifactPath) {
    if (field.valueShape === "multi-spec-list" && isRecord(value)) {
        const sourceType = value.source_type ?? value.sourceType;
        if (sourceType === undefined || sourceType === "markdown") {
            return traceValue;
        }
        const id = value.id;
        if (typeof id === "string" && id.trim() !== "") {
            return specPathLookup.get(id) ?? null;
        }
        return null;
    }
    return shouldResolveAsLocalPath(field.valueShape)
        ? resolveChildOverviewTraceValue(field.field, traceValue, sourceArtifactPath)
        : null;
}
function shouldResolveAsLocalPath(valueShape) {
    return valueShape === "single-local-path" ||
        valueShape === "multi-spec-list" ||
        valueShape === "body-reference-list";
}
function resolveChildOverviewTraceValue(fieldPath, traceValue, sourceArtifactPath) {
    if (isRepositoryLocalTraceValue(traceValue)) {
        return traceValue;
    }
    const sourceDirectory = node_path_1.default.posix.dirname(stripFragment(sourceArtifactPath));
    if (fieldPath === "body.Phases") {
        return node_path_1.default.posix.join(sourceDirectory, "phases", traceValue, "phase-overview.md");
    }
    if (fieldPath === "body.Milestones") {
        return node_path_1.default.posix.join(sourceDirectory, "milestones", traceValue, "milestone-overview.md");
    }
    if (fieldPath === "body.Tasks") {
        return node_path_1.default.posix.join(sourceDirectory, "tasks", traceValue, "task-definition.md");
    }
    return traceValue;
}
function isRepositoryLocalTraceValue(value) {
    try {
        return (0, paths_1.parseRepositoryRelativePath)(value).segments.length > 1;
    }
    catch {
        return false;
    }
}
function readSpecPathLookup(repositoryRoot) {
    const specIndexPath = (0, path_resolution_1.resolveSpecIndexAbsolutePath)(repositoryRoot);
    if (!node_fs_1.default.existsSync(specIndexPath)) {
        return new Map();
    }
    try {
        const parsed = JSON.parse(node_fs_1.default.readFileSync(specIndexPath, "utf8"));
        if (!isRecord(parsed) || !Array.isArray(parsed.specs)) {
            return new Map();
        }
        const lookup = new Map();
        for (const entry of parsed.specs) {
            if (!isRecord(entry)) {
                continue;
            }
            const id = entry.id;
            const specPath = entry.spec_path;
            if (typeof id === "string" && id.trim() !== "" && typeof specPath === "string" && specPath.trim() !== "") {
                lookup.set(id, specPath);
            }
        }
        return lookup;
    }
    catch {
        return new Map();
    }
}
function classifyValueResolutionSeverity(options) {
    if (options.status === "resolved-local-target") {
        return "none";
    }
    if (options.status === "unsupported-external-reference") {
        return "advisory";
    }
    if (options.status === "missing-local-target" &&
        options.normalizedPath !== undefined &&
        hasRetiredTargetEvidence(options.sourceText, options.normalizedPath)) {
        return "advisory";
    }
    return options.requirement === "required" ? "blocking" : "advisory";
}
function hasRetiredTargetEvidence(sourceText, normalizedPath) {
    const pathPattern = escapeRegExp(normalizedPath);
    const retiredNearPath = new RegExp(`(?:deleted|retired|intentionally retired|migrated and retired)[^\\n]{0,120}\`${pathPattern}\`|\`${pathPattern}\`[^\\n]{0,120}(?:deleted|retired|intentionally retired|migrated and retired)`, "iu");
    return retiredNearPath.test(sourceText);
}
function escapeRegExp(value) {
    return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
}
function evaluateHierarchyCheck(repositoryRoot, sourceArtifactPath, family, field, relationship, actualValue) {
    const expectedPath = expectedExplicitTargetPath(repositoryRoot, sourceArtifactPath, family, field);
    if (expectedPath === null) {
        return [];
    }
    let comparison;
    try {
        comparison = (0, output_1.compareExplicitTraceLinkTarget)({
            relationship,
            sourceArtifactPath: stripFragment(sourceArtifactPath),
            field,
            actualValue,
            expectedPath,
            mismatchStatus: field === "upstream.task_definition" ? "stale-link" : "hierarchy-mismatch"
        });
    }
    catch (error) {
        comparison = {
            status: field === "upstream.task_definition" ? "stale-link" : "hierarchy-mismatch",
            relationship,
            sourceArtifactPath,
            field,
            originalValue: actualValue,
            normalizedActualPath: actualValue,
            normalizedExpectedPath: expectedPath,
            message: error instanceof Error ? error.message : "Explicit trace-link metadata could not be compared."
        };
    }
    return [
        {
            sourceArtifactPath,
            family,
            field,
            relationship,
            expectedPath,
            comparison,
            severity: comparison.status === "hierarchy-match" ? "none" : "blocking",
            noWriteRequired: true
        }
    ];
}
function expectedExplicitTargetPath(repositoryRoot, sourceArtifactPath, family, field) {
    const parsedPath = (0, paths_1.parseRepositoryRelativePath)(stripFragment(sourceArtifactPath)).normalizedPath;
    const segments = parsedPath.split("/");
    if (segments.length < 2) {
        return null;
    }
    const phasesIndex = segments.indexOf("phases");
    const milestonesIndex = segments.indexOf("milestones");
    const tasksIndex = segments.indexOf("tasks");
    if (field === "upstream.slice_overview" && phasesIndex >= 0) {
        return expectedPlanningOverviewPath(repositoryRoot, "slice", segments.slice(0, phasesIndex).join("/"));
    }
    if (field === "upstream.phase_overview") {
        if (phasesIndex >= 0 && segments.length > phasesIndex + 1) {
            return expectedPlanningOverviewPath(repositoryRoot, "phase", segments.slice(0, phasesIndex + 2).join("/"));
        }
    }
    if (field === "upstream.milestone_overview") {
        if (milestonesIndex >= 0 && segments.length > milestonesIndex + 1) {
            return expectedPlanningOverviewPath(repositoryRoot, "milestone", segments.slice(0, milestonesIndex + 2).join("/"));
        }
    }
    if (field === "upstream.slice_overview" && tasksIndex >= 0) {
        return expectedPlanningOverviewPath(repositoryRoot, "slice", segments.slice(0, tasksIndex).join("/"));
    }
    if (family === "implementation_summary" && field === "upstream.task_definition") {
        return node_path_1.default.posix.join(node_path_1.default.posix.dirname(parsedPath), "task-definition.md");
    }
    return null;
}
function expectedPlanningOverviewPath(repositoryRoot, layer, directoryPath) {
    const resolution = (0, paths_1.resolvePlanningOverviewPath)(repositoryRoot, {
        layer,
        directoryPath
    });
    return resolution.selected?.path ?? (0, paths_1.composeCanonicalPlanningOverviewPath)(layer, directoryPath);
}
function evaluatePairingCheck(repositoryRoot, artifact) {
    if (artifact.family === "task_definition") {
        const expectedPath = node_path_1.default.posix.join(node_path_1.default.posix.dirname(stripFragment(artifact.path)), "implementation-summary.md");
        return [
            createPairingEvidence({
                repositoryRoot,
                sourceArtifactPath: artifact.path,
                family: "task_definition",
                relationship: "task-definition-to-implementation-summary",
                expectedPath
            })
        ];
    }
    if (artifact.family === "implementation_summary") {
        const expectedPath = node_path_1.default.posix.join(node_path_1.default.posix.dirname(stripFragment(artifact.path)), "task-definition.md");
        return [
            createPairingEvidence({
                repositoryRoot,
                sourceArtifactPath: artifact.path,
                family: "implementation_summary",
                relationship: "implementation-summary-to-task-definition",
                expectedPath
            })
        ];
    }
    return [];
}
function createPairingEvidence(options) {
    const expectedAbsolutePath = node_path_1.default.resolve(options.repositoryRoot, options.expectedPath);
    const present = node_fs_1.default.existsSync(expectedAbsolutePath);
    return {
        sourceArtifactPath: options.sourceArtifactPath,
        family: options.family,
        relationship: options.relationship,
        expectedPath: options.expectedPath,
        status: present ? "paired-artifact-present" : "missing-paired-artifact",
        severity: present ? "none" : "blocking",
        message: present
            ? "Expected paired task artifact exists at the sibling path."
            : "Expected paired task artifact is missing from the sibling path.",
        noWriteRequired: true
    };
}
function summarizeResolution(valueResolutions, hierarchyChecks, pairingChecks) {
    return {
        valueResolutionCount: valueResolutions.length,
        resolvedLocalTargetCount: countValueStatus(valueResolutions, "resolved-local-target"),
        missingLocalTargetCount: countValueStatus(valueResolutions, "missing-local-target"),
        malformedLocalLinkCount: countValueStatus(valueResolutions, "malformed-local-link"),
        outsideTraversalBoundaryCount: countValueStatus(valueResolutions, "outside-traversal-boundary"),
        unsupportedReferenceCount: countValueStatus(valueResolutions, "unsupported-external-reference"),
        hierarchyCheckCount: hierarchyChecks.length,
        hierarchyMismatchCount: hierarchyChecks.filter((check) => check.comparison.status === "hierarchy-mismatch").length,
        staleLinkCount: hierarchyChecks.filter((check) => check.comparison.status === "stale-link").length,
        pairingCheckCount: pairingChecks.length,
        pairingMismatchCount: pairingChecks.filter((check) => check.status !== "paired-artifact-present").length,
        blockingEvidenceCount: countSeverity(valueResolutions, "blocking") +
            countSeverity(hierarchyChecks, "blocking") +
            countSeverity(pairingChecks, "blocking"),
        advisoryEvidenceCount: countSeverity(valueResolutions, "advisory") +
            countSeverity(hierarchyChecks, "advisory") +
            countSeverity(pairingChecks, "advisory")
    };
}
function countValueStatus(valueResolutions, status) {
    return valueResolutions.filter((evidence) => evidence.resolution.status === status).length;
}
function countSeverity(evidence, severity) {
    return evidence.filter((entry) => entry.severity === severity).length;
}
function stripFragment(repositoryPath) {
    return repositoryPath.split("#", 1)[0];
}
function isRecord(value) {
    return typeof value === "object" && value !== null && !Array.isArray(value);
}
