import generateModule from '@babel/generator';
import { parse } from '@babel/parser';
import traverseModule from '@babel/traverse';
import { ExitCode, getFilesWithExtension, getSourceFiles, sair } from '../helpers/index.js';
import { chalk, config } from '../../core/config/index.js';
import { getMessages } from '../../core/messages/index.js';
import { Command } from 'commander';
import * as fs from 'fs';
import * as path from 'path';
const { log, CliComandoNamesMensagens } = getMessages();
const traverse = traverseModule.default || traverseModule;
const generate = generateModule.default || generateModule;
function parseMappingLine(line) {
    const parts = line.split('=');
    if (parts.length < 2)
        return null;
    const oldName = parts[0].trim();
    const newNome = parts[1].trim();
    if (!oldName || !newNome || oldName === newNome)
        return null;
    return {
        oldName,
        newName: newNome
    };
}
function loadMappingsFromFile(filePath, mappings, raizDir) {
    const content = fs.readFileSync(filePath, 'utf-8');
    const lines = content.split('\n');
    for (const line of lines) {
        const parsed = parseMappingLine(line);
        if (parsed) {
            const existing = mappings.get(parsed.oldName);
            if (existing !== undefined && existing !== parsed.newName && config.VERBOSE) {
                log.info(CliComandoNamesMensagens.conflitoMapeamento.replace('{nome}', parsed.oldName).replace('{arquivo}', path.relative(raizDir, filePath)).replace('{novo}', parsed.newName).replace('{anterior}', existing));
            }
            mappings.set(parsed.oldName, parsed.newName);
        }
    }
}
function loadMappings(raizDir, namesDir, mappingFile) {
    const mappings = new Map();
    if (mappingFile) {
        const resolved = path.resolve(raizDir, mappingFile);
        if (!fs.existsSync(resolved)) {
            log.erro(CliComandoNamesMensagens.arquivoMapeamentoNaoEncontrado.replace('{arquivo}', chalk.bold(mappingFile)));
            sair(ExitCode.Failure);
            return mappings;
        }
        loadMappingsFromFile(resolved, mappings, raizDir);
    }
    else {
        const mappingArquivo = path.resolve(namesDir, 'name.txt');
        try {
            loadMappingsFromFile(mappingArquivo, mappings, raizDir);
        }
        catch {
            try {
                const txtFiles = getFilesWithExtension(namesDir, '.txt');
                if (txtFiles.length === 0) {
                    log.erro(CliComandoNamesMensagens.nenhumArquivoMapeamento.replace('{pasta}', chalk.bold('names/')).replace('{comando}', chalk.bold('names --scan')));
                    sair(ExitCode.Failure);
                    return mappings;
                }
                for (const arquivo of txtFiles) {
                    loadMappingsFromFile(arquivo, mappings, raizDir);
                }
            }
            catch {
                log.erro(CliComandoNamesMensagens.pastaNaoEncontrada.replace('{pasta}', chalk.bold('names/')).replace('{comando}', chalk.bold('names --scan')));
                sair(ExitCode.Failure);
                return mappings;
            }
        }
    }
    return mappings;
}
function doScan(raizDir, srcDir, namesDir, legacy) {
    try {
        fs.mkdirSync(namesDir, { recursive: true });
    }
    catch {
    }
    log.info(chalk.cyan(CliComandoNamesMensagens.iniciandoVarredura));
    const files = getSourceFiles(srcDir);
    const allNomes = new Set();
    let arquivosComNomes = 0;
    for (const file of files) {
        try {
            const code = fs.readFileSync(file, 'utf-8');
            const ast = parse(code, {
                sourceType: 'module',
                plugins: ['typescript', 'decorators-legacy']
            });
            const variableNomes = new Set();
            traverse(ast, {
                VariableDeclarator(path) {
                    if (path.node.id.type === 'Identifier') {
                        const name = path.node.id.name;
                        variableNomes.add(name);
                        allNomes.add(name);
                    }
                }
            });
            if (variableNomes.size > 0) {
                const relPath = path.relative(srcDir, file);
                const outRelPath = relPath.replace(/\.(ts|js)$/i, '.txt');
                const outFile = path.join(namesDir, outRelPath);
                const outDir = path.dirname(outFile);
                try {
                    fs.mkdirSync(outDir, { recursive: true });
                }
                catch {
                }
                const sorted = Array.from(variableNomes).sort();
                const content = sorted.map((name) => `${name} = `).join('\n');
                fs.writeFileSync(outFile, content);
                arquivosComNomes++;
            }
        }
        catch {
            console.warn(CliComandoNamesMensagens.avisoErroProcessar.replace('{arquivo}', path.relative(raizDir, file)));
        }
    }
    if (legacy) {
        const saidaArquivo = path.resolve(namesDir, 'name.txt');
        const sortedNomes = Array.from(allNomes).sort();
        const content = sortedNomes.map((name) => `${name} = `).join('\n');
        fs.writeFileSync(saidaArquivo, content);
        log.sucesso(CliComandoNamesMensagens.varreduraConcluidaFragmentada
            .replace('{variaveis}', String(sortedNomes.length))
            .replace('{arquivos}', String(arquivosComNomes))
            .replace('{pastaFragmentada}', chalk.bold('names/'))
            .replace('{pastaAgregada}', chalk.bold(path.relative(raizDir, saidaArquivo))));
    }
    else {
        log.sucesso(CliComandoNamesMensagens.varreduraConcluidaEspelhada
            .replace('{variaveis}', String(allNomes.size))
            .replace('{arquivos}', String(arquivosComNomes))
            .replace('{pasta}', chalk.bold('names/')));
    }
}
function doApply(raizDir, srcDir, namesDir, mappingFile) {
    const mappings = loadMappings(raizDir, namesDir, mappingFile);
    if (mappings.size === 0) {
        log.aviso(CliComandoNamesMensagens.nenhumMapeamento);
        return;
    }
    const files = getSourceFiles(srcDir);
    let totalArquivosUpdated = 0;
    log.info(chalk.cyan(CliComandoNamesMensagens.iniciandoRenomeacao.replace('{total}', String(mappings.size))));
    for (const file of files) {
        try {
            const code = fs.readFileSync(file, 'utf-8');
            const ast = parse(code, {
                sourceType: 'module',
                plugins: ['typescript', 'decorators-legacy']
            });
            let changed = false;
            traverse(ast, {
                Identifier(path) {
                    const newNome = mappings.get(path.node.name);
                    if (newNome && newNome !== path.node.name) {
                        path.node.name = newNome;
                        changed = true;
                    }
                }
            });
            if (changed) {
                const output = generate(ast, {
                    retainLines: false,
                    comments: true,
                    compact: false
                }, code);
                fs.writeFileSync(file, output.code);
                if (config.VERBOSE)
                    log.info(CliComandoNamesMensagens.arquivoAtualizado.replace('{arquivo}', path.relative(raizDir, file)));
                totalArquivosUpdated++;
            }
        }
        catch {
        }
    }
    log.sucesso(CliComandoNamesMensagens.renomeacaoConcluida.replace('{total}', String(totalArquivosUpdated)));
}
export function comandoNames(aplicarFlagsGlobais) {
    return new Command('names')
        .description(CliComandoNamesMensagens.descricao)
        .option('--scan', CliComandoNamesMensagens.opcaoScan, false)
        .option('--apply', CliComandoNamesMensagens.opcaoApply, false)
        .option('--legacy', CliComandoNamesMensagens.opcaoAgregado, false)
        .option('--mapping <file>', CliComandoNamesMensagens.opcaoMapeamento)
        .action(async function (opts) {
        try {
            await aplicarFlagsGlobais(this.parent && typeof this.parent.opts === 'function'
                ? this.parent.opts()
                : {});
        }
        catch (err) {
            log.erro(CliComandoNamesMensagens.falhaAoAplicarFlags(err instanceof Error ? err.message : String(err)));
            sair(ExitCode.Failure);
            return;
        }
        const RAIZ_DIR = process.cwd();
        const SRC_DIR = path.resolve(RAIZ_DIR, 'src');
        const NAMES_DIR = path.resolve(RAIZ_DIR, 'names');
        if (!opts.scan && !opts.apply) {
            log.aviso(CliComandoNamesMensagens.nenhumaOperacaoSelecionada);
            return;
        }
        if (opts.scan) {
            doScan(RAIZ_DIR, SRC_DIR, NAMES_DIR, opts.legacy ?? false);
        }
        if (opts.apply) {
            doApply(RAIZ_DIR, SRC_DIR, NAMES_DIR, opts.mapping);
        }
    });
}
//# sourceMappingURL=command-names.js.map