import * as path from 'node:path';
import { parentPort, workerData } from 'node:worker_threads';
import { getMessages } from '../messages/index.js';
import { decifrarSintaxe } from '../parsing/index.js';
const port = parentPort;
const workerAstCache = new Map();
async function executarTecnicaEmArquivo(tecnica, arquivo, contexto, timeoutMs) {
    if (typeof tecnica?.aplicar !== 'function')
        return [];
    const execPromise = tecnica.aplicar(arquivo.content ?? '', arquivo.relPath, arquivo.ast ?? null, arquivo.fullCaminho ?? '', contexto);
    if (timeoutMs && timeoutMs > 0) {
        const msgs = getMessages().ExecutorExtraMensagens;
        return Promise.race([execPromise, new Promise((_, reject) => setTimeout(() => reject(new Error(msgs.timeoutTecnica(tecnica.nome, timeoutMs))), timeoutMs))]).then(result => Array.isArray(result) ? result : [], (err) => {
            return [{
                    arquivo: arquivo.relPath,
                    linha: 1,
                    coluna: 1,
                    tipo: 'ERRO_EXECUCAO',
                    mensagem: msgs.erroExecutarTecnica(tecnica.nome, err.message),
                    severidade: 'erro',
                    tecnica: tecnica.nome,
                    contexto: {
                        erro: err.message,
                        stack: err.stack
                    }
                }];
        });
    }
    try {
        const result = await execPromise;
        return Array.isArray(result) ? result : [];
    }
    catch (erro) {
        const err = erro;
        return [{
                arquivo: arquivo.relPath,
                linha: 1,
                coluna: 1,
                tipo: 'ERRO_EXECUCAO',
                mensagem: getMessages().ExecutorExtraMensagens.erroExecutarTecnica(tecnica.nome, err.message),
                severidade: 'erro',
                tecnica: tecnica.nome,
                contexto: {
                    erro: err.message,
                    stack: err.stack
                }
            }];
    }
}
function yieldEventLoop() {
    return new Promise(resolve => setImmediate(resolve));
}
async function processarLote(lote, contexto) {
    const resultados = [];
    const wd = workerData;
    const timeoutMs = Number(wd?.timeoutMs) || 0;
    const techniques = Array.isArray(wd?.techniques) ? wd.techniques : [];
    const files = Array.isArray(wd?.files) ? wd.files : [];
    for (const file of files) {
        if (!file.ast && file.content) {
            const ext = path.extname(file.relPath);
            const cached = workerAstCache.get(file.relPath);
            if (cached) {
                file.ast = cached;
            }
            else {
                try {
                    const parsed = await decifrarSintaxe(file.content, ext, { relPath: file.relPath });
                    if (parsed) {
                        file.ast = parsed;
                        workerAstCache.set(file.relPath, parsed);
                    }
                }
                catch {
                }
            }
        }
        const fileResultado = {
            arquivo: file.relPath,
            ocorrencias: [],
            tempos: [],
            erros: []
        };
        for (const tecnica of techniques) {
            if (tecnica.test && !tecnica.test(file.relPath))
                continue;
            try {
                const inicio = Date.now();
                const out = await executarTecnicaEmArquivo(tecnica, file, contexto, timeoutMs);
                const dur = Date.now() - inicio;
                if (out && Array.isArray(out))
                    fileResultado.ocorrencias.push(...out);
                fileResultado.tempos.push({
                    tecnica: tecnica.nome,
                    duracaoMs: dur
                });
            }
            catch (err) {
                fileResultado.erros.push(String(err && err.message ? err.message : err));
            }
            await yieldEventLoop();
        }
        resultados.push({
            sucesso: true,
            arquivo: file.relPath,
            ocorrencias: fileResultado.ocorrencias,
            tempoProcessamento: Date.now(),
            erros: fileResultado.erros,
            tempos: fileResultado.tempos
        });
        await yieldEventLoop();
    }
    return resultados;
}
async function main() {
    const HEARTBEAT_MS = 5000;
    let heartbeatInterval = null;
    try {
        const wd = workerData;
        const contexto = wd?.contexto;
        try {
            if (port && typeof port.postMessage === 'function') {
                heartbeatInterval = setInterval(() => {
                    try {
                        port.postMessage({
                            type: 'heartbeat',
                            ts: Date.now(),
                            workerId: process.pid
                        });
                    }
                    catch {
                    }
                }, HEARTBEAT_MS);
            }
        }
        catch {
        }
        const resultados = await processarLote(wd.lote ?? wd.files ?? [], contexto);
        const occurrences = resultados.flatMap(raiz => raiz.ocorrencias || []);
        const metrics = resultados.flatMap(raiz => raiz.tempos || []);
        const processedArquivos = Array.isArray(wd.files) ? wd.files.length : resultados.length || 0;
        if (port)
            port.postMessage({
                sucesso: true,
                resultados,
                workerId: process.pid,
                occurrences,
                metrics,
                processedArquivos,
                duration: Date.now()
            });
    }
    catch (erro) {
        try {
            if (port)
                port.postMessage({
                    sucesso: false,
                    erro: erro instanceof Error ? erro.message : String(erro),
                    stack: erro instanceof Error ? erro.stack : undefined,
                    workerId: process.pid
                });
        }
        catch {
        }
    }
    finally {
        if (heartbeatInterval)
            clearInterval(heartbeatInterval);
    }
}
main().catch(erro => {
    if (port)
        port.postMessage({
            sucesso: false,
            erro: erro instanceof Error ? erro.message : String(erro),
            stack: erro instanceof Error ? erro.stack : undefined,
            workerId: process.pid
        });
});
//# sourceMappingURL=worker-executor.js.map