import crypto from 'node:crypto';
import { promises as fs } from 'node:fs';
import path from 'node:path';
import { ExitCode, sair } from '../helpers/index.js';
import { config, formatPct } from '../../core/config/index.js';
import { messages } from '../../core/messages/index.js';
import { lerEstado, salvarEstado } from '../../shared/persistence/index.js';
import { Command } from 'commander';
const { log, logSistema } = messages;
async function obterCommit() {
    try {
        const { executarShellSeguro } = await import('../../core/utils/index.js');
        return executarShellSeguro('git rev-parse --short HEAD', {
            stdio: ['ignore', 'pipe', 'ignore']
        }).toString().trim();
    }
    catch {
        return undefined;
    }
}
function calcularHash(snapshot) {
    return crypto.createHash('sha1').update(JSON.stringify(snapshot, Object.keys(snapshot).sort())).digest('hex').slice(0, 10);
}
async function gerarBaseline(destDir, metricas) {
    const commit = await obterCommit();
    const base = {
        tipo: 'baseline',
        timestamp: new Date().toISOString(),
        commit,
        node: process.version,
        totalArquivos: metricas?.totalArquivos,
        tempoParsingMs: metricas?.tempoParsingMs,
        tempoAnaliseMs: metricas?.tempoAnaliseMs,
        cacheAstHits: metricas?.cacheAstHits,
        cacheAstMiss: metricas?.cacheAstMiss,
        analistasTop: Array.isArray(metricas?.analistas) ? metricas.analistas.slice().sort((arquivo, base) => base.duracaoMs - arquivo.duracaoMs).slice(0, 5).map(arquivo => ({
            nome: arquivo.nome,
            duracaoMs: arquivo.duracaoMs,
            ocorrencias: arquivo.ocorrencias
        })) : undefined
    };
    const hashConteudo = calcularHash(base);
    const snapshot = {
        ...base,
        hashConteudo
    };
    await fs.mkdir(destDir, {
        recursive: true
    });
    const nome = `baseline-${Date.now()}.json`;
    await salvarEstado(path.join(destDir, nome), snapshot);
    return snapshot;
}
async function carregarSnapshots(dir) {
    try {
        const arquivos = await fs.readdir(dir);
        const jsons = arquivos.filter(arquivo => arquivo.endsWith('.json'));
        const out = [];
        for (const arquivo of jsons) {
            try {
                const parsed = await lerEstado(path.join(dir, arquivo));
                if (parsed && parsed.tipo === 'baseline')
                    out.push(parsed);
            }
            catch {
            }
        }
        return out.sort((arquivo, base) => arquivo.timestamp.localeCompare(base.timestamp));
    }
    catch {
        return [];
    }
}
function diffPercent(arquivo, base) {
    if (!arquivo && !base)
        return 0;
    if (!arquivo || !base)
        return 0;
    if (arquivo === 0)
        return 0;
    return (base - arquivo) / arquivo * 100;
}
function compararSnapshots(base, atual) {
    const campos = ['tempoParsingMs', 'tempoAnaliseMs', 'cacheAstHits', 'cacheAstMiss', 'totalArquivos'];
    const diffs = campos.map(elem => {
        const anterior = base[elem];
        const novo = atual[elem];
        const anteriorNum = typeof anterior === 'number' ? anterior : undefined;
        const novoNum = typeof novo === 'number' ? novo : undefined;
        return {
            campo: elem,
            anterior: anteriorNum,
            novo: novoNum,
            variacaoPct: diffPercent(anteriorNum, novoNum)
        };
    });
    return diffs;
}
export function comandoPerf() {
    return new Command('perf').description(messages.CliComandoDesempMensagens.descricao).option('-d, --dir <dir>', 'Diretório de snapshots', config.PERF_SNAPSHOT_DIR).option('-j, --json', 'Saída JSON').option('-l, --limite <n>', 'Limite para regressão (%)', valor => Number(valor), 30).addCommand(new Command('baseline').description(messages.CliComandoDesempMensagens.descricaoBaseline).action(async (opts, cmd) => {
        try {
            const parent = cmd.parent?.opts?.() || {};
            const dir = parent.dir ? String(parent.dir) : config.PERF_SNAPSHOT_DIR;
            const globalMetrics = globalThis;
            const metricas = globalMetrics.__ULTIMAS_METRICAS_TUTANUS__;
            const snap = await gerarBaseline(dir, metricas || undefined);
            if (parent.json) {
                console.log(JSON.stringify({
                    gerado: true,
                    snapshot: snap
                }, null, 2));
            }
            else {
                log.sucesso(`Baseline gerada: commit=${snap.commit || 'n/a'} parsing=${snap.tempoParsingMs}ms analise=${snap.tempoAnaliseMs}ms`);
            }
        }
        catch (err) {
            log.erro(`Falha na geração de baseline: ${err instanceof Error ? err.message : String(err)}`);
            sair(ExitCode.Failure);
            return;
        }
    })).addCommand(new Command('compare').description(messages.CliComandoDesempMensagens.descricaoCompare).action(async (opts, cmd) => {
        const parent = cmd.parent?.opts?.() || {};
        const dir = parent.dir ? String(parent.dir) : config.PERF_SNAPSHOT_DIR;
        const limite = parent.limite;
        let snaps;
        try {
            snaps = await carregarSnapshots(dir);
        }
        catch (err) {
            log.erro(`Falha ao carregar snapshots: ${err instanceof Error ? err.message : String(err)}`);
            sair(ExitCode.Failure);
            return;
        }
        if (snaps.length < 2) {
            const msg = 'Menos de dois snapshots para comparar';
            if (parent.json)
                console.log(JSON.stringify({
                    erro: msg
                }));
            else
                log.aviso(msg);
            return;
        }
        const anterior = snaps[snaps.length - 2];
        const atual = snaps[snaps.length - 1];
        const diffs = compararSnapshots(anterior, atual);
        const regressao = diffs.filter(diretorio => diretorio.campo === 'tempoAnaliseMs' || diretorio.campo === 'tempoParsingMs').some(diretorio => diretorio.variacaoPct > limite);
        if (parent.json) {
            console.log(JSON.stringify({
                base: anterior.hashConteudo,
                atual: atual.hashConteudo,
                limite,
                diffs,
                regressao
            }, null, 2));
        }
        else {
            log.info(messages.CliComandoDesempMensagens.tituloComparacaoSnapshotsComIcone(messages.ICONES_DIAGNOSTICO.info));
            diffs.forEach(diretorio => {
                log.info(`  ${diretorio.campo}: ${diretorio.anterior ?? '-'} => ${diretorio.novo ?? '-'} (${formatPct(diretorio.variacaoPct)})`);
            });
            if (regressao)
                logSistema.performanceRegressaoDetectada(limite);
            else
                logSistema.performanceSemRegressoes();
        }
        if (regressao) {
            sair(ExitCode.Failure);
            return;
        }
    }));
}
//# sourceMappingURL=command-perf.js.map