/**
 * 第五轮 - 全面清理剩余通用变量名
 */
const fs = require('fs');
const babel = require('@babel/core');
const traverse = require('@babel/traverse').default;
const generate = require('@babel/generator').default;
const t = require('@babel/types');

const inputPath = 'D:/temp/破解/cursorpro-0.4.5/deobfuscated_full/extension/out/webview/provider.js';
const outputPath = 'D:/temp/破解/cursorpro-0.4.5/deobfuscated_full/extension/out/webview/provider.js';

console.log('╔════════════════════════════════════════════════════╗');
console.log('║       第五轮全面清理                                ║');
console.log('╚════════════════════════════════════════════════════╝');

const code = fs.readFileSync(inputPath, 'utf8');
console.log(`读取文件: ${(code.length / 1024).toFixed(2)} KB`);

console.log('\n[1] 解析 AST...');
let ast;
try {
    ast = babel.parseSync(code, { sourceType: 'script' });
} catch (e) {
    console.error('AST 解析失败:', e.message);
    process.exit(1);
}

// 名称管理
const scopeNames = new Map();
function getUniqueName(scope, base) {
    const key = scope.uid;
    if (!scopeNames.has(key)) scopeNames.set(key, new Set());
    const used = scopeNames.get(key);
    let name = base, i = 1;
    while (used.has(name) || scope.hasBinding(name)) name = base + i++;
    used.add(name);
    return name;
}

console.log('\n[2] 收集所有通用变量的上下文...');

// 分析每个变量的完整上下文
const varAnalysis = new Map();

traverse(ast, {
    VariableDeclarator(path) {
        if (!t.isIdentifier(path.node.id)) return;
        const name = path.node.id.name;
        if (!/^(var|arg|v)\d+$/.test(name)) return;

        const analysis = {
            name,
            scope: path.scope,
            path,
            init: path.node.init,
            usages: [],
            suggestedName: null
        };

        // 收集使用点
        const binding = path.scope.getBinding(name);
        if (binding) {
            analysis.references = binding.referencePaths;
        }

        varAnalysis.set(path, analysis);
    }
});

console.log(`找到 ${varAnalysis.size} 个待处理变量`);

console.log('\n[3] 分析初始化表达式...');

for (const [path, analysis] of varAnalysis) {
    const init = analysis.init;
    if (!init) continue;

    // 1. 成员访问 xxx.yyy
    if (t.isMemberExpression(init)) {
        const prop = init.property?.name;
        if (prop === 'platform') analysis.suggestedName = 'platform';
        else if (prop === 'stdout') analysis.suggestedName = 'stdout';
        else if (prop === 'stderr') analysis.suggestedName = 'stderr';
        else if (prop === 'length') analysis.suggestedName = 'len';
        else if (prop === 'code') analysis.suggestedName = 'code';
        else if (prop === 'message') analysis.suggestedName = 'msg';
        else if (prop === 'name') analysis.suggestedName = 'name';
        else if (prop === 'value') analysis.suggestedName = 'value';
        else if (prop === 'type') analysis.suggestedName = 'type';
        else if (prop === 'path') analysis.suggestedName = 'pathStr';
        else if (prop === 'data') analysis.suggestedName = 'data';
        else if (prop === 'env') analysis.suggestedName = 'env';
        else if (prop === 'webview') analysis.suggestedName = 'webview';
    }

    // 2. 调用表达式
    if (t.isCallExpression(init)) {
        const callee = init.callee;
        if (t.isMemberExpression(callee)) {
            const obj = callee.object;
            const method = callee.property?.name;

            // vscode.xxx
            if (t.isIdentifier(obj, { name: 'vscode' })) {
                if (method === 'workspace') analysis.suggestedName = 'workspace';
                else if (method === 'window') analysis.suggestedName = 'window';
            }
            // workspace.getConfiguration
            if (method === 'getConfiguration') analysis.suggestedName = 'config';
            else if (method === 'get') analysis.suggestedName = 'configValue';
            else if (method === 'readFileSync') analysis.suggestedName = 'fileContent';
            else if (method === 'readdirSync') analysis.suggestedName = 'dirItems';
            else if (method === 'existsSync') analysis.suggestedName = 'exists';
            else if (method === 'statSync') analysis.suggestedName = 'stat';
            else if (method === 'join') analysis.suggestedName = 'joinedPath';
            else if (method === 'dirname') analysis.suggestedName = 'dirPath';
            else if (method === 'basename') analysis.suggestedName = 'baseName';
            else if (method === 'extname') analysis.suggestedName = 'ext';
            else if (method === 'resolve') analysis.suggestedName = 'resolvedPath';
            else if (method === 'split') analysis.suggestedName = 'parts';
            else if (method === 'trim') analysis.suggestedName = 'trimmed';
            else if (method === 'match') analysis.suggestedName = 'match';
            else if (method === 'replace') analysis.suggestedName = 'replaced';
            else if (method === 'toLowerCase') analysis.suggestedName = 'lower';
            else if (method === 'toUpperCase') analysis.suggestedName = 'upper';
            else if (method === 'slice') analysis.suggestedName = 'sliced';
            else if (method === 'substring') analysis.suggestedName = 'substr';
            else if (method === 'indexOf') analysis.suggestedName = 'idx';
            else if (method === 'find') analysis.suggestedName = 'found';
            else if (method === 'filter') analysis.suggestedName = 'filtered';
            else if (method === 'map') analysis.suggestedName = 'mapped';
            else if (method === 'forEach') continue; // void
            else if (method === 'push') continue;
            else if (method === 'pop') analysis.suggestedName = 'popped';
            else if (method === 'shift') analysis.suggestedName = 'shifted';
            else if (method === 'exec') analysis.suggestedName = 'execMatch';
            else if (method === 'test') analysis.suggestedName = 'isMatch';
            else if (method === 'normalize') analysis.suggestedName = 'normalized';
            else if (method === 'toString') analysis.suggestedName = 'str';
            else if (method === 'parse') analysis.suggestedName = 'parsed';
            else if (method === 'stringify') analysis.suggestedName = 'jsonStr';
            else if (method === 'keys') analysis.suggestedName = 'keys';
            else if (method === 'values') analysis.suggestedName = 'values';
            else if (method === 'entries') analysis.suggestedName = 'entries';
        }

        // 独立函数调用
        if (t.isIdentifier(callee)) {
            const fn = callee.name;
            if (fn === 'require') analysis.suggestedName = 'module';
            else if (fn === 'parseInt') analysis.suggestedName = 'num';
            else if (fn === 'parseFloat') analysis.suggestedName = 'num';
            else if (fn === 'String') analysis.suggestedName = 'str';
            else if (fn === 'Number') analysis.suggestedName = 'num';
            else if (fn === 'Boolean') analysis.suggestedName = 'bool';
            else if (fn === 'Array') analysis.suggestedName = 'arr';
            else if (fn === 'Object') analysis.suggestedName = 'obj';
        }
    }

    // 3. await 表达式
    if (t.isAwaitExpression(init)) {
        const arg = init.argument;
        if (t.isCallExpression(arg) && t.isMemberExpression(arg.callee)) {
            const method = arg.callee.property?.name;
            if (method === 'exec' || method === 'execAsync') analysis.suggestedName = 'execResult';
            else if (method?.startsWith('get')) {
                const rest = method.slice(3);
                analysis.suggestedName = rest.charAt(0).toLowerCase() + rest.slice(1);
            }
            else if (method?.startsWith('_get')) {
                const rest = method.slice(4);
                analysis.suggestedName = rest.charAt(0).toLowerCase() + rest.slice(1);
            }
            else if (method?.startsWith('read')) analysis.suggestedName = 'content';
            else if (method?.startsWith('fetch')) analysis.suggestedName = 'response';
        }
    }

    // 4. new 表达式
    if (t.isNewExpression(init)) {
        const callee = init.callee;
        if (t.isIdentifier(callee)) {
            const cls = callee.name;
            if (cls === 'RegExp') analysis.suggestedName = 'regex';
            else if (cls === 'Date') analysis.suggestedName = 'date';
            else if (cls === 'Map') analysis.suggestedName = 'map';
            else if (cls === 'Set') analysis.suggestedName = 'set';
            else if (cls === 'Promise') analysis.suggestedName = 'promise';
            else if (cls === 'Error') analysis.suggestedName = 'error';
            else if (cls === 'URL') analysis.suggestedName = 'url';
        }
    }

    // 5. 字面量
    if (t.isStringLiteral(init)) analysis.suggestedName = 'str';
    if (t.isNumericLiteral(init)) analysis.suggestedName = 'num';
    if (t.isBooleanLiteral(init)) analysis.suggestedName = init.value ? 'isTrue' : 'isFalse';
    if (t.isNullLiteral(init)) analysis.suggestedName = 'value';
    if (t.isArrayExpression(init)) analysis.suggestedName = 'arr';
    if (t.isObjectExpression(init)) analysis.suggestedName = 'obj';
    if (t.isRegExpLiteral(init)) analysis.suggestedName = 'regex';

    // 6. 二元/逻辑表达式
    if (t.isBinaryExpression(init)) {
        const op = init.operator;
        if (['===', '!==', '==', '!=', '>', '<', '>=', '<='].includes(op)) {
            analysis.suggestedName = 'cond';
        } else if (['+', '-', '*', '/', '%'].includes(op)) {
            analysis.suggestedName = 'result';
        }
    }
    if (t.isLogicalExpression(init)) analysis.suggestedName = 'cond';
    if (t.isConditionalExpression(init)) analysis.suggestedName = 'value';
    if (t.isUnaryExpression(init, { operator: '!' })) analysis.suggestedName = 'not';
}

// 统计推断结果
let suggested = 0;
for (const [, analysis] of varAnalysis) {
    if (analysis.suggestedName) suggested++;
}
console.log(`推断了 ${suggested} / ${varAnalysis.size} 个变量名`);

console.log('\n[4] 应用重命名...');
let renamed = 0;

for (const [path, analysis] of varAnalysis) {
    if (!analysis.suggestedName) continue;

    const binding = analysis.scope.getBinding(analysis.name);
    if (!binding) continue;

    const newName = getUniqueName(analysis.scope, analysis.suggestedName);

    path.node.id.name = newName;
    binding.referencePaths.forEach(ref => {
        ref.node.name = newName;
    });
    renamed++;
}

console.log(`重命名了 ${renamed} 个变量`);

// 处理函数参数
console.log('\n[5] 处理函数/回调参数...');
let paramRenamed = 0;

traverse(ast, {
    'ArrowFunctionExpression|FunctionExpression'(path) {
        const params = path.node.params;

        // 获取父级上下文
        const parent = path.parent;
        let contextHint = null;

        if (t.isCallExpression(parent) && t.isMemberExpression(parent.callee)) {
            const method = parent.callee.property?.name;
            if (['forEach', 'map', 'filter', 'find', 'some', 'every', 'reduce'].includes(method)) {
                contextHint = 'iterator';
            } else if (method === 'then') {
                contextHint = 'then';
            } else if (method === 'catch') {
                contextHint = 'catch';
            } else if (method?.includes('Message')) {
                contextHint = 'message';
            } else if (method?.includes('Change')) {
                contextHint = 'change';
            }
        }

        params.forEach((param, idx) => {
            if (!t.isIdentifier(param)) return;
            if (!/^arg\d+$/.test(param.name)) return;

            let newName = null;

            if (contextHint === 'iterator') {
                newName = idx === 0 ? 'item' : idx === 1 ? 'index' : 'arr';
            } else if (contextHint === 'then') {
                newName = 'res';
            } else if (contextHint === 'catch') {
                newName = 'err';
            } else if (contextHint === 'message') {
                newName = 'msg';
            } else if (contextHint === 'change') {
                newName = 'change';
            } else {
                newName = 'param' + idx;
            }

            if (newName) {
                const binding = path.scope.getBinding(param.name);
                if (binding) {
                    const finalName = getUniqueName(path.scope, newName);
                    param.name = finalName;
                    binding.referencePaths.forEach(ref => {
                        ref.node.name = finalName;
                    });
                    paramRenamed++;
                }
            }
        });
    }
});

console.log(`重命名了 ${paramRenamed} 个参数`);

console.log('\n[6] 生成代码...');
const output = generate(ast, { comments: false, compact: false }, code);

let finalCode = output.code;
finalCode = finalCode.replace(/\["([a-zA-Z_$][a-zA-Z0-9_$]*)"\]/g, '.$1');

console.log('\n[7] 保存文件...');
fs.writeFileSync(outputPath, finalCode);
console.log(`新文件大小: ${(finalCode.length / 1024).toFixed(2)} KB`);

const remaining = [...new Set((finalCode.match(/\b(var|arg|v)\d+\b/g) || []))];
console.log(`剩余通用变量名: ${remaining.length} 个`);

console.log('\n✅ 完成！');
