/**
 * Antagonist Remediation Prioritizer
 *
 * Prioritizes findings based on:
 * 1. Attack surface reduction (blocks most narratives)
 * 2. Bottleneck findings (appear in multiple chains)
 * 3. Effort vs. impact trade-offs
 *
 * @module agents/antagonist/prioritizer
 */
/**
 * Effort estimation based on finding category
 */
const CATEGORY_EFFORT = {
    "hardcoded-secret": "low",
    xss: "low",
    "path-traversal": "low",
    "sql-injection": "medium",
    "auth-bypass": "medium",
    "broken-access-control": "medium",
    "command-injection": "medium",
    "code-injection": "high",
    "prompt-injection": "high",
    "excessive-agency": "high",
    "logic-flaw": "high",
};
/**
 * Impact estimation based on severity and narrative involvement
 */
function calculateImpact(finding, narrativeCount) {
    const severityScore = {
        critical: 5,
        high: 4,
        medium: 3,
        low: 2,
        info: 1,
    };
    const score = severityScore[finding.severity] + narrativeCount;
    if (score >= 7)
        return "high";
    if (score >= 4)
        return "medium";
    return "low";
}
/**
 * Count how many narratives each finding appears in
 */
function countNarrativeAppearances(findings, narratives) {
    const appearances = new Map();
    for (const finding of findings) {
        appearances.set(finding.id, []);
    }
    for (const narrative of narratives) {
        for (const findingId of narrative.findingIds) {
            const existing = appearances.get(findingId) || [];
            existing.push(narrative.id);
            appearances.set(findingId, existing);
        }
    }
    return appearances;
}
/**
 * Calculate attack surface reduction if finding is fixed
 */
function calculateAttackSurfaceReduction(findingId, narratives) {
    const totalNarratives = narratives.length;
    if (totalNarratives === 0)
        return 0;
    const blockedNarratives = narratives.filter((n) => n.findingIds.includes(findingId)).length;
    return Math.round((blockedNarratives / totalNarratives) * 100);
}
/**
 * Generate prioritized remediation list
 */
export function prioritizeRemediations(findings, narratives) {
    const appearances = countNarrativeAppearances(findings, narratives);
    const prioritizations = [];
    const findingsWithNarratives = findings.filter((f) => {
        const narrativeIds = appearances.get(f.id) || [];
        return narrativeIds.length > 0;
    });
    const scored = findingsWithNarratives.map((finding) => {
        const narrativeIds = appearances.get(finding.id) || [];
        const attackSurfaceReduction = calculateAttackSurfaceReduction(finding.id, narratives);
        const severityScore = {
            critical: 100,
            high: 75,
            medium: 50,
            low: 25,
            info: 10,
        };
        const effortPenalty = {
            low: 0,
            medium: 15,
            high: 30,
        };
        const effort = CATEGORY_EFFORT[finding.category] || "medium";
        const score = severityScore[finding.severity] +
            attackSurfaceReduction +
            narrativeIds.length * 10 -
            effortPenalty[effort];
        return { finding, narrativeIds, score, effort };
    });
    scored.sort((a, b) => b.score - a.score);
    for (let i = 0; i < scored.length; i++) {
        const { finding, narrativeIds, effort } = scored[i];
        prioritizations.push({
            order: i + 1,
            findingId: finding.id,
            reason: generateReason(finding, narrativeIds.length, narratives),
            blocksNarratives: narrativeIds,
            effort,
            impact: calculateImpact(finding, narrativeIds.length),
        });
    }
    for (const finding of findings) {
        const narrativeIds = appearances.get(finding.id) || [];
        if (narrativeIds.length === 0 && finding.severity !== "info") {
            prioritizations.push({
                order: prioritizations.length + 1,
                findingId: finding.id,
                reason: `Standalone ${finding.severity} finding not in attack chains`,
                blocksNarratives: [],
                effort: CATEGORY_EFFORT[finding.category] || "medium",
                impact: calculateImpact(finding, 0),
            });
        }
    }
    return prioritizations;
}
/**
 * Generate reason for prioritization
 */
function generateReason(finding, narrativeCount, narratives) {
    const reasons = [];
    if (finding.severity === "critical") {
        reasons.push("Critical severity requires immediate attention");
    }
    if (narrativeCount > 2) {
        reasons.push(`Bottleneck: appears in ${narrativeCount} attack paths`);
    }
    else if (narrativeCount > 0) {
        reasons.push(`Part of ${narrativeCount} attack path(s)`);
    }
    const isEntryPoint = narratives.some((n) => n.entryPoint === finding.id);
    if (isEntryPoint) {
        reasons.push("Entry point for attack chain");
    }
    const effort = CATEGORY_EFFORT[finding.category] || "medium";
    if (effort === "low") {
        reasons.push("Quick fix");
    }
    if (reasons.length === 0) {
        reasons.push(`${finding.severity} ${finding.category} vulnerability`);
    }
    return reasons.join(". ");
}
/**
 * Calculate overall prioritization metrics
 */
export function calculatePrioritizationMetrics(prioritizations, narratives) {
    const criticalPathFindings = prioritizations.filter((p) => p.blocksNarratives.length > 0).length;
    const quickWins = prioritizations.filter((p) => p.effort === "low" && p.impact !== "low").length;
    const effortHours = {
        low: 1,
        medium: 4,
        high: 8,
    };
    const totalHours = prioritizations.reduce((sum, p) => sum + effortHours[p.effort], 0);
    const topThree = prioritizations.slice(0, 3);
    const topThreeBlocks = new Set(topThree.flatMap((p) => p.blocksNarratives));
    const topThreeImpact = `Fixing top 3 blocks ${topThreeBlocks.size}/${narratives.length} attack paths`;
    return {
        totalFindings: prioritizations.length,
        criticalPathFindings,
        quickWins,
        estimatedEffort: `${totalHours} hours`,
        topThreeImpact,
    };
}
//# sourceMappingURL=prioritizer.js.map