/**
 * Bottleneck Detector
 *
 * Analyzes load test results and code patterns to identify performance bottlenecks.
 * Uses semantic analysis to reduce false positives on intentional patterns.
 *
 * @module scanners/scale/bottleneck-detector
 */
import { readFile, readdir } from "fs/promises";
import { join } from "path";
import { logger } from "../../logger.js";
/**
 * Patterns that indicate intentional sequential processing (workflow orchestration)
 */
const WORKFLOW_CONTEXT_PATTERNS = [
    /(?:workflow|pipeline|sequential|steps|phases|orchestrat|saga)/i,
    /await\s+Promise\.all/,
    /for\s+await\s+\(/,
    /\.reduce\(\s*async/,
    /runInSequence|runSequentially|executeInOrder/i,
    /step\d+|phase\d+|stage\d+/i,
];
/**
 * File patterns that are low-risk for N+1 issues
 */
const LOW_RISK_FILE_PATTERNS = [
    /\.test\.(ts|js|tsx|jsx)$/,
    /\.spec\.(ts|js|tsx|jsx)$/,
    /__tests__\//,
    /\.config\.(ts|js)$/,
    /migrations?\//,
    /seeds?\//,
    /scripts?\//,
    /fixtures?\//,
];
const N_PLUS_ONE_PATTERNS = [
    { pattern: /\.findMany\(\s*\{[^}]*include\s*:\s*\{/g, name: "Prisma nested include", baseConfidence: 85 },
    { pattern: /\.findAll\(\s*\{[^}]*include\s*:\s*\[/g, name: "Sequelize nested include", baseConfidence: 85 },
    { pattern: /\.find\(\s*\{[^}]*relations\s*:\s*\[/g, name: "TypeORM relations", baseConfidence: 85 },
    { pattern: /for\s*\([^)]*\)\s*\{[^}]*(?:SELECT|INSERT|UPDATE|DELETE)/gi, name: "SQL in loop", baseConfidence: 90 },
    { pattern: /\.forEach\(\s*async/g, name: "forEach async", baseConfidence: 60 },
    { pattern: /\.map\(\s*async[^)]*=>\s*\{[^}]*(?:findOne|findById|query)/gi, name: "async map with query", baseConfidence: 80 },
];
/**
 * Memory leak patterns
 */
const MEMORY_LEAK_PATTERNS = [
    // Growing arrays in closures
    /const\s+\w+\s*=\s*\[\s*\][\s\S]*setInterval/g,
    // Event listeners without cleanup
    /addEventListener\([^)]+\)(?![\s\S]*removeEventListener)/g,
    // Global state accumulation
    /global\.\w+\s*=\s*\[[\s\S]*\.push\(/g,
];
/**
 * Blocking operation patterns
 */
const BLOCKING_PATTERNS = [
    // Sync file operations
    /(?:readFileSync|writeFileSync|existsSync|statSync)/g,
    // Blocking crypto
    /crypto\.(?:pbkdf2Sync|scryptSync|randomBytes)\s*\(/g,
    // Large JSON parsing
    /JSON\.parse\([^)]*\.length\s*>\s*\d{6}/g,
];
/**
 * Check if a file is low-risk based on its path
 */
function isLowRiskFile(filePath) {
    return LOW_RISK_FILE_PATTERNS.some((pattern) => pattern.test(filePath));
}
/**
 * Check if the pattern match is in a workflow/orchestration context
 */
function isWorkflowContext(content, matchIndex) {
    const contextStart = Math.max(0, matchIndex - 500);
    const contextEnd = Math.min(content.length, matchIndex + 500);
    const context = content.slice(contextStart, contextEnd);
    return WORKFLOW_CONTEXT_PATTERNS.some((pattern) => pattern.test(context));
}
/**
 * Check if the loop has a small fixed iteration count
 */
function hasSmallFixedIteration(content, matchIndex) {
    const lineStart = content.lastIndexOf("\n", matchIndex) + 1;
    const lineEnd = content.indexOf("\n", matchIndex);
    const line = content.slice(lineStart, lineEnd === -1 ? undefined : lineEnd);
    const smallArrayPatterns = [
        /\.length\s*[<>]=?\s*[0-9]{1,2}\)/,
        /\[\s*['"][^'"]+['"]\s*(?:,\s*['"][^'"]+['"]\s*){0,5}\]/,
        /(?:steps|phases|stages|config|options)\s*\./i,
    ];
    return smallArrayPatterns.some((p) => p.test(line));
}
/**
 * Calculate adjusted confidence for a pattern match
 */
function calculateConfidence(baseConfidence, content, matchIndex, filePath) {
    let confidence = baseConfidence;
    let reason;
    if (isLowRiskFile(filePath)) {
        confidence -= 30;
        reason = "Low-risk file (test/config/migration)";
    }
    if (isWorkflowContext(content, matchIndex)) {
        confidence -= 25;
        reason = reason ? `${reason}, workflow context` : "Workflow/orchestration context";
    }
    if (hasSmallFixedIteration(content, matchIndex)) {
        confidence -= 20;
        reason = reason ? `${reason}, small iteration` : "Small fixed iteration count";
    }
    const isLikelyFalsePositive = confidence < 50;
    return { confidence: Math.max(10, confidence), isLikelyFalsePositive, reason };
}
/**
 * Analyze source code for potential bottlenecks
 */
async function analyzeSourceCode(projectPath) {
    const bottlenecks = [];
    const srcDirs = ["src", "app", "pages", "api", "lib"];
    for (const dir of srcDirs) {
        try {
            const dirPath = join(projectPath, dir);
            const files = await readdir(dirPath, { recursive: true });
            for (const file of files) {
                if (typeof file !== "string")
                    continue;
                if (!file.match(/\.(ts|js|tsx|jsx)$/))
                    continue;
                try {
                    const filePath = join(dirPath, file);
                    const fullPath = `${dir}/${file}`;
                    const content = await readFile(filePath, "utf-8");
                    // Check for N+1 patterns with semantic analysis
                    for (const { pattern, name, baseConfidence } of N_PLUS_ONE_PATTERNS) {
                        const regex = new RegExp(pattern.source, pattern.flags);
                        let match;
                        while ((match = regex.exec(content)) !== null) {
                            const { confidence, isLikelyFalsePositive, reason } = calculateConfidence(baseConfidence, content, match.index, fullPath);
                            if (confidence >= 50) {
                                bottlenecks.push({
                                    type: "database",
                                    location: fullPath,
                                    severity: confidence >= 80 ? "high" : "medium",
                                    description: `Potential N+1 query pattern: ${name}`,
                                    metrics: {
                                        current: 1,
                                        threshold: 0,
                                        unit: "patterns",
                                    },
                                    recommendation: "Consider using eager loading or batching queries",
                                    confidence,
                                    isLikelyFalsePositive,
                                    falsePositiveReason: reason,
                                });
                            }
                            else if (!isLikelyFalsePositive) {
                                logger.debug("scale.bottleneck_low_confidence", {
                                    file: fullPath,
                                    pattern: name,
                                    confidence,
                                    reason,
                                });
                            }
                        }
                    }
                    // Check for memory leak patterns (skip in test files)
                    if (!isLowRiskFile(fullPath)) {
                        for (const pattern of MEMORY_LEAK_PATTERNS) {
                            const matches = content.match(pattern);
                            if (matches && matches.length > 0) {
                                bottlenecks.push({
                                    type: "memory",
                                    location: fullPath,
                                    severity: "medium",
                                    description: `Potential memory leak pattern detected`,
                                    metrics: {
                                        current: matches.length,
                                        threshold: 0,
                                        unit: "patterns",
                                    },
                                    recommendation: "Ensure proper cleanup of resources and event listeners",
                                    confidence: 75,
                                    isLikelyFalsePositive: false,
                                });
                            }
                        }
                        // Check for blocking patterns
                        for (const pattern of BLOCKING_PATTERNS) {
                            const matches = content.match(pattern);
                            if (matches && matches.length > 0) {
                                bottlenecks.push({
                                    type: "cpu",
                                    location: fullPath,
                                    severity: "medium",
                                    description: `Blocking operation detected: ${matches[0]}`,
                                    metrics: {
                                        current: matches.length,
                                        threshold: 0,
                                        unit: "occurrences",
                                    },
                                    recommendation: "Use async alternatives to avoid blocking the event loop",
                                    confidence: 85,
                                    isLikelyFalsePositive: false,
                                });
                            }
                        }
                    }
                }
                catch {
                    // Skip files that can't be read
                }
            }
        }
        catch {
            // Directory doesn't exist
        }
    }
    // Deduplicate bottlenecks by location + type
    const seen = new Set();
    const deduped = bottlenecks.filter((b) => {
        const key = `${b.type}:${b.location}:${b.description}`;
        if (seen.has(key))
            return false;
        seen.add(key);
        return true;
    });
    return deduped;
}
/**
 * Analyze load test results for bottlenecks
 */
function analyzeLoadTestResults(results) {
    const bottlenecks = [];
    // Check for high latency
    if (results.summary.latency.p95 > 500) {
        bottlenecks.push({
            type: "endpoint",
            location: "Overall API",
            severity: results.summary.latency.p95 > 1000 ? "critical" : "high",
            description: `High 95th percentile latency: ${results.summary.latency.p95.toFixed(0)}ms`,
            metrics: {
                current: results.summary.latency.p95,
                threshold: 500,
                unit: "ms",
            },
            recommendation: "Profile endpoints to identify slow operations, add caching, or optimize queries",
        });
    }
    // Check for high error rate
    if (results.summary.errorRate > 0.01) {
        bottlenecks.push({
            type: "endpoint",
            location: "Overall API",
            severity: results.summary.errorRate > 0.05 ? "critical" : "high",
            description: `High error rate: ${(results.summary.errorRate * 100).toFixed(2)}%`,
            metrics: {
                current: results.summary.errorRate,
                threshold: 0.01,
                unit: "rate",
            },
            recommendation: "Check error logs, add retry logic, or scale resources",
        });
    }
    // Check for low throughput
    if (results.summary.avgThroughput < 100) {
        bottlenecks.push({
            type: "endpoint",
            location: "Overall API",
            severity: "medium",
            description: `Low throughput: ${results.summary.avgThroughput.toFixed(1)} req/s`,
            metrics: {
                current: results.summary.avgThroughput,
                threshold: 100,
                unit: "req/s",
            },
            recommendation: "Consider horizontal scaling or optimizing request handling",
        });
    }
    // Check individual endpoints
    for (const scenario of results.scenarios) {
        for (const endpoint of scenario.endpoints) {
            if (endpoint.latency.p95 > 500) {
                bottlenecks.push({
                    type: "endpoint",
                    location: `${endpoint.method} ${endpoint.path}`,
                    severity: endpoint.latency.p95 > 1000 ? "critical" : "high",
                    description: `Slow endpoint: p95=${endpoint.latency.p95.toFixed(0)}ms`,
                    metrics: {
                        current: endpoint.latency.p95,
                        threshold: 500,
                        unit: "ms",
                    },
                    recommendation: "Add endpoint-specific caching or optimize the handler",
                });
            }
        }
    }
    return bottlenecks;
}
/**
 * Detect bottlenecks in a project
 */
export async function detectBottlenecks(projectPath, loadTestResults, options = {}) {
    const { includeAllConfidence = false, minConfidence = 50 } = options;
    const bottlenecks = [];
    logger.info("scale.bottleneck_detection_started", { projectPath, minConfidence });
    // Analyze source code
    const codeBottlenecks = await analyzeSourceCode(projectPath);
    // Filter by confidence unless includeAllConfidence is true
    const filteredCodeBottlenecks = includeAllConfidence
        ? codeBottlenecks
        : codeBottlenecks.filter((b) => b.confidence >= minConfidence);
    bottlenecks.push(...filteredCodeBottlenecks);
    // Analyze load test results if available
    if (loadTestResults) {
        const loadBottlenecks = analyzeLoadTestResults(loadTestResults);
        bottlenecks.push(...loadBottlenecks.map((b) => ({
            ...b,
            confidence: 100,
            isLikelyFalsePositive: false,
        })));
    }
    // Sort by severity, then by confidence
    const severityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
    bottlenecks.sort((a, b) => {
        const severityDiff = severityOrder[a.severity] - severityOrder[b.severity];
        if (severityDiff !== 0)
            return severityDiff;
        return b.confidence - a.confidence;
    });
    const highConfidenceCount = bottlenecks.filter((b) => b.confidence >= 80).length;
    const likelyFpCount = bottlenecks.filter((b) => b.isLikelyFalsePositive).length;
    logger.info("scale.bottleneck_detection_completed", {
        total: bottlenecks.length,
        highConfidence: highConfidenceCount,
        likelyFalsePositives: likelyFpCount,
        critical: bottlenecks.filter((b) => b.severity === "critical").length,
        high: bottlenecks.filter((b) => b.severity === "high").length,
    });
    return bottlenecks;
}
/**
 * Calculate bottleneck score (100 = no bottlenecks)
 * Now factors in confidence - low confidence findings have less impact
 */
export function calculateBottleneckScore(bottlenecks) {
    let score = 100;
    for (const bottleneck of bottlenecks) {
        const extended = bottleneck;
        const confidenceMultiplier = extended.confidence !== undefined ? extended.confidence / 100 : 1;
        if (extended.isLikelyFalsePositive) {
            continue;
        }
        let penalty;
        switch (bottleneck.severity) {
            case "critical":
                penalty = 25;
                break;
            case "high":
                penalty = 15;
                break;
            case "medium":
                penalty = 8;
                break;
            case "low":
                penalty = 3;
                break;
            default:
                penalty = 0;
        }
        score -= penalty * confidenceMultiplier;
    }
    return Math.max(0, Math.round(score));
}
//# sourceMappingURL=bottleneck-detector.js.map