import { getMessages } from '../../core/messages/index.js';
const MAX_MESSAGE_PREVIEW_LENGTH = 60;
const BUG_PATTERNS = [
    {
        id: 'null-pointer-deref',
        name: 'Potencial Null Pointer Dereference',
        severity: 'erro',
        category: 'null-safety',
        weight: 0.92,
        detect(content, _relPath) {
            const patterns = [
                /\.\s*map\s*\([^)]+\)\s*\.\s*filter\s*\([^)]+\)\s*\.\s*forEach\s*\(/g,
                /\.\s*find\s*\([^)]+\)\s*\.\s*(?!catch)/g,
                /const\s+\w+\s*=\s*\[[^\]]*\]\s*\.\s*shift\s*\(\)/g,
                /\.\s*pop\s*\(\)\s*\.\s*\w+/g,
            ];
            for (const pattern of patterns) {
                const matches = content.matchAll(pattern);
                for (const m of matches) {
                    return {
                        confidence: 0.85,
                        line: contarLinhasAtePos(content, m.index ?? 0),
                        message: `Possível null pointer dereference: ${m[0].slice(0, MAX_MESSAGE_PREVIEW_LENGTH)}`,
                        suggestion: 'Verificar se o resultado é null/undefined antes de encadear métodos',
                    };
                }
            }
            const optionalChainPattern = /(\w+)\s*\.\s*(find|pop|shift|first)\s*\(/g;
            let match;
            while ((match = optionalChainPattern.exec(content)) !== null) {
                const [, varName, funcName] = match;
                const line = contarLinhasAtePos(content, match.index);
                const beforeContent = content.slice(Math.max(0, match.index - 120), match.index);
                if (funcName === 'pop')
                    continue;
                const varDeclaration = content.slice(Math.max(0, match.index - 200), match.index);
                if (new RegExp(`(const|let|var)\\s+${varName}\\s*=\\s*\\[`).test(varDeclaration))
                    continue;
                const hasOptional = /\?\.\s*$/.test(beforeContent);
                const hasLengthGuard = /\b(length|size)\s*[>!]\s*0\b/.test(varDeclaration)
                    || new RegExp(`while\\s*\\(\\s*${varName}\\.length\\s*\\)`).test(varDeclaration)
                    || new RegExp(`if\\s*\\(\\s*${varName}\\.length\\s*[>!]`).test(varDeclaration);
                const isMapFilterChain = /\.map\s*\(/.test(varDeclaration) || /\.filter\s*\(/.test(varDeclaration);
                if (!hasOptional && !hasLengthGuard && !isMapFilterChain) {
                    return {
                        confidence: 0.75,
                        line,
                        message: `Chamada '${funcName}' sem optional chaining pode causar null pointer`,
                        suggestion: 'Use optional chaining: `?.`',
                    };
                }
            }
            return null;
        },
    },
    {
        id: 'memory-leak',
        name: 'Potencial Memory Leak',
        severity: 'aviso',
        category: 'memory',
        weight: 0.88,
        detect(content, _relPath) {
            const patterns = [
                /addEventListener\s*\([^)]+\)(?!\s*,\s*\{[^}]*signal\s*:)/g,
                /setInterval\s*\([^)]+\)(?!\s*[;]\s*(const|let|var)\s+\w+\s*=\s*setInterval)/g,
                /\.\s*on\s*=\s*\([^)]*\)\s*=>/g,
                /subscribe\s*\([^)]+\)(?!\s*\.\s*unsubscribe)/g,
            ];
            for (const pattern of patterns) {
                const matches = content.matchAll(pattern);
                for (const m of matches) {
                    const line = contarLinhasAtePos(content, m.index ?? 0);
                    if (!hasCleanup(content, line)) {
                        return {
                            confidence: 0.82,
                            line,
                            message: `Possível memory leak: ${m[0].slice(0, 50)} sem cleanup`,
                            suggestion: 'Adicionar cleanup (removeEventListener, clearInterval, unsubscribe)',
                        };
                    }
                }
            }
            return null;
        },
    },
    {
        id: 'race-condition',
        name: 'Potencial Race Condition',
        severity: 'erro',
        category: 'concurrency',
        weight: 0.9,
        detect(content, _relPath) {
            const asyncPatterns = [
                /Promise\.all\s*\([^)]+\)\s*\.\s*then\s*\([^)]+\)\s*(?![^]*catch)/g,
            ];
            for (const pattern of asyncPatterns) {
                const matches = content.matchAll(pattern);
                for (const m of matches) {
                    const line = contarLinhasAtePos(content, m.index ?? 0);
                    if (hasSharedState(content, line)) {
                        return {
                            confidence: 0.78,
                            line,
                            message: `Possível race condition: estado compartilhado entre operações async`,
                            suggestion: 'Usar locks ou atomic operations para estado compartilhado',
                        };
                    }
                }
            }
            return null;
        },
    },
    {
        id: 'unsafe-type-assumption',
        name: 'Type Assumption Insegura',
        severity: 'aviso',
        category: 'type-safety',
        weight: 0.85,
        detect(content, _relPath) {
            const patterns = [
                /as\s+(string|number|boolean)\b(?!\s*\|\s*(undefined|null))/g,
                /\.\s*valueOf\s*\(\)/g,
                /JSON\.parse\s*\([^)]+\)\s*as\s+/g,
            ];
            for (const pattern of patterns) {
                const matches = content.matchAll(pattern);
                for (const m of matches) {
                    const line = contarLinhasAtePos(content, m.index ?? 0);
                    if (!hasValidation(content, line)) {
                        return {
                            confidence: 0.72,
                            line,
                            message: `Type assertion insegura: ${m[0].slice(0, 40)}`,
                            suggestion: 'Validar o tipo antes de fazer cast, usar type guards',
                        };
                    }
                }
            }
            return null;
        },
    },
    {
        id: 'unhandled-promise',
        name: 'Promise Não Tratada',
        severity: 'aviso',
        category: 'logic',
        weight: 0.87,
        detect(content, _relPath) {
            const pattern = /(?<![.\w])new\s+Promise\s*\([^)]*\)(?![^]*\.(then|catch|finally)\s*\()/g;
            const matches = content.matchAll(pattern);
            for (const m of matches) {
                const line = contarLinhasAtePos(content, m.index ?? 0);
                const beforeContent = content.slice(Math.max(0, m.index - 20), m.index);
                if (/\b(await|return)\s+/.test(beforeContent))
                    continue;
                return {
                    confidence: 0.9,
                    line,
                    message: 'Promise criada sem tratamento de then/catch',
                    suggestion: 'Adicionar .catch() ou .then() para tratar o resultado',
                };
            }
            return null;
        },
    },
    {
        id: 'deep-nesting',
        name: 'Aninhamento Excessivo',
        severity: 'info',
        category: 'logic',
        weight: 0.7,
        detect(content, _relPath) {
            let maxDepth = 0;
            let currentDepth = 0;
            let depthLine = 0;
            let lineCount = 1;
            for (const char of content) {
                if (char === '\n')
                    lineCount++;
                if (char === '{' || char === '(' || char === '[') {
                    currentDepth++;
                    if (currentDepth > maxDepth) {
                        maxDepth = currentDepth;
                        depthLine = lineCount;
                    }
                }
                else if (char === '}' || char === ')' || char === ']') {
                    currentDepth = Math.max(0, currentDepth - 1);
                }
            }
            if (maxDepth > 6) {
                return {
                    confidence: Math.min(0.95, 0.5 + maxDepth * 0.06),
                    line: depthLine,
                    message: `Aninhamento excessivo (${maxDepth} níveis) - alta probabilidade de bugs`,
                    suggestion: 'Extrair lógica em funções menores para reduzir complexidade',
                };
            }
            return null;
        },
    },
    {
        id: 'magic-numbers',
        name: 'Números Mágicos sem Contexto',
        severity: 'info',
        category: 'logic',
        weight: 0.65,
        detect(content, _relPath) {
            const magicNumPattern = /(?<![.\w])(86400|3600|60|1000|1024|65535|2147483647|9999)\b(?!\s*[*\/%])/g;
            const matches = content.matchAll(magicNumPattern);
            const results = [];
            for (const m of matches) {
                const line = contarLinhasAtePos(content, m.index ?? 0);
                const lineContent = content.split('\n')[line - 1] || '';
                if (!lineContent.includes('const') && !lineContent.includes('let') && !lineContent.includes('var') && !lineContent.includes('//') && !lineContent.includes('/*')) {
                    results.push({
                        confidence: 0.6,
                        line,
                        message: `Número mágico: ${m[0]} - considerar extrair para constante nomeada`,
                        suggestion: `Criar constante com nome descritivo para ${m[0]}`,
                    });
                }
            }
            return results[0] ?? null;
        },
    },
    {
        id: 'insecure-comparison',
        name: 'Comparação Insegura',
        severity: 'aviso',
        category: 'security',
        weight: 0.83,
        detect(content, _relPath) {
            const patterns = [
                /password\s*===?\s*['"`][^'"`]{0,30}['"`]/gi,
                /token\s*===?\s*['"`][^'"`]{0,30}['"`]/gi,
                /secret\s*===?\s*['"`][^'"`]{0,30}['"`]/gi,
            ];
            for (const pattern of patterns) {
                const matches = content.matchAll(pattern);
                for (const m of matches) {
                    const line = contarLinhasAtePos(content, m.index ?? 0);
                    return {
                        confidence: 0.88,
                        line,
                        message: `Comparação potencialmente insegura: ${m[0].slice(0, 40)}`,
                        suggestion: 'Usar === em vez de ==, evitar hardcoded secrets',
                    };
                }
            }
            return null;
        },
    },
];
function extractFeatures(content, _relPath) {
    const lines = content.split('\n');
    const fileLength = lines.length;
    const totalChars = content.length;
    const avgLineLength = fileLength > 0 ? totalChars / fileLength : 0;
    const functionCount = (content.match(/(?:function\s+\w+|=>\s*\{|\w+\s*\([^)]*\)\s*\{)/g) || []).length;
    const callbackCount = (content.match(/=>\s*\{/g) || []).length;
    const promiseCount = (content.match(/(?:new\s+Promise|async\s+function|\.then\s*\()/g) || []).length;
    const tryCatchCount = (content.match(/(?:try\s*\{|\.catch\s*\()/g) || []).length;
    const anyTypeCount = (content.match(/:\s*any(?![a-zA-Z0-9_$])/g) || []).length;
    const unsafeCastCount = (content.match(/as\s+(?:any|unknown)\b/g) || []).length;
    const todoCount = (content.match(/(?:\/\/|#)\s*(TODO|FIXME|HACK|XXX)/g) || []).length;
    const commentCount = (content.match(/\/\//g) || []).length;
    const commentRatio = totalChars > 0 ? commentCount / totalChars : 0;
    let maxNestingDepth = 0;
    let currentNesting = 0;
    for (const char of content) {
        if (char === '{' || char === '(' || char === '[') {
            currentNesting++;
            maxNestingDepth = Math.max(maxNestingDepth, currentNesting);
        }
        else if (char === '}' || char === ')' || char === ']') {
            currentNesting = Math.max(0, currentNesting - 1);
        }
    }
    const cyclomaticComplexity = functionCount + (content.match(/(?:if|else if|for|while|case\s+|catch\s*\(|&&|\|\|)/g) || []).length;
    return {
        fileLength,
        avgLineLength,
        maxNestingDepth,
        functionCount,
        callbackCount,
        promiseCount,
        tryCatchCount,
        anyTypeCount,
        unsafeCastCount,
        todoCount,
        commentRatio,
        cyclomaticComplexity,
    };
}
function isSelfAnalysis(relPath) {
    return /^src[/\\]analistas[/\\]/.test(relPath)
        || /^src[/\\]relatorios[/\\]/.test(relPath)
        || /^src[/\\]guardian[/\\]/.test(relPath)
        || /^src[/\\]api[/\\]/.test(relPath)
        || /^src[/\\]bin[/\\]/.test(relPath)
        || /^src[/\\]shared[/\\]/.test(relPath)
        || /^extensions[/\\]/.test(relPath)
        || /^src[/\\]cli[/\\]/.test(relPath)
        || /^src[/\\]licencas[/\\]/.test(relPath);
}
function isLowRiskFile(relPath) {
    return /^src[/\\]core[/\\]messages[/\\]/.test(relPath)
        || /^(?:src[/\\])?vitest\.config/.test(relPath)
        || /\.config\.[jt]s$/.test(relPath)
        || /\.d\.ts$/.test(relPath);
}
function isHighComplexityDomain(relPath) {
    return /[/\\]formatadores[/\\]/.test(relPath)
        || /[/\\]formatters[/\\]/.test(relPath)
        || /[/\\]parsers?[/\\]/.test(relPath)
        || /[/\\]engines[/\\]/.test(relPath)
        || /[/\\]execution[/\\]/.test(relPath)
        || /[/\\]workers?[/\\]/.test(relPath)
        || /[/\\]handlers?[/\\]/.test(relPath)
        || /[/\\]validadores?[/\\]/.test(relPath)
        || /[/\\]validators?[/\\]/.test(relPath)
        || /[/\\]exporters?[/\\]/.test(relPath)
        || /[/\\]scanners?[/\\]/.test(relPath);
}
function calcularProbabilidadeBug(features, relPath) {
    if (isSelfAnalysis(relPath) || isLowRiskFile(relPath)) {
        return { probability: 0, factors: [], gruposAtivos: 0 };
    }
    const factors = [];
    let gruposAtivos = 0;
    const isHighDomain = isHighComplexityDomain(relPath);
    const complexityThreshold = isHighDomain ? 120 : 50;
    const functionThreshold = isHighDomain ? 60 : 30;
    const nestingThreshold = isHighDomain ? 10 : 6;
    const mBug = getMessages().BugMlMensagens;
    const complexityFactors = [];
    if (features.cyclomaticComplexity > complexityThreshold) {
        complexityFactors.push(mBug.complexidadeCiclomatica(features.cyclomaticComplexity));
    }
    if (features.functionCount > functionThreshold) {
        complexityFactors.push(mBug.muitasFuncoes(features.functionCount));
    }
    if (features.maxNestingDepth > nestingThreshold) {
        complexityFactors.push(mBug.aninhamentoProfundo(features.maxNestingDepth, features.maxNestingDepth));
    }
    if (features.fileLength > 500) {
        complexityFactors.push(mBug.arquivoMuitoLongo(features.fileLength));
    }
    if (complexityFactors.length > 0) {
        factors.push(...complexityFactors.slice(0, 2));
        gruposAtivos++;
    }
    if (features.unsafeCastCount > 3) {
        factors.push(mBug.castsInseguros(features.unsafeCastCount));
        gruposAtivos++;
    }
    else if (features.anyTypeCount > 5) {
        factors.push(mBug.usoExcessivoAny(features.anyTypeCount));
        gruposAtivos++;
    }
    if (features.promiseCount > 0 && features.tryCatchCount === 0 && !isHighDomain) {
        const asyncContentCheck = features.promiseCount > 2;
        if (asyncContentCheck) {
            factors.push(mBug.promisesSemTratamento);
            gruposAtivos++;
        }
    }
    if (features.todoCount > 5) {
        factors.push(`${getMessages().BugMlMensagens.muitosTODOs} (${features.todoCount})`);
        gruposAtivos++;
    }
    if (factors.length === 0)
        return { probability: 0, factors: [], gruposAtivos: 0 };
    let logProb = 0;
    for (const _ of factors) {
        logProb += Math.log(0.5);
    }
    const probability = Math.min(0.90, 1 - Math.exp(logProb));
    return { probability, factors, gruposAtivos };
}
function hasCleanup(content, _line) {
    return /(removeEventListener|clearInterval|clearTimeout|unsubscribe|destroy|dispose)/.test(content);
}
function hasSharedState(content, _line) {
    const sharedPatterns = [
        /(let|var)\s+\w+\s*(?=\s*[=])/g,
        /this\.\w+\s*=/g,
        /global\.\w+\s*=/g,
        /window\.\w+\s*=/g,
    ];
    for (const pattern of sharedPatterns) {
        if (content.match(pattern))
            return true;
    }
    return false;
}
function hasValidation(content, _line) {
    const validationPatterns = [
        /typeof\s+\w+\s*===\s*['"]/g,
        /instanceof/g,
        /\w+\s*!==\s*(null|undefined)/g,
        /\w+\s*!==\s*(null|undefined)/g,
        /isNaN/g,
        /Array\.isArray/g,
    ];
    for (const pattern of validationPatterns) {
        if (content.match(pattern))
            return true;
    }
    return false;
}
function contarLinhasAtePos(content, pos) {
    return content.slice(0, pos).split('\n').length;
}
export const detectorBugsMl = {
    nome: 'detector-bugs-ml',
    global: false,
    test(_relPath) {
        return /\.(ts|js|tsx|jsx|mjs|cjs)$/.test(_relPath);
    },
    aplicar(src, relPath, _ast, _fullPath, _contexto) {
        const ocorrencias = [];
        if (!src)
            return [];
        const features = extractFeatures(src, relPath);
        const { probability: bugProb, factors, gruposAtivos } = calcularProbabilidadeBug(features, relPath);
        if (bugProb > 0.75 && factors.length > 0 && gruposAtivos >= 2) {
            ocorrencias.push({
                tipo: 'ml-alta-probabilidade-bugs',
                nivel: bugProb > 0.8 ? 'aviso' : 'info',
                mensagem: getMessages().BugMlMensagens.altaProbabilidade((bugProb * 100).toFixed(0), factors.join(', ')),
                origem: 'detector-bugs-ml',
                relPath,
                linha: 0,
            });
        }
        for (const pattern of BUG_PATTERNS) {
            try {
                const match = pattern.detect(src, relPath);
                if (match && match.confidence > 0.5) {
                    const nivel = pattern.severity === 'erro' && match.confidence > 0.8 ? 'erro' : pattern.severity;
                    ocorrencias.push({
                        tipo: `ml-${pattern.id}`,
                        nivel,
                        mensagem: getMessages().BugMlMensagens.mlPattern((match.confidence * 100).toFixed(0), match.message),
                        origem: 'detector-bugs-ml',
                        relPath,
                        linha: match.line ?? 0,
                        sugestao: match.suggestion,
                    });
                }
            }
            catch { }
        }
        return ocorrencias;
    },
};
//# sourceMappingURL=detector-bugs-ml.js.map