import { config } from '../../../core/config/index.js';
import { detectarContextoProjeto } from '../../../shared/index.js';
import { criarAnalista, isBabelNode } from '../../../types/index.js';
function isNodePath(x) {
    return typeof x === 'object' && x !== null && 'node' in x && isBabelNode(x.node) && typeof x.traverse === 'function';
}
const LIMITE_LINHAS = config.ANALISE_LIMITES?.FUNCOES_LONGAS?.MAX_LINHAS ?? 30;
const LIMITE_PARAMETROS = config.ANALISE_LIMITES?.FUNCOES_LONGAS?.MAX_PARAMETROS ?? 4;
const LIMITE_ANINHAMENTO = config.ANALISE_LIMITES?.FUNCOES_LONGAS?.MAX_ANINHAMENTO ?? 3;
function pushOcorrencia(ocorrencias, tipo, nivel, relPath, linha, mensagem) {
    ocorrencias.push({ tipo, nivel, relPath, arquivo: relPath, linha, mensagem, origem: 'analista-funcoes-longas' });
}
function locValido(loc) {
    if (!loc || typeof loc !== 'object')
        return false;
    const s = loc.start;
    const e = loc.end;
    if (!s || typeof s !== 'object' || !e || typeof e !== 'object')
        return false;
    const sl = s.line;
    const el = e.line;
    return typeof sl === 'number' && typeof el === 'number' && sl >= 1 && el >= sl;
}
function verificarFuncao(fn, limitesAjustados, contextoArquivo, aninhamento, ocorrencias, relPath) {
    if (!locValido(fn.loc))
        return;
    const startLine = fn.loc.start.line;
    const endLine = fn.loc.end.line;
    const linhas = endLine - startLine + 1;
    if (linhas > limitesAjustados.linhas) {
        pushOcorrencia(ocorrencias, 'FUNCAO_LONGA', 'aviso', relPath, startLine, `Função com ${linhas} linhas (máx: ${limitesAjustados.linhas})`);
    }
    const paramsArr = fn.params;
    if (paramsArr && Array.isArray(paramsArr) && paramsArr.length > limitesAjustados.parametros) {
        pushOcorrencia(ocorrencias, 'MUITOS_PARAMETROS', 'aviso', relPath, startLine, `Função com muitos parâmetros (${paramsArr.length}, máx: ${limitesAjustados.parametros})`);
    }
    if (aninhamento > limitesAjustados.aninhamento) {
        pushOcorrencia(ocorrencias, 'FUNCAO_ANINHADA', 'aviso', relPath, startLine, `Função aninhada em nível ${aninhamento} (máx: ${limitesAjustados.aninhamento})`);
    }
    const semComentario = fn.leadingComments == null || (Array.isArray(fn.leadingComments) && fn.leadingComments.length === 0);
    if (semComentario && !contextoArquivo.isTest) {
        pushOcorrencia(ocorrencias, 'FUNCAO_SEM_COMENTARIO', 'info', relPath, startLine, 'Função sem comentário acima.');
    }
}
const TIPOS_FUNCAO = new Set(['FunctionDeclaration', 'FunctionExpression', 'ArrowFunctionExpression']);
function criarVisitors(analisarRecursivo, aninhamento) {
    return {
        FunctionDeclaration: (caminho) => analisarRecursivo(caminho, aninhamento + 1),
        FunctionExpression: (caminho) => analisarRecursivo(caminho, aninhamento + 1),
        ArrowFunctionExpression: (caminho) => analisarRecursivo(caminho, aninhamento + 1),
    };
}
function extrairFileNode(fileAst) {
    if (!fileAst || typeof fileAst !== 'object')
        return null;
    try {
        const maybeNode = fileAst.node;
        if (maybeNode && typeof maybeNode === 'object' && Array.isArray(maybeNode.body)) {
            return maybeNode;
        }
        if (Array.isArray(fileAst.body)) {
            return fileAst;
        }
    }
    catch {
        return null;
    }
    return null;
}
function verificarFuncoesDoBody(body, limitesAjustados, contextoArquivo, ocorrencias, relPath) {
    for (const child of body) {
        if (child && typeof child === 'object' && TIPOS_FUNCAO.has(child.type ?? '')) {
            verificarFuncao(child, limitesAjustados, contextoArquivo, 0, ocorrencias, relPath);
        }
    }
}
function processarFileAst(fileAst, limitesAjustados, contextoArquivo, ocorrencias, relPath) {
    if (!fileAst)
        return ocorrencias;
    const fileNode = extrairFileNode(fileAst);
    if (!fileNode)
        return ocorrencias;
    const body = Array.isArray(fileNode.body) ? fileNode.body : [];
    verificarFuncoesDoBody(body, limitesAjustados, contextoArquivo, ocorrencias, relPath);
    return ocorrencias;
}
export const analistaFuncoesLongas = criarAnalista({
    aplicar(src, relPath, ast, _fullPath) {
        const contextoArquivo = detectarContextoProjeto({
            arquivo: relPath,
            conteudo: src,
            relPath
        });
        const limitesAjustados = {
            linhas: contextoArquivo.isTest || contextoArquivo.isConfiguracao ? LIMITE_LINHAS * 2 : LIMITE_LINHAS,
            parametros: contextoArquivo.isTest ? LIMITE_PARAMETROS + 2 : LIMITE_PARAMETROS,
            aninhamento: LIMITE_ANINHAMENTO
        };
        const ocorrencias = [];
        function analisarRecursivo(path, aninhamento = 0) {
            const node = isNodePath(path) ? path.node : path;
            const ehFuncao = TIPOS_FUNCAO.has(node.type ?? '');
            if (ehFuncao) {
                verificarFuncao(node, limitesAjustados, contextoArquivo, aninhamento, ocorrencias, relPath);
            }
            const nivelAtual = ehFuncao ? aninhamento + 1 : aninhamento;
            if (isNodePath(path) && typeof path.traverse === 'function') {
                path.traverse(criarVisitors(analisarRecursivo, nivelAtual));
            }
        }
        if (ast && typeof ast.traverse === 'function') {
            analisarRecursivo(ast, 0);
            return ocorrencias;
        }
        return processarFileAst(ast, limitesAjustados, contextoArquivo, ocorrencias, relPath);
    },
    nome: 'analista-funcoes-longas',
    categoria: 'complexidade',
    descricao: 'Detecta funcoes muito longas, com muitos parametros, aninhamento excessivo ou sem comentario',
    limites: {
        linhas: LIMITE_LINHAS,
        params: LIMITE_PARAMETROS,
        aninhamento: LIMITE_ANINHAMENTO
    },
    test: (relPath) => relPath.endsWith('.js') || relPath.endsWith('.ts'),
    global: false
});
//# sourceMappingURL=analyst-functions-long.js.map