// ---------------------------------------------------------------------------
// Helpers — one per dimension (keeps cyclomatic complexity ≤ 15)
// ---------------------------------------------------------------------------
function clamp(value) {
    return Math.max(0, Math.min(100, value));
}
function calcTestCoverageScore(modules) {
    const successful = modules.filter((m) => m.status === 'success');
    if (successful.length === 0) {
        return 0;
    }
    const totalRatio = successful.reduce((sum, m) => sum + m.analysis.testCoverage.testToSourceRatio, 0);
    const avgRatio = totalRatio / successful.length;
    return clamp(avgRatio * 100);
}
function calcTechDebtScore(modules) {
    const successful = modules.filter((m) => m.status === 'success');
    if (successful.length === 0) {
        return 100;
    }
    let totalHotspots = 0;
    let totalFiles = 0;
    for (const m of successful) {
        for (const metric of m.analysis.complexityMetrics) {
            totalFiles++;
            if (metric.avgComplexity > 10) {
                totalHotspots++;
            }
        }
    }
    if (totalFiles === 0) {
        return 100;
    }
    const density = totalHotspots / totalFiles;
    return clamp(100 - density * 500);
}
function calcArchConsistencyScore(modules, cross) {
    const successful = modules.filter((m) => m.status === 'success');
    let score = 100;
    if (successful.length > 1) {
        // Inconsistency 1: test presence varies across modules
        const withTests = successful.filter((m) => m.analysis.testCoverage.testedModules.length > 0);
        const allHaveTests = withTests.length === successful.length;
        const noneHaveTests = withTests.length === 0;
        if (!allHaveTests && !noneHaveTests) {
            score -= 15;
        }
        // Inconsistency 2: config presence varies across modules
        const withConfig = successful.filter((m) => m.analysis.configAnalysis.configFiles.length > 0);
        const allHaveConfig = withConfig.length === successful.length;
        const noneHaveConfig = withConfig.length === 0;
        if (!allHaveConfig && !noneHaveConfig) {
            score -= 15;
        }
        // Inconsistency 3: high std deviation in avg complexity across modules
        const complexities = successful.map((m) => {
            const metrics = m.analysis.complexityMetrics;
            if (metrics.length === 0) {
                return 0;
            }
            return metrics.reduce((s, c) => s + c.avgComplexity, 0) / metrics.length;
        });
        const mean = complexities.reduce((s, v) => s + v, 0) / complexities.length;
        const variance = complexities.reduce((s, v) => s + (v - mean) ** 2, 0) / complexities.length;
        const stdDev = Math.sqrt(variance);
        if (stdDev > 5) {
            score -= 15;
        }
    }
    // Penalise antipatterns from cross-module analysis
    for (const ap of cross.antipatterns) {
        if (ap.severity === 'medium') {
            score -= 10;
        }
        else if (ap.severity === 'high' || ap.severity === 'critical') {
            score -= 20;
        }
    }
    return clamp(score);
}
function calcDepHealthScore(modules, cross) {
    let score = 100;
    // Circular dependency cycles
    score -= cross.circularDeps.length * 25;
    // Orphan modules
    score -= cross.orphanModules.length * 5;
    // God module detection: any module where >50% of other modules depend on it
    const totalModules = modules.length;
    if (totalModules > 1) {
        const incomingCount = new Map();
        for (const dep of cross.dependencies) {
            incomingCount.set(dep.to, (incomingCount.get(dep.to) ?? 0) + 1);
        }
        for (const count of incomingCount.values()) {
            if (count / totalModules > 0.5) {
                score -= 10;
                break; // penalise once even if multiple god modules exist
            }
        }
    }
    return clamp(score);
}
// ---------------------------------------------------------------------------
// Grade mapping
// ---------------------------------------------------------------------------
function deriveGrade(score) {
    if (score >= 80) {
        return 'A';
    }
    if (score >= 60) {
        return 'B';
    }
    if (score >= 40) {
        return 'C';
    }
    if (score >= 20) {
        return 'D';
    }
    return 'F';
}
// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------
export function calculateProjectHealth(moduleResults, crossModuleAnalysis) {
    const testCoverage = calcTestCoverageScore(moduleResults);
    const techDebt = calcTechDebtScore(moduleResults);
    const archConsistency = calcArchConsistencyScore(moduleResults, crossModuleAnalysis);
    const depHealth = calcDepHealthScore(moduleResults, crossModuleAnalysis);
    const breakdown = {
        testCoverage,
        techDebt,
        archConsistency,
        depHealth,
    };
    const score = Math.round((testCoverage + techDebt + archConsistency + depHealth) / 4);
    return {
        score,
        breakdown,
        grade: deriveGrade(score),
    };
}
//# sourceMappingURL=health-scorer.js.map