"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");
const ordered_root_identity_1 = require("./ordered-root-identity");
const validation_links_discovery_1 = require("./validation-links-discovery");
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);
        return traceValues.map((traceValue) => {
            const structuredTaskDeclaration = field.structuredTaskDeclarations?.find((declaration) => declaration.referenceValue === traceValue);
            const resolution = resolveTraceValueForLinksCheck({
                repositoryRoot,
                field,
                value,
                traceValue,
                specPathLookup,
                sourceArtifactPath: artifact.path,
                artifact,
                structuredTaskDeclaration,
            });
            return {
                sourceArtifactPath: artifact.path,
                family: artifact.family,
                field: field.field,
                relationship: field.relationship,
                requirement: field.requirement,
                valueShape: field.valueShape,
                materializationPolicy: field.materializationPolicy,
                missingTargetPolicy: field.missingTargetPolicy,
                originalValue: traceValue,
                ...(structuredTaskDeclaration === undefined ? {} : { structuredTaskDeclaration }),
                resolution,
                severity: classifyValueResolutionSeverity({
                    status: resolution.status,
                    field
                }),
                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 resolveTraceValueForLinksCheck(options) {
    const sourceType = readTraceSourceType(options.field, options.value);
    let localValue = null;
    if (options.structuredTaskDeclaration?.mappingStatus === 'invalid') {
        return {
            status: 'malformed-local-link',
            originalValue: options.traceValue,
            message: options.structuredTaskDeclaration.issues.map((issue) => issue.message).join(' '),
        };
    }
    if (options.field.field === 'body.Task Plan'
        && options.structuredTaskDeclaration?.mappingStatus === 'valid'
        && options.structuredTaskDeclaration.orderedTaskRootName !== null) {
        localValue = options.structuredTaskDeclaration.referenceKind === 'markdown-link'
            ? resolveChildOverviewTraceValue(options.repositoryRoot, options.field.field, options.traceValue, options.sourceArtifactPath, options.artifact)
            : node_path_1.default.posix.join(node_path_1.default.posix.dirname(stripFragment(options.sourceArtifactPath)), 'tasks', options.structuredTaskDeclaration.orderedTaskRootName, 'task-definition.md');
    }
    if (localValue !== null) {
        // Canonical Structured Task Plan mappings resolve before generic body references.
    }
    else if (options.field.valueShape === "multi-spec-list" && isRecord(options.value)) {
        if (sourceType === undefined || sourceType === "markdown") {
            localValue = options.traceValue;
        }
        else {
            const id = options.value.id;
            if (typeof id === "string" && id.trim() !== "") {
                localValue = options.specPathLookup.get(id) ?? null;
            }
            if (localValue === null && sourceType === "message") {
                return createResolvedNonLocalSource(options.traceValue, sourceType);
            }
        }
    }
    else if (options.field.valueShape === "source-reference") {
        if (sourceType === "markdown") {
            localValue = options.traceValue;
        }
        else if (sourceType === "message") {
            return createResolvedNonLocalSource(options.traceValue, sourceType);
        }
    }
    else if (shouldResolveAsLocalPath(options.field.valueShape)) {
        if (isExplicitArtifactRelativeTraceValue(options.traceValue)) {
            return finalizeLocalTraceResolution(options.field, options.traceValue, resolveExplicitArtifactRelativeTraceValue({
                repositoryRoot: options.repositoryRoot,
                sourceArtifactPath: options.sourceArtifactPath,
                traceValue: options.traceValue
            }));
        }
        localValue = resolveChildOverviewTraceValue(options.repositoryRoot, options.field.field, options.traceValue, options.sourceArtifactPath, options.artifact);
    }
    if (localValue !== null) {
        return finalizeLocalTraceResolution(options.field, options.traceValue, (0, output_1.resolveLocalTraceLinkValue)({
            repositoryRoot: options.repositoryRoot,
            value: localValue,
            traversalBoundaries: output_1.TRACE_LINK_TRAVERSAL_BOUNDARIES
        }), options.structuredTaskDeclaration);
    }
    return {
        status: "unsupported-external-reference",
        originalValue: options.traceValue,
        message: sourceType === undefined
            ? "Trace value does not declare a supported local or non-local source type."
            : `Trace value declares unsupported source type '${sourceType}'.`
    };
}
function finalizeLocalTraceResolution(field, originalValue, resolution, structuredTaskDeclaration) {
    if (resolution.status === "missing-local-target"
        && (field.missingTargetPolicy === "planned-target"
            || structuredTaskDeclaration?.mappingStatus === 'valid')) {
        return {
            ...resolution,
            status: "planned-local-target",
            originalValue,
            message: structuredTaskDeclaration?.mappingStatus === 'valid'
                ? `Structured task '${structuredTaskDeclaration.taskId}' is declared as '${structuredTaskDeclaration.orderedTaskRootName}' but its canonical pair has not been created yet.`
                : "Implementation target is a valid planned repository path and is intentionally absent before implementation."
        };
    }
    return {
        ...resolution,
        originalValue
    };
}
function resolveExplicitArtifactRelativeTraceValue(options) {
    const sourceDirectory = node_path_1.default.posix.dirname(stripFragment(options.sourceArtifactPath));
    const normalizedPath = node_path_1.default.posix.normalize(node_path_1.default.posix.join(sourceDirectory, stripFragment(options.traceValue.replace(/\\/gu, "/"))));
    if (normalizedPath === "."
        || normalizedPath === ".."
        || normalizedPath.startsWith("../")
        || node_path_1.default.posix.isAbsolute(normalizedPath)) {
        return {
            status: "outside-traversal-boundary",
            originalValue: options.traceValue,
            normalizedPath,
            message: "Explicit artifact-relative trace value resolves outside the repository boundary."
        };
    }
    return (0, output_1.resolveLocalTraceLinkValue)({
        repositoryRoot: options.repositoryRoot,
        value: normalizedPath,
        traversalBoundaries: output_1.TRACE_LINK_TRAVERSAL_BOUNDARIES
    });
}
function isExplicitArtifactRelativeTraceValue(value) {
    const normalizedSeparators = value.replace(/\\/gu, "/");
    return normalizedSeparators.startsWith("./") || normalizedSeparators.startsWith("../");
}
function readTraceSourceType(field, value) {
    const valueSourceType = isRecord(value)
        ? value.source_type ?? value.sourceType
        : undefined;
    const sourceType = valueSourceType ?? field.sourceType;
    return typeof sourceType === "string" && sourceType.trim() !== ""
        ? sourceType.trim().toLowerCase()
        : undefined;
}
function createResolvedNonLocalSource(traceValue, sourceType) {
    return {
        status: "resolved-non-local-source",
        originalValue: traceValue,
        message: `Trace value is supported non-local '${sourceType}' source provenance and requires no local target.`
    };
}
function shouldResolveAsLocalPath(valueShape) {
    return valueShape === "single-local-path" ||
        valueShape === "multi-spec-list" ||
        valueShape === "body-reference-list";
}
function resolveChildOverviewTraceValue(repositoryRoot, fieldPath, traceValue, sourceArtifactPath, artifact) {
    const placeholderField = fieldPath === "body.Navigation"
        ? (0, validation_links_discovery_1.navigationPlaceholderUpstreamField)(traceValue)
        : null;
    if (placeholderField !== null) {
        const placeholderValue = artifact.traceFields
            .find((field) => field.field === placeholderField)
            ?.values.find((value) => typeof value === "string" && value.trim() !== "");
        if (placeholderValue !== undefined) {
            return placeholderValue;
        }
    }
    if (isRootedRepositoryLocalTraceValue(repositoryRoot, traceValue)) {
        return traceValue;
    }
    const sourceDirectory = node_path_1.default.posix.dirname(stripFragment(sourceArtifactPath));
    if (traceValue.includes("/")) {
        return node_path_1.default.posix.normalize(node_path_1.default.posix.join(sourceDirectory, stripFragment(traceValue)));
    }
    if (fieldPath === "body.Task Plan") {
        return resolveStructuredTaskTracePath(repositoryRoot, sourceDirectory, traceValue);
    }
    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 resolveStructuredTaskTracePath(repositoryRoot, workRootPath, taskIdentityOrBasename) {
    const collectionPath = node_path_1.default.posix.join(workRootPath, "tasks");
    const directPath = node_path_1.default.posix.join(collectionPath, taskIdentityOrBasename, "task-definition.md");
    const directAbsolute = node_path_1.default.resolve(repositoryRoot, directPath);
    if (node_fs_1.default.existsSync(directAbsolute) && node_fs_1.default.statSync(directAbsolute).isFile())
        return directPath;
    try {
        const inventory = (0, ordered_root_identity_1.inspectStructuredTaskCollectionSync)({ repositoryRoot, collectionPath });
        const matches = inventory.entries.filter((entry) => entry.taskId === taskIdentityOrBasename);
        return matches.length === 1 ? matches[0].taskDefinitionPath : directPath;
    }
    catch {
        return directPath;
    }
}
function isRootedRepositoryLocalTraceValue(repositoryRoot, value) {
    try {
        const parsed = (0, paths_1.parseRepositoryRelativePath)(value);
        if (parsed.segments[0] === ".day-shift"
            || parsed.segments[0] === "src"
            || parsed.segments[0] === "tests"
            || parsed.segments[0] === "tests_v2") {
            return true;
        }
        const firstSegmentPath = node_path_1.default.join(repositoryRoot, parsed.segments[0]);
        return node_fs_1.default.existsSync(firstSegmentPath) && node_fs_1.default.statSync(firstSegmentPath).isDirectory();
    }
    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" || options.status === "resolved-non-local-source") {
        return "none";
    }
    if (options.status === "planned-local-target") {
        return "advisory";
    }
    if (options.status === "unsupported-external-reference") {
        return "advisory";
    }
    return options.field.missingTargetPolicy === "required" ? "blocking" : "advisory";
}
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"),
        resolvedNonLocalSourceCount: countValueStatus(valueResolutions, "resolved-non-local-source"),
        missingLocalTargetCount: countValueStatus(valueResolutions, "missing-local-target"),
        malformedLocalLinkCount: countValueStatus(valueResolutions, "malformed-local-link"),
        outsideTraversalBoundaryCount: countValueStatus(valueResolutions, "outside-traversal-boundary"),
        unsupportedReferenceCount: countValueStatus(valueResolutions, "unsupported-external-reference"),
        plannedTargetCount: countValueStatus(valueResolutions, "planned-local-target"),
        requiredTargetCount: valueResolutions.filter((entry) => entry.missingTargetPolicy === "required").length,
        unresolvedRequiredTargetCount: valueResolutions.filter((entry) => entry.missingTargetPolicy === "required"
            && entry.resolution.status !== "resolved-local-target"
            && entry.resolution.status !== "resolved-non-local-source"
            && entry.resolution.status !== "planned-local-target").length,
        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"),
        dispositionLifecycleEvidenceCount: valueResolutions.filter((entry) => entry.severity === "none" &&
            (entry.disposition?.status === "retired" || entry.disposition?.status === "deleted")).length
    };
}
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);
}
