import { traverse } from '../../../core/config/index.js';
import { messages } from '../../../core/messages/index.js';
import { shouldSuppressOccurrence } from '../../../shared/helpers/index.js';
import { criarOcorrencia } from '../../../types/index.js';
const visitorAsync = {
    ExpressionStatement(path, state) {
        const expr = path.node.expression;
        if (expr.type === 'CallExpression') {
            const callee = expr.callee;
            if (callee.type === 'MemberExpression') {
                const propName = callee.property.type === 'Identifier' ? callee.property.name : '';
                if (propName === 'then' || propName === 'catch' || propName === 'finally') {
                    return;
                }
            }
            const funcName = getFunctionName(callee);
            if (funcName && isProbablyAsync(funcName)) {
                const binding = path.scope.getBinding(funcName);
                if (binding && binding.path.isFunction() && !binding.path.node.async) {
                    return;
                }
                const occurrences = state.ocorrencias || [];
                occurrences.push({
                    tipo: 'floating-promise',
                    linha: path.node.loc?.start.line || 0,
                    coluna: path.node.loc?.start.column || 0,
                    severidade: 'media',
                    nivel: 'aviso',
                    mensagem: messages.DetectorJsTsMensagens.floatingPromise(funcName),
                    relPath: state.relPath
                });
            }
        }
    },
    CallExpression(path, state) {
        const callee = path.node.callee;
        if (callee.type === 'MemberExpression') {
            const prop = callee.property;
            const propName = prop.type === 'Identifier' ? prop.name : '';
            if (propName === 'map' || propName === 'forEach') {
                const arg = path.node.arguments[0];
                if (arg && (arg.type === 'ArrowFunctionExpression' || arg.type === 'FunctionExpression')) {
                    if (arg.async) {
                        const isForEach = propName === 'forEach';
                        const occurrences = state.ocorrencias || [];
                        occurrences.push({
                            tipo: isForEach ? 'async-in-foreach' : 'async-in-map-without-all',
                            linha: path.node.loc?.start.line || 0,
                            coluna: path.node.loc?.start.column || 0,
                            severidade: 'alta',
                            nivel: 'erro',
                            mensagem: isForEach ? messages.DetectorJsTsMensagens.asyncInForEach(propName) : messages.DetectorJsTsMensagens.asyncInMap(propName),
                            relPath: state.relPath
                        });
                    }
                }
            }
        }
    }
};
function getFunctionName(node) {
    if (node.type === 'Identifier')
        return node.name;
    if (node.type === 'MemberExpression' && node.property.type === 'Identifier')
        return node.property.name;
    return null;
}
function isProbablyAsync(name) {
    if (/^(fetch|request|axios|prisma|db\.|database\.|api\.)/.test(name))
        return true;
    if (/Async$/.test(name))
        return true;
    if (['fetch', 'axios', 'insert', 'mutate', 'query', 'save', 'load'].includes(name))
        return true;
    return false;
}
export const analistaAntiPadroesAsync = {
    nome: 'anti-padroes-async',
    categoria: 'code-quality',
    descricao: 'Detecta antipadrões de promises e fluxos assíncronos (ex: floating promises, maps sem Promise.all)',
    visitor: visitorAsync,
    test: (relPath) => {
        return /\.(js|jsx|ts|tsx)$/.test(relPath) && !/\.test\.(ts|js)$/.test(relPath);
    },
    aplicar: async (src, relPath, ast) => {
        if (!ast)
            return [];
        if (shouldSuppressOccurrence('anti-padroes-async', relPath)) {
            return [];
        }
        const stateObj = {
            ocorrencias: [],
            relPath
        };
        const nodeToTraverse = ('node' in ast ? ast.node : ast);
        traverse(nodeToTraverse, visitorAsync, undefined, stateObj);
        return stateObj.ocorrencias.map(ocorrencia => criarOcorrencia({
            tipo: ocorrencia.tipo ?? 'anti-padroes-async',
            nivel: ocorrencia.nivel || 'aviso',
            mensagem: ocorrencia.mensagem,
            linha: ocorrencia.linha || 0,
            relPath,
            origem: 'anti-padroes-async'
        }));
    }
};
//# sourceMappingURL=detector-anti-patterns-async.js.map