"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.analyzeImpact = analyzeImpact;
function sortUnique(values) {
    return [...new Set(values)].sort((left, right) => left.localeCompare(right));
}
function normalizePath(value) {
    return value.replace(/\\/g, '/');
}
function deriveChangedModulesFromFiles(changedFiles, modulePaths) {
    if (changedFiles.length === 0 || modulePaths.length === 0) {
        return [];
    }
    const normalizedFiles = changedFiles.map((filePath) => normalizePath(filePath));
    const sortedModulePaths = [...modulePaths]
        .map((entry) => ({ ...entry, modulePath: normalizePath(entry.modulePath) }))
        .sort((left, right) => {
        const lenCompare = right.modulePath.length - left.modulePath.length;
        if (lenCompare !== 0) {
            return lenCompare;
        }
        return left.moduleName.localeCompare(right.moduleName);
    });
    const resolved = new Set();
    for (const changedFile of normalizedFiles) {
        for (const modulePath of sortedModulePaths) {
            if (changedFile === modulePath.modulePath ||
                changedFile.startsWith(`${modulePath.modulePath}/`)) {
                resolved.add(modulePath.moduleName);
                break;
            }
        }
    }
    return sortUnique(resolved);
}
function resolveImpactLevel(impactRadiusScore) {
    if (impactRadiusScore <= 6) {
        return 'LOW';
    }
    if (impactRadiusScore <= 14) {
        return 'MEDIUM';
    }
    return 'HIGH';
}
function compareChain(left, right) {
    const lengthCompare = left.length - right.length;
    if (lengthCompare !== 0) {
        return lengthCompare;
    }
    return left.join('->').localeCompare(right.join('->'));
}
function isBetterChain(candidate, current) {
    if (!current) {
        return true;
    }
    return compareChain(candidate, current) < 0;
}
function formatChain(chain) {
    return chain.join(' -> ');
}
function deriveDependencyChains(inbound, changedModules) {
    const directChains = new Map();
    const transitiveChains = new Map();
    const changedSet = new Set(changedModules);
    for (const changedModule of changedModules) {
        const minDepthByModule = new Map();
        const queue = [[changedModule]];
        while (queue.length > 0) {
            const currentChain = queue.shift();
            if (!currentChain) {
                continue;
            }
            const currentModule = currentChain[currentChain.length - 1];
            const inboundDependents = sortUnique(inbound.get(currentModule) ?? new Set());
            for (const dependent of inboundDependents) {
                if (changedSet.has(dependent) || currentChain.includes(dependent)) {
                    continue;
                }
                const nextChain = [...currentChain, dependent];
                const depth = nextChain.length - 1;
                const recordedDepth = minDepthByModule.get(dependent);
                if (recordedDepth !== undefined && depth > recordedDepth) {
                    continue;
                }
                if (recordedDepth === undefined || depth < recordedDepth) {
                    minDepthByModule.set(dependent, depth);
                }
                if (depth === 1) {
                    if (isBetterChain(nextChain, directChains.get(dependent))) {
                        directChains.set(dependent, nextChain);
                    }
                }
                else if (!directChains.has(dependent)) {
                    if (isBetterChain(nextChain, transitiveChains.get(dependent))) {
                        transitiveChains.set(dependent, nextChain);
                    }
                }
                queue.push(nextChain);
            }
        }
    }
    for (const directModule of directChains.keys()) {
        transitiveChains.delete(directModule);
    }
    return { directChains, transitiveChains };
}
function buildImpactFactors(input) {
    const factors = [];
    const governanceSurfaceCount = input.governanceSurface.impactedContracts.length +
        input.governanceSurface.impactedPublicEntrypoints.length +
        input.governanceSurface.impactedAdrs.length +
        input.governanceSurface.impactedArchitectureArtifacts.length;
    if (input.changedModules.length > 0) {
        factors.push({
            id: 'IAF-001',
            label: 'Changed modules in scope',
            description: 'Modules directly changed by this diff scope.',
            category: 'scope',
            weight: input.changedModules.length * 2,
            evidence: input.changedModules,
        });
    }
    if (input.directDependencyChains.length > 0) {
        factors.push({
            id: 'IAF-002',
            label: 'Direct dependents impacted',
            description: 'Modules directly depending on changed modules.',
            category: 'dependency',
            weight: input.directDependencyChains.length * 3,
            evidence: input.directDependencyChains.map((entry) => formatChain(entry.chain)),
        });
    }
    if (input.transitiveDependencyChains.length > 0) {
        factors.push({
            id: 'IAF-003',
            label: 'Transitive dependents impacted',
            description: 'Downstream modules impacted through dependent chains.',
            category: 'dependency',
            weight: input.transitiveDependencyChains.length * 2,
            evidence: input.transitiveDependencyChains.map((entry) => formatChain(entry.chain)),
        });
    }
    if (governanceSurfaceCount > 0) {
        factors.push({
            id: 'IAF-004',
            label: 'Governance surface touched',
            description: 'Contracts, entrypoints, ADRs, or architecture artifacts were touched.',
            category: 'governance-surface',
            weight: governanceSurfaceCount * 2,
            evidence: sortUnique([
                ...input.governanceSurface.impactedContracts,
                ...input.governanceSurface.impactedPublicEntrypoints,
                ...input.governanceSurface.impactedAdrs,
                ...input.governanceSurface.impactedArchitectureArtifacts,
            ]),
        });
    }
    return factors.sort((left, right) => left.id.localeCompare(right.id));
}
function analyzeImpact(input) {
    const moduleUniverse = new Set(input.graph.modules);
    const changedFromInput = input.changedModules ?? [];
    const changedFromFiles = deriveChangedModulesFromFiles(input.changedFiles ?? [], input.modulePaths ?? []);
    const changedModules = sortUnique([...changedFromInput, ...changedFromFiles].filter((moduleName) => moduleUniverse.has(moduleName)));
    const affectedSurface = {
        changedModules,
        directlyAffectedModules: [],
        transitivelyAffectedModules: [],
        impactedContracts: sortUnique(input.touchedContracts ?? []),
        impactedPublicEntrypoints: sortUnique(input.touchedPublicEntrypoints ?? []),
        impactedAdrs: sortUnique(input.touchedAdrs ?? []),
        impactedArchitectureArtifacts: sortUnique(input.touchedArchitectureArtifacts ?? []),
    };
    if (changedModules.length === 0) {
        const governanceSurfaceCount = affectedSurface.impactedContracts.length +
            affectedSurface.impactedPublicEntrypoints.length +
            affectedSurface.impactedAdrs.length +
            affectedSurface.impactedArchitectureArtifacts.length;
        const scoring = {
            modelVersion: 1,
            formula: 'score = (changed*2) + (direct*3) + (transitive*2) + (governance*2) + max(0, depth-2)',
            components: {
                changedModules: 0,
                directlyAffectedModules: 0,
                transitivelyAffectedModules: 0,
                governanceSurfaceItems: governanceSurfaceCount,
                maxTransitiveChainDepth: 0,
            },
            computedScore: governanceSurfaceCount * 2,
            thresholds: {
                lowMax: 6,
                mediumMax: 14,
                highMin: 15,
            },
            rationale: [
                'No changed modules were resolved; score reflects governance-surface signals only.',
            ],
        };
        return {
            changedModules: [],
            directDependents: [],
            transitiveDependents: [],
            totalAffectedModules: 0,
            impactRadiusScore: scoring.computedScore,
            impactLevel: resolveImpactLevel(scoring.computedScore),
            summary: governanceSurfaceCount > 0
                ? `No changed modules resolved; governance surfaces touched: ${governanceSurfaceCount}.`
                : 'No changed modules or governance surfaces resolved.',
            affectedSurface,
            directDependencyChains: [],
            transitiveDependencyChains: [],
            factors: buildImpactFactors({
                changedModules: [],
                directDependencyChains: [],
                transitiveDependencyChains: [],
                governanceSurface: affectedSurface,
            }),
            reasoning: [],
            scoring,
        };
    }
    const inbound = new Map();
    for (const moduleName of moduleUniverse) {
        inbound.set(moduleName, new Set());
    }
    for (const edge of input.graph.edges) {
        if (!moduleUniverse.has(edge.source) || !moduleUniverse.has(edge.target)) {
            continue;
        }
        inbound.get(edge.target)?.add(edge.source);
    }
    const { directChains, transitiveChains } = deriveDependencyChains(inbound, changedModules);
    const directDependents = sortUnique(directChains.keys());
    const transitiveDependents = sortUnique(transitiveChains.keys());
    const totalAffectedModules = sortUnique([
        ...directDependents,
        ...transitiveDependents,
    ]).length;
    affectedSurface.directlyAffectedModules = directDependents;
    affectedSurface.transitivelyAffectedModules = transitiveDependents;
    const directDependencyChains = directDependents.map((moduleName) => ({
        module: moduleName,
        chain: directChains.get(moduleName) ?? [moduleName],
    }));
    const transitiveDependencyChains = transitiveDependents.map((moduleName) => ({
        module: moduleName,
        chain: transitiveChains.get(moduleName) ?? [moduleName],
    }));
    const maxTransitiveChainDepth = transitiveDependencyChains.reduce((maxDepth, entry) => Math.max(maxDepth, entry.chain.length - 1), 0);
    const governanceSurfaceCount = affectedSurface.impactedContracts.length +
        affectedSurface.impactedPublicEntrypoints.length +
        affectedSurface.impactedAdrs.length +
        affectedSurface.impactedArchitectureArtifacts.length;
    const factors = buildImpactFactors({
        changedModules,
        directDependencyChains,
        transitiveDependencyChains,
        governanceSurface: affectedSurface,
    });
    const weightedScore = factors.reduce((accumulator, factor) => accumulator + factor.weight, 0) +
        Math.max(0, maxTransitiveChainDepth - 2);
    const impactRadiusScore = Math.max(weightedScore, totalAffectedModules);
    const reasoning = [];
    let reasoningCounter = 1;
    for (const moduleName of changedModules) {
        reasoning.push({
            id: `IR-${String(reasoningCounter).padStart(3, '0')}`,
            type: 'changed-scope',
            targetType: 'module',
            target: moduleName,
            evidence: `Changed module: ${moduleName}`,
        });
        reasoningCounter += 1;
    }
    for (const entry of directDependencyChains) {
        reasoning.push({
            id: `IR-${String(reasoningCounter).padStart(3, '0')}`,
            type: 'direct-impact',
            targetType: 'module',
            target: entry.module,
            evidence: `Direct dependent path: ${formatChain(entry.chain)}`,
            chain: entry.chain,
        });
        reasoningCounter += 1;
    }
    for (const entry of transitiveDependencyChains) {
        reasoning.push({
            id: `IR-${String(reasoningCounter).padStart(3, '0')}`,
            type: 'transitive-impact',
            targetType: 'module',
            target: entry.module,
            evidence: `Transitive chain path: ${formatChain(entry.chain)}`,
            chain: entry.chain,
        });
        reasoningCounter += 1;
    }
    for (const contractPath of affectedSurface.impactedContracts) {
        reasoning.push({
            id: `IR-${String(reasoningCounter).padStart(3, '0')}`,
            type: 'governance-surface',
            targetType: 'contract',
            target: contractPath,
            evidence: `Touched contract: ${contractPath}`,
        });
        reasoningCounter += 1;
    }
    for (const entrypointPath of affectedSurface.impactedPublicEntrypoints) {
        reasoning.push({
            id: `IR-${String(reasoningCounter).padStart(3, '0')}`,
            type: 'governance-surface',
            targetType: 'public-entrypoint',
            target: entrypointPath,
            evidence: `Touched public entrypoint: ${entrypointPath}`,
        });
        reasoningCounter += 1;
    }
    for (const adrPath of affectedSurface.impactedAdrs) {
        reasoning.push({
            id: `IR-${String(reasoningCounter).padStart(3, '0')}`,
            type: 'governance-surface',
            targetType: 'adr',
            target: adrPath,
            evidence: `Touched ADR: ${adrPath}`,
        });
        reasoningCounter += 1;
    }
    for (const artifactPath of affectedSurface.impactedArchitectureArtifacts) {
        reasoning.push({
            id: `IR-${String(reasoningCounter).padStart(3, '0')}`,
            type: 'governance-surface',
            targetType: 'architecture-artifact',
            target: artifactPath,
            evidence: `Touched architecture artifact: ${artifactPath}`,
        });
        reasoningCounter += 1;
    }
    const scoring = {
        modelVersion: 1,
        formula: 'score = (changed*2) + (direct*3) + (transitive*2) + (governance*2) + max(0, depth-2)',
        components: {
            changedModules: changedModules.length,
            directlyAffectedModules: directDependents.length,
            transitivelyAffectedModules: transitiveDependents.length,
            governanceSurfaceItems: governanceSurfaceCount,
            maxTransitiveChainDepth,
        },
        computedScore: impactRadiusScore,
        thresholds: {
            lowMax: 6,
            mediumMax: 14,
            highMin: 15,
        },
        rationale: [
            'Direct dependents are weighted higher than transitive dependents to prioritize immediate coupling.',
            'Governance surface touches raise score because contracts, entrypoints, ADRs, and architecture artifacts affect review scope.',
        ],
    };
    const summary = `Changed=${changedModules.length}, Direct=${directDependents.length}, ` +
        `Transitive=${transitiveDependents.length}, GovernanceSurfaces=${governanceSurfaceCount}.`;
    return {
        changedModules,
        directDependents,
        transitiveDependents,
        totalAffectedModules,
        impactRadiusScore,
        impactLevel: resolveImpactLevel(impactRadiusScore),
        summary,
        affectedSurface,
        directDependencyChains,
        transitiveDependencyChains,
        factors,
        reasoning,
        scoring,
    };
}
//# sourceMappingURL=impactAnalysisEngine.js.map