/** Weights used to compute the health score. */
const STATUS_WEIGHTS = {
    up_to_date: 0,
    outdated_patch: 2,
    outdated_minor: 5,
    outdated_major: 10,
    vulnerable: 30,
    unmaintained: 15,
};
const MAX_PENALTY = 100;
/** Map severity codes to display labels. */
export function severityLabel(code) {
    if (code === 'critical') {
        return 'CRITICAL';
    }
    if (code === 'warning') {
        return 'WARNING';
    }
    return 'INFO';
}
/**
 * Parse a semver string into its components.
 * Returns null for non-semver strings.
 */
export function parseSemver(v) {
    const cleaned = v.replace(/^[^0-9]*/, '');
    const parts = cleaned.split('.');
    if (parts.length < 2) {
        return null;
    }
    /* v8 ignore start */
    const major = parseInt(parts[0] ?? '0', 10);
    const minor = parseInt(parts[1] ?? '0', 10);
    const patch = parseInt(parts[2] ?? '0', 10);
    if (isNaN(major) || isNaN(minor) || isNaN(patch)) {
        return null;
    }
    /* v8 ignore stop */
    return { major, minor, patch };
}
/**
 * Compare current vs latest version and return a status code.
 * If parsing fails, returns 'outdated_minor' as a safe default.
 */
export function classifyDepStatus(currentVer, latestVer) {
    const cur = parseSemver(currentVer);
    const lat = parseSemver(latestVer);
    if (!cur || !lat) {
        return 'outdated_minor';
    }
    if (cur.major === lat.major && cur.minor === lat.minor && cur.patch === lat.patch) {
        return 'up_to_date';
    }
    if (cur.major < lat.major) {
        return 'outdated_major';
    }
    if (cur.minor < lat.minor) {
        return 'outdated_minor';
    }
    return 'outdated_patch';
}
/**
 * Build top-5 action strings from the dep list and computed score.
 */
export function getTopActions(deps, score) {
    const actions = [];
    const vulnerable = deps.filter((d) => d.statusCode === 'vulnerable');
    if (vulnerable.length > 0) {
        const names = vulnerable
            .slice(0, 3)
            .map((d) => d.name)
            .join(', ');
        actions.push(`Fix ${vulnerable.length} vulnerable package(s): ${names}`);
    }
    const unmaintained = deps.filter((d) => d.statusCode === 'unmaintained');
    if (unmaintained.length > 0) {
        actions.push(`Replace ${unmaintained.length} unmaintained package(s)`);
    }
    const major = deps.filter((d) => d.statusCode === 'outdated_major');
    if (major.length > 0) {
        actions.push(`Plan major upgrades for ${major.length} package(s)`);
    }
    const minor = deps.filter((d) => d.statusCode === 'outdated_minor');
    if (minor.length > 0) {
        actions.push(`Update ${minor.length} outdated (minor) package(s)`);
    }
    if (score.score < 70) {
        actions.push('Run pnpm audit / npm audit to get CVE details');
    }
    return actions.slice(0, 5);
}
/**
 * Compute the StackHealthScore from a list of audited dependencies.
 */
export function calculateHealthScore(deps) {
    const byStatus = {
        up_to_date: 0,
        outdated_patch: 0,
        outdated_minor: 0,
        outdated_major: 0,
        vulnerable: 0,
        unmaintained: 0,
    };
    let totalPenalty = 0;
    for (const dep of deps) {
        byStatus[dep.statusCode] += 1;
        totalPenalty += STATUS_WEIGHTS[dep.statusCode];
    }
    const normaliser = Math.max(deps.length, 1);
    const avgPenalty = totalPenalty / normaliser;
    const rawScore = Math.max(0, MAX_PENALTY - avgPenalty);
    const score = Math.round(Math.min(100, rawScore));
    const partial = {
        score,
        totalDeps: deps.length,
        byStatus,
        topActions: [],
    };
    partial.topActions = getTopActions(deps, partial);
    return partial;
}
//# sourceMappingURL=health-scorer.js.map