import { config, isMetaPath } from '../config/index.js';
import { messages } from '../messages/index.js';
import { lerEstado } from '../../shared/persistence/index.js';
import pLimit from 'p-limit';
import * as path from 'path';
import { ocorrenciaParseErro } from '../../types/index.js';
import { executarInquisicao as executarExecucao, registrarUltimasMetricas } from './executor.js';
import { scanRepository } from './scanner.js';
function getGlobal() {
    return globalThis;
}
function getObjectKeysLen(value) {
    if (typeof value === 'object' && value !== null) {
        return Object.keys(value).length;
    }
    return 0;
}
function detalheErro(erro) {
    if (!erro || typeof erro !== 'object') {
        return { mensagem: String(erro) };
    }
    const errObj = erro;
    return {
        mensagem: errObj.message ?? '',
        linha: errObj.loc?.line,
        coluna: errObj.loc?.column,
        detalhe: errObj.message
    };
}
function getSimbolosLog() {
    const logAny = messages.log;
    const s = logAny.simbolos;
    if (s && typeof s === 'object')
        return s;
    return {
        info: '[INFO]',
        sucesso: '[OK]',
        erro: '[ERRO]',
        aviso: '[AVISO]',
        debug: '[BUG]',
        fase: '[ORANGE]',
        passo: '[.]',
        scan: '[BUSCA]',
        guardian: '[SHIELD]',
        pasta: '[FOLDER]'
    };
}
const simbolosLogExec = new Proxy({}, {
    get(_target, prop) { return getSimbolosLog()[prop]; }
});
function infoDestaque(mensagem) {
    const logInfo = messages.log;
    if (typeof logInfo.infoDestaque === 'function') {
        logInfo.infoDestaque(mensagem);
    }
    else {
        logInfo.info(mensagem);
    }
}
async function handleParseError(entry) {
    const g = getGlobal();
    const lista = g.__TUTANUS_PARSE_ERROS__ || [];
    try {
        const babel = await import('@babel/parser');
        try {
            babel.parse(entry.content || '', {
                sourceType: 'unambiguous',
                plugins: ['typescript', 'jsx', 'decorators-legacy', 'classProperties']
            });
            lista.push(ocorrenciaParseErro({
                mensagem: messages.InquisidorMensagens.parseAstNaoGerada,
                relPath: entry.relPath,
                origem: 'parser'
            }));
        }
        catch (erro) {
            const { mensagem, linha, coluna, detalhe } = detalheErro(erro);
            lista.push(ocorrenciaParseErro({
                mensagem: messages.InquisidorMensagens.parseErro(mensagem || messages.InquisidorMensagens.parseAstNaoGerada),
                relPath: entry.relPath,
                origem: 'parser',
                linha,
                coluna,
                detalhe
            }));
        }
    }
    catch {
        lista.push(ocorrenciaParseErro({
            mensagem: messages.InquisidorMensagens.parseAstNaoGerada,
            relPath: entry.relPath,
            origem: 'parser'
        }));
    }
    g.__TUTANUS_PARSE_ERROS__ = lista;
}
async function aplicarPriorizacao(entries, metaSet) {
    if (!config.ANALISE_PRIORIZACAO_ENABLED || !config.ANALISE_INCREMENTAL_STATE_PATH)
        return entries;
    try {
        const inc = await lerEstado(config.ANALISE_INCREMENTAL_STATE_PATH).catch(() => null);
        if (!inc?.arquivos)
            return entries;
        const pesos = {
            duracaoMs: config.ANALISE_PRIORIZACAO_PESOS?.duracaoMs ?? 1,
            ocorrencias: config.ANALISE_PRIORIZACAO_PESOS?.ocorrencias ?? 2,
            penalidadeReuso: config.ANALISE_PRIORIZACAO_PESOS?.penalidadeReuso ?? 0.5
        };
        const scored = entries.map(entry => {
            const hist = inc.arquivos[entry.relPath];
            let score = 0;
            if (hist) {
                let dur = 0;
                let occ = 0;
                if (hist.analistas) {
                    for (const a of Object.values(hist.analistas)) {
                        dur += a.duracaoMs;
                        occ += a.ocorrencias;
                    }
                }
                else {
                    occ = hist.ocorrencias?.length || 0;
                }
                score = dur * pesos.duracaoMs + occ * pesos.ocorrencias - (hist.reaproveitadoCount || 0) * pesos.penalidadeReuso;
            }
            return { entry, score };
        });
        scored.sort((a, b) => b.score - a.score);
        const prioritarios = scored.filter(s => !metaSet.has(s.entry.relPath)).map(s => s.entry);
        const metas = scored.filter(s => metaSet.has(s.entry.relPath)).map(s => s.entry);
        const result = [...prioritarios, ...metas];
        if (config.LOG_ESTRUTURADO) {
            messages.log.info(JSON.stringify({
                tipo: 'priorizacao',
                estrategia: 'historico-incremental',
                top: prioritarios.slice(0, 10).map(e => ({ arq: e.relPath, score: scored.find(s => s.entry.relPath === e.relPath)?.score })),
                metaEmpurrados: metas.length
            }));
        }
        else {
            const exibidos = prioritarios.slice(0, 5).map(e => e.relPath).join(', ') || '-';
            messages.log.info(messages.InquisidorExtraMensagens.priorizacaoAplicada.replace('{exibidos}', exibidos));
            if (metas.length) {
                messages.log.info(messages.InquisidorExtraMensagens.arquivosMetaMovidos.replace('{icone}', simbolosLogExec.info).replace('{total}', String(metas.length)));
            }
        }
        return result;
    }
    catch (erro) {
        if (config.DEV_MODE)
            messages.log.erro(`Falha priorização: ${erro instanceof Error ? erro.message : String(erro)}`);
        return entries;
    }
}
const EXTENSOES_COM_AST = new Set(Array.isArray(config.SCANNER_EXTENSOES_COM_AST) ? config.SCANNER_EXTENSOES_COM_AST : ['.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs']);
export async function prepararComAst(entries, baseDir) {
    const g = getGlobal();
    const cache = g.__TUTANUS_AST_CACHE__ || new Map();
    if (!g.__TUTANUS_AST_CACHE__)
        g.__TUTANUS_AST_CACHE__ = cache;
    const metricas = g.__TUTANUS_METRICAS__ || { parsingTimeMs: 0, cacheHits: 0, cacheMiss: 0 };
    metricas.parsingTimeMs = 0;
    metricas.cacheHits = 0;
    metricas.cacheMiss = 0;
    g.__TUTANUS_METRICAS__ = metricas;
    const CONCURRENCY = Number(config.AUTOANALISE_CONCURRENCY ?? 50);
    const limit = pLimit(CONCURRENCY);
    return Promise.all(entries.map(entry => limit(async () => {
        let ast;
        const ext = path.extname(entry.relPath);
        const mtimeMs = entry.ultimaModificacao ?? 0;
        const cacheValido = mtimeMs > 0;
        const nomeLower = entry.relPath.toLowerCase();
        const extEfetiva = nomeLower.endsWith('.d.ts') ? '.d.ts' : nomeLower.endsWith('.map') ? '.map' : ext;
        if (entry.content && EXTENSOES_COM_AST.has(extEfetiva)) {
            if (config.ANALISE_AST_CACHE_ENABLED && cacheValido) {
                const anterior = cache.get(entry.relPath);
                if (anterior && anterior.mtimeMs === mtimeMs) {
                    ast = anterior.ast;
                    metricas.cacheHits++;
                }
            }
            try {
                if (!ast) {
                    const inicioParse = performance.now();
                    const parsed = await import('../parsing/index.js').then(mod => mod.decifrarSintaxe(entry.content || '', extEfetiva, { relPath: entry.relPath }));
                    if (parsed && typeof parsed === 'object' && getObjectKeysLen(parsed) > 0) {
                        ast = undefined;
                    }
                    else if (parsed == null) {
                        if (/(^|\/)node_modules(\/|\\)/.test(entry.relPath)) {
                            ast = undefined;
                        }
                        else {
                            await handleParseError(entry);
                        }
                    }
                    metricas.parsingTimeMs += performance.now() - inicioParse;
                    metricas.cacheMiss++;
                    if (config.ANALISE_AST_CACHE_ENABLED && cacheValido) {
                        cache.set(entry.relPath, { mtimeMs, size: 0, ast });
                    }
                }
            }
            catch (erro) {
                const err = erro instanceof Error ? erro : new Error(String(erro));
                messages.log.erro(messages.InquisidorMensagens.falhaGerarAst(entry.relPath, err.message));
                const lista = g.__TUTANUS_PARSE_ERROS__ || [];
                lista.push(ocorrenciaParseErro({
                    mensagem: messages.InquisidorMensagens.parseErro(err.message),
                    relPath: entry.relPath,
                    origem: 'parser'
                }));
                g.__TUTANUS_PARSE_ERROS__ = lista;
            }
        }
        return {
            ...entry,
            ast,
            content: entry.content ?? null,
            fullCaminho: typeof entry.fullCaminho === 'string' ? entry.fullCaminho : path.resolve(baseDir, entry.relPath)
        };
    })));
}
export async function iniciarInquisicao(baseDir = process.cwd(), options = {}, tecnicas) {
    const { includeContent = true, incluirMetadados: _incluirMetadados = true, skipExec = false } = options;
    messages.log.info(messages.InquisidorExtraMensagens.iniciandoInquisicao.replace('{icone}', simbolosLogExec.scan).replace('{diretorio}', baseDir));
    function onScanProgress(msg) {
        try {
            const progressData = JSON.parse(msg);
            if (progressData.tipo === 'diretorio') {
                const g = getGlobal();
                g.__TUTANUS_DIR_COUNT__ = (g.__TUTANUS_DIR_COUNT__ || 0) + 1;
                if (!g.__TUTANUS_DIR_SAMPLES__)
                    g.__TUTANUS_DIR_SAMPLES__ = [];
                if (g.__TUTANUS_DIR_SAMPLES__.length < 5) {
                    g.__TUTANUS_DIR_SAMPLES__.push(progressData.caminho);
                }
            }
            else if (progressData.tipo === 'erro') {
                messages.log.erro(`Erro ao ${progressData.acao} ${progressData.caminho}: ${progressData.mensagem}`);
            }
        }
        catch {
            if (msg && msg.includes('[AVISO]'))
                messages.log.aviso(msg);
        }
    }
    const fileMap = await scanRepository(baseDir, {
        includeContent,
        onProgress: onScanProgress
    });
    let entriesBase = Object.values(fileMap);
    const metaSet = new Set(entriesBase.filter(erro => isMetaPath(erro.relPath)).map(erro => erro.relPath));
    entriesBase = await aplicarPriorizacao(entriesBase, metaSet);
    const fileEntries = entriesBase.map(entry => ({
        ...entry,
        ast: undefined,
        fullCaminho: typeof entry.fullCaminho === 'string' ? entry.fullCaminho : path.resolve(baseDir, entry.relPath)
    }));
    try {
        const g = getGlobal();
        const totalDirs = g.__TUTANUS_DIR_COUNT__ || 0;
        const amostra = Array.isArray(g.__TUTANUS_DIR_SAMPLES__) ? g.__TUTANUS_DIR_SAMPLES__ : [];
        if (config.LOG_ESTRUTURADO) {
            messages.log.info(JSON.stringify({
                tipo: 'varredura_preliminar',
                totalDiretorios: totalDirs,
                amostraDiretorios: amostra
            }));
        }
    }
    catch {
    }
    let totalArquivos = fileEntries.length;
    let ocorrencias = [];
    if (!skipExec) {
        let tecnicasEfetivas = tecnicas;
        if (!tecnicasEfetivas) {
            const mod = await import('../../analysts/registry/registry.js');
            tecnicasEfetivas = mod.registroAnalistas;
        }
        const execRes = await executarExecucao(fileEntries, tecnicasEfetivas, baseDir, undefined);
        totalArquivos = execRes.totalArquivos;
        ocorrencias = execRes.ocorrencias;
    }
    const parseErros = getGlobal().__TUTANUS_PARSE_ERROS__ || [];
    if (parseErros.length) {
        getGlobal().__TUTANUS_PARSE_ERROS_ORIGINAIS__ = parseErros.length;
        if (config.PARSE_ERRO_AGRUPAR) {
            const porArquivo = {};
            for (const pe of parseErros) {
                const chave = pe.relPath || '__desconhecido__';
                (porArquivo[chave] = porArquivo[chave] || []).push(pe);
            }
            for (const [arq, lista] of Object.entries(porArquivo)) {
                if (lista.length <= (config.PARSE_ERRO_MAX_POR_ARQUIVO || 1)) {
                    ocorrencias.push(...lista);
                }
                else {
                    ocorrencias.push(ocorrenciaParseErro({
                        mensagem: messages.InquisidorMensagens.parseErrosAgregados(lista.length),
                        relPath: arq || '',
                        origem: 'parser'
                    }));
                }
            }
        }
        else {
            ocorrencias.push(...parseErros);
        }
    }
    if (!skipExec) {
        messages.log.sucesso(messages.InquisidorExtraMensagens.inquisicaoConcluida.replace('{total}', String(ocorrencias.length)));
    }
    else if (!config.COMPACT_MODE) {
        infoDestaque(`Varredura concluída: total de arquivos: ${fileEntries.length}`);
    }
    return {
        totalArquivos,
        ocorrencias,
        arquivosAnalisados: fileEntries.map(arquivo => arquivo.relPath),
        timestamp: Date.now(),
        duracaoMs: 0,
        fileEntries,
        guardian: undefined
    };
}
export { executarExecucao as executarInquisicao, registrarUltimasMetricas };
//# sourceMappingURL=inquisitor.js.map