/**
 * Capacity Estimator
 *
 * Estimates maximum capacity and projects costs based on load test results.
 *
 * @module scanners/scale/capacity-estimator
 */
import { logger } from "../../logger.js";
/**
 * Instance type pricing (approximate monthly costs)
 */
const INSTANCE_PRICING = {
    // AWS EC2 (us-east-1)
    "t3.micro": { cpu: 2, memory: 1, price: 8 },
    "t3.small": { cpu: 2, memory: 2, price: 15 },
    "t3.medium": { cpu: 2, memory: 4, price: 30 },
    "t3.large": { cpu: 2, memory: 8, price: 60 },
    "t3.xlarge": { cpu: 4, memory: 16, price: 120 },
    "m5.large": { cpu: 2, memory: 8, price: 70 },
    "m5.xlarge": { cpu: 4, memory: 16, price: 140 },
    "m5.2xlarge": { cpu: 8, memory: 32, price: 280 },
    // Vercel (approximate)
    "vercel-hobby": { cpu: 1, memory: 1, price: 0 },
    "vercel-pro": { cpu: 1, memory: 1, price: 20 },
    "vercel-enterprise": { cpu: 4, memory: 4, price: 500 },
    // Railway
    "railway-starter": { cpu: 1, memory: 0.5, price: 5 },
    "railway-pro": { cpu: 2, memory: 2, price: 20 },
};
/**
 * Estimate breakpoint (where system starts failing)
 */
function estimateBreakpoint(loadTestResult) {
    // Use Little's Law and observed metrics
    const avgLatency = loadTestResult.summary.latency.mean;
    const throughput = loadTestResult.summary.avgThroughput;
    const errorRate = loadTestResult.summary.errorRate;
    // Find the scenario with highest VUs
    const maxVus = Math.max(...loadTestResult.scenarios.map((s) => s.vusMax));
    // If error rate is already high, breakpoint is near current load
    if (errorRate > 0.05) {
        return {
            vus: Math.round(maxVus * 0.8),
            confidence: 0.7,
        };
    }
    // If latency is degraded but not failing, estimate 50% more capacity
    if (loadTestResult.summary.latency.p95 > 500) {
        return {
            vus: Math.round(maxVus * 1.2),
            confidence: 0.6,
        };
    }
    // System handling load well, estimate 2x capacity
    if (errorRate < 0.01 && avgLatency < 200) {
        return {
            vus: Math.round(maxVus * 2.5),
            confidence: 0.5,
        };
    }
    // Default conservative estimate
    return {
        vus: Math.round(maxVus * 1.5),
        confidence: 0.4,
    };
}
/**
 * Calculate maximum requests per second
 */
function estimateMaxRPS(loadTestResult) {
    const breakpoint = estimateBreakpoint(loadTestResult);
    const currentRPS = loadTestResult.summary.avgThroughput;
    const currentVUs = Math.max(...loadTestResult.scenarios.map((s) => s.vusMax));
    if (currentVUs === 0)
        return 0;
    // Linear extrapolation with degradation factor
    const rpsPerVU = currentRPS / currentVUs;
    const degradationFactor = 0.8; // Assume 20% degradation at scale
    return Math.round(breakpoint.vus * rpsPerVU * degradationFactor);
}
/**
 * Generate recommendations based on results
 */
function generateRecommendations(loadTestResult, bottlenecks) {
    const recommendations = [];
    // High latency recommendations
    if (loadTestResult.summary.latency.p95 > 500) {
        recommendations.push("Add application-level caching (Redis, Memcached) to reduce database load");
        recommendations.push("Consider using a CDN for static assets and cacheable API responses");
    }
    // High error rate recommendations
    if (loadTestResult.summary.errorRate > 0.01) {
        recommendations.push("Implement circuit breakers to prevent cascade failures");
        recommendations.push("Add rate limiting to protect against traffic spikes");
    }
    // Database bottlenecks
    const dbBottlenecks = bottlenecks.filter((b) => b.type === "database");
    if (dbBottlenecks.length > 0) {
        recommendations.push("Optimize database queries - add indexes and use connection pooling");
        recommendations.push("Consider read replicas for read-heavy workloads");
    }
    // Memory bottlenecks
    const memBottlenecks = bottlenecks.filter((b) => b.type === "memory");
    if (memBottlenecks.length > 0) {
        recommendations.push("Profile memory usage and fix potential leaks");
        recommendations.push("Consider increasing instance memory or using smaller request payloads");
    }
    // General scaling recommendations
    if (loadTestResult.summary.avgThroughput < 100) {
        recommendations.push("Consider horizontal scaling with a load balancer");
    }
    // Add default if no specific recommendations
    if (recommendations.length === 0) {
        recommendations.push("System performing well - monitor for degradation at higher loads");
    }
    return recommendations;
}
/**
 * Project infrastructure costs
 */
function projectCosts(maxRPS, maxConcurrent) {
    // Simple heuristic: 1000 RPS per m5.large
    const rpsPerInstance = 1000;
    const instancesNeeded = Math.ceil(maxRPS / rpsPerInstance);
    // Choose appropriate instance type
    let instanceType = "t3.medium";
    if (maxConcurrent > 500)
        instanceType = "t3.large";
    if (maxConcurrent > 1000)
        instanceType = "m5.large";
    if (maxConcurrent > 5000)
        instanceType = "m5.xlarge";
    const pricing = INSTANCE_PRICING[instanceType];
    return {
        provider: "AWS",
        instanceType,
        instances: Math.max(2, instancesNeeded), // At least 2 for HA
        monthlyCost: Math.max(2, instancesNeeded) * pricing.price,
    };
}
/**
 * Estimate capacity for a project
 */
export async function estimateCapacity(loadTestResult, bottlenecks = []) {
    logger.info("scale.capacity_estimation_started");
    // If no load test results, return conservative estimates
    if (!loadTestResult) {
        return {
            maxConcurrentUsers: 100,
            maxRequestsPerSecond: 50,
            estimatedBreakpoint: {
                vus: 200,
                confidence: 0.2,
            },
            recommendations: [
                "Run load tests to get accurate capacity estimates",
                "Start with baseline performance monitoring",
            ],
        };
    }
    const breakpoint = estimateBreakpoint(loadTestResult);
    const maxRPS = estimateMaxRPS(loadTestResult);
    const recommendations = generateRecommendations(loadTestResult, bottlenecks);
    const costProjection = projectCosts(maxRPS, breakpoint.vus);
    const estimate = {
        maxConcurrentUsers: breakpoint.vus,
        maxRequestsPerSecond: maxRPS,
        estimatedBreakpoint: breakpoint,
        recommendations,
        costProjection,
    };
    logger.info("scale.capacity_estimation_completed", {
        maxConcurrent: estimate.maxConcurrentUsers,
        maxRPS: estimate.maxRequestsPerSecond,
        confidence: breakpoint.confidence,
    });
    return estimate;
}
/**
 * Calculate capacity score (100 = excellent capacity margin)
 */
export function calculateCapacityScore(estimate, targetConcurrent = 100) {
    // Score based on how much headroom we have
    const headroom = estimate.maxConcurrentUsers / targetConcurrent;
    if (headroom >= 5)
        return 100; // 5x headroom
    if (headroom >= 3)
        return 90; // 3x headroom
    if (headroom >= 2)
        return 80; // 2x headroom
    if (headroom >= 1.5)
        return 70; // 1.5x headroom
    if (headroom >= 1)
        return 50; // Just meeting target
    return Math.round(headroom * 50); // Below target
}
//# sourceMappingURL=capacity-estimator.js.map