"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.IMPACT_CHANGED_PATH_NO_GIT_DISCOVERY_BOUNDARY = exports.IMPACT_CHANGED_PATH_INPUT_SOURCE = void 0;
exports.associateImpactChangedPathsWithArtifacts = associateImpactChangedPathsWithArtifacts;
exports.normalizeImpactChangedPathInputs = normalizeImpactChangedPathInputs;
const changed_paths_1 = require("./changed-paths");
const scope_guard_1 = require("./scope-guard");
exports.IMPACT_CHANGED_PATH_INPUT_SOURCE = changed_paths_1.CHANGED_PATH_INPUT_SOURCE;
exports.IMPACT_CHANGED_PATH_NO_GIT_DISCOVERY_BOUNDARY = changed_paths_1.CHANGED_PATH_NO_GIT_DISCOVERY_BOUNDARY;
function associateImpactChangedPathsWithArtifacts(input) {
    const normalization = (0, changed_paths_1.normalizeChangedPathInputs)(input.changedPaths);
    const candidates = collectImpactPathCandidates(input.artifactEvidence);
    const associations = [];
    const matchedChangedPaths = new Set();
    for (const changedPath of normalization.normalizedChangedPaths) {
        for (const candidate of candidates) {
            const relationship = getPathRelationship(changedPath, candidate.matchedPath);
            if (relationship === null) {
                continue;
            }
            matchedChangedPaths.add(changedPath);
            associations.push({
                changedPath,
                inputSource: changed_paths_1.CHANGED_PATH_INPUT_SOURCE,
                source: candidate.source,
                relationship,
                matchedPath: candidate.matchedPath,
                sourceArtifactPath: candidate.sourceArtifactPath,
                artifactFamily: candidate.artifactFamily,
                sourceField: candidate.sourceField,
                sourceSection: candidate.sourceSection,
                message: buildAssociationMessage(changedPath, candidate, relationship)
            });
        }
    }
    return {
        normalizedChangedPaths: normalization.normalizedChangedPaths,
        changedPathEvidence: normalization.evidence,
        invalidChangedPathEvidence: normalization.evidence.filter((entry) => entry.state === "invalid-input"),
        duplicateChangedPathEvidence: normalization.evidence.filter((entry) => entry.state === "duplicate"),
        associations,
        unmatchedChangedPaths: normalization.normalizedChangedPaths.filter((changedPath) => !matchedChangedPaths.has(changedPath)),
        inputSource: normalization.inputSource,
        gitDiscoveryPerformed: normalization.gitDiscoveryPerformed,
        noGitDiscoveryBoundary: normalization.noGitDiscoveryBoundary,
        readOnly: true
    };
}
function normalizeImpactChangedPathInputs(changedPaths) {
    return (0, changed_paths_1.normalizeChangedPathInputs)(changedPaths);
}
function collectImpactPathCandidates(artifactEvidence) {
    const candidates = [];
    const seen = new Set();
    for (const artifact of artifactEvidence) {
        addCandidate(candidates, seen, {
            source: "artifact-path",
            matchedPath: artifact.artifactPath,
            sourceArtifactPath: artifact.artifactPath,
            artifactFamily: artifact.artifactFamily,
            sourceField: "artifact_path"
        });
        for (const metadata of artifact.metadataEvidence) {
            collectMetadataCandidate(candidates, seen, artifact, metadata);
        }
        for (const specReference of artifact.specReferenceEvidence) {
            collectSpecReferenceCandidate(candidates, seen, artifact, specReference);
        }
        for (const section of artifact.bodySectionEvidence) {
            collectBodySectionCandidates(candidates, seen, artifact, section);
        }
    }
    return candidates;
}
function collectMetadataCandidate(candidates, seen, artifact, metadata) {
    if (metadata.value === undefined || metadata.state !== "explicit") {
        return;
    }
    if (!metadata.field.startsWith("upstream.")) {
        return;
    }
    addCandidate(candidates, seen, {
        source: "upstream-trace",
        matchedPath: metadata.value,
        sourceArtifactPath: artifact.artifactPath,
        artifactFamily: artifact.artifactFamily,
        sourceField: metadata.field
    });
}
function collectSpecReferenceCandidate(candidates, seen, artifact, specReference) {
    if (specReference.ref === undefined || specReference.state !== "explicit") {
        return;
    }
    addCandidate(candidates, seen, {
        source: "upstream-trace",
        matchedPath: specReference.ref,
        sourceArtifactPath: artifact.artifactPath,
        artifactFamily: artifact.artifactFamily,
        sourceField: "upstream.specs"
    });
}
function collectBodySectionCandidates(candidates, seen, artifact, section) {
    if (section.state !== "explicit") {
        return;
    }
    for (const extractedPath of section.extractedPaths) {
        addCandidate(candidates, seen, {
            source: sourceForBodySection(artifact.artifactFamily, section.sectionName),
            matchedPath: extractedPath,
            sourceArtifactPath: artifact.artifactPath,
            artifactFamily: artifact.artifactFamily,
            sourceSection: section.sectionName
        });
    }
}
function sourceForBodySection(family, sectionName) {
    if (family === "task_definition" && sectionName === "Implementation Target Paths") {
        return "task-target-path";
    }
    if (family === "implementation_summary" && sectionName === "Changed Paths") {
        return "implementation-summary-changed-path";
    }
    if (family === "reconciliation") {
        return "reconciliation-reference";
    }
    return "body-reference";
}
function addCandidate(candidates, seen, candidate) {
    const key = [
        candidate.source,
        candidate.matchedPath,
        candidate.sourceArtifactPath,
        candidate.sourceField ?? "",
        candidate.sourceSection ?? ""
    ].join("\0");
    if (seen.has(key)) {
        return;
    }
    seen.add(key);
    candidates.push(candidate);
}
function getPathRelationship(changedPath, evidencePath) {
    if (changedPath === evidencePath) {
        return "exact";
    }
    if ((0, scope_guard_1.isRequestedPathWithinDeclaredTarget)(changedPath, evidencePath)) {
        return "path-prefix";
    }
    return null;
}
function buildAssociationMessage(changedPath, candidate, relationship) {
    const sourceLocation = candidate.sourceField !== undefined
        ? candidate.sourceField
        : `## ${candidate.sourceSection ?? "unknown"}`;
    return `Caller-provided changed path '${changedPath}' has a ${relationship} relationship with ${candidate.source} evidence '${candidate.matchedPath}' from '${candidate.sourceArtifactPath}' (${sourceLocation}).`;
}
