/**
 * 完整反混淆脚本 v3 - 更强大的字符串解码
 * 策略：先执行完整的解码器设置，再遍历所有可能的调用参数
 */
const fs = require('fs');
const path = require('path');
const vm = require('vm');

const deobfuscateIO = require('obfuscator-io-deobfuscator').deobfuscate;
const beautify = require('js-beautify').js;

const outputDir = 'D:/temp/破解/cursorpro-0.4.5/deobfuscated_full/extension/out';

function ensureDir(dir) {
    if (!fs.existsSync(dir)) {
        fs.mkdirSync(dir, { recursive: true });
    }
}

/**
 * 提取并执行完整的解码器设置代码
 */
function setupFullDecoder(code) {
    // 查找字符串数组函数名
    const arrayFuncMatch = code.match(/function\s+(_0x[a-f0-9]+)\s*\(\)\s*\{/);
    if (!arrayFuncMatch) {
        console.log('  未找到字符串数组函数');
        return null;
    }
    const arrayFuncName = arrayFuncMatch[1];

    // 查找解码函数 - 更宽松的匹配
    const decoderPatterns = [
        /function\s+(_0x[a-f0-9]+)\s*\(\s*_0x[a-f0-9]+\s*,\s*_0x[a-f0-9]+\s*\)\s*\{\s*const\s+_0x[a-f0-9]+\s*=\s*_0x[a-f0-9]+\s*\(\s*\)\s*;/,
        /function\s+(_0x[a-f0-9]+)\s*\(\s*_0x[a-f0-9]+\s*,\s*_0x[a-f0-9]+\s*\)\s*\{/
    ];

    let decoderFuncName = null;
    for (const pattern of decoderPatterns) {
        const match = code.match(pattern);
        if (match) {
            decoderFuncName = match[1];
            break;
        }
    }

    if (!decoderFuncName) {
        console.log('  未找到解码函数');
        return null;
    }

    console.log(`  字符串数组函数: ${arrayFuncName}`);
    console.log(`  解码函数: ${decoderFuncName}`);

    // 提取从开头到解码函数结束的所有代码
    // 找到解码函数的结束位置
    const decoderStart = code.indexOf(`function ${decoderFuncName}(`);
    if (decoderStart === -1) {
        console.log('  未找到解码函数定义');
        return null;
    }

    // 找到解码函数的结束
    let braceCount = 0;
    let decoderEnd = decoderStart;
    let foundStart = false;
    for (let i = decoderStart; i < code.length; i++) {
        if (code[i] === '{') {
            braceCount++;
            foundStart = true;
        } else if (code[i] === '}') {
            braceCount--;
            if (foundStart && braceCount === 0) {
                decoderEnd = i + 1;
                break;
            }
        }
    }

    // 提取从文件开头到解码函数结束的所有代码
    // 这包括 vip 变量、IIFE、数组函数、解码函数
    const setupCode = code.substring(0, decoderEnd);

    // 创建沙盒并执行
    const sandbox = {
        console: { log: () => {}, warn: () => {}, error: () => {} },
        parseInt: parseInt,
        String: String,
        decodeURIComponent: decodeURIComponent
    };

    try {
        vm.runInNewContext(setupCode, sandbox);

        // 检查解码函数是否可用
        const testResult = vm.runInNewContext(`typeof ${decoderFuncName}`, sandbox);
        if (testResult !== 'function') {
            console.log(`  解码函数不可用: ${testResult}`);
            return null;
        }

        console.log(`  解码器设置成功`);
        return { sandbox, decoderFuncName, arrayFuncName, setupCode };
    } catch (e) {
        console.log(`  沙盒执行失败: ${e.message}`);
        // 尝试更激进的方法 - 执行整个文件的前半部分
        return setupAggressiveDecoder(code, decoderFuncName, arrayFuncName);
    }
}

/**
 * 更激进的解码器设置 - 执行更多代码
 */
function setupAggressiveDecoder(code, decoderFuncName, arrayFuncName) {
    console.log('  尝试激进模式...');

    // 找到第一个 exports 或 Object.defineProperty 之前的所有代码
    const exportIndex = Math.min(
        code.indexOf('Object.defineProperty(exports') !== -1 ? code.indexOf('Object.defineProperty(exports') : Infinity,
        code.indexOf('exports.') !== -1 ? code.indexOf('exports.') : Infinity
    );

    if (exportIndex === Infinity) {
        console.log('  未找到 exports 边界');
        return null;
    }

    const setupCode = code.substring(0, exportIndex);

    const sandbox = {
        console: { log: () => {}, warn: () => {}, error: () => {} },
        parseInt: parseInt,
        String: String,
        decodeURIComponent: decodeURIComponent,
        this: {},
        Object: Object
    };

    try {
        vm.runInNewContext(setupCode, sandbox);

        const testResult = vm.runInNewContext(`typeof ${decoderFuncName}`, sandbox);
        if (testResult === 'function') {
            console.log(`  激进模式成功`);
            return { sandbox, decoderFuncName, arrayFuncName, setupCode };
        }
    } catch (e) {
        console.log(`  激进模式失败: ${e.message}`);
    }

    return null;
}

/**
 * 收集并解码所有字符串调用
 */
function decodeAllStrings(code, decoder) {
    if (!decoder) return { code, decodedCount: 0 };

    const { sandbox, decoderFuncName } = decoder;

    // 收集所有解码函数别名
    const aliasPattern = new RegExp(`const\\s+(_0x[a-f0-9]+)\\s*=\\s*${decoderFuncName}\\s*;`, 'g');
    const aliases = [decoderFuncName];
    let match;
    while ((match = aliasPattern.exec(code)) !== null) {
        aliases.push(match[1]);
    }
    console.log(`  解码函数别名: ${aliases.join(', ')}`);

    // 构建所有可能的调用模式
    const decodedMap = new Map();
    let totalFound = 0;

    for (const alias of aliases) {
        // 匹配多种调用格式
        const patterns = [
            new RegExp(`${alias}\\s*\\(\\s*(0x[a-f0-9]+)\\s*,\\s*'([^']*)'\\s*\\)`, 'g'),
            new RegExp(`${alias}\\s*\\(\\s*(0x[a-f0-9]+)\\s*,\\s*"([^"]*)"\\s*\\)`, 'g'),
        ];

        for (const pattern of patterns) {
            let callMatch;
            while ((callMatch = pattern.exec(code)) !== null) {
                const fullMatch = callMatch[0];
                const num = callMatch[1];
                const key = callMatch[2];

                if (!decodedMap.has(fullMatch)) {
                    try {
                        const decoded = vm.runInNewContext(
                            `${decoderFuncName}(${num},'${key.replace(/'/g, "\\'")}')`,
                            sandbox
                        );
                        if (typeof decoded === 'string') {
                            decodedMap.set(fullMatch, decoded);
                            totalFound++;
                        }
                    } catch (e) {
                        // 解码失败，跳过
                    }
                }
            }
        }
    }

    console.log(`  找到 ${totalFound} 个字符串调用`);

    // 替换所有解码后的字符串
    let decodedCount = 0;
    for (const [original, decoded] of decodedMap) {
        // 选择合适的引号
        let replacement;
        if (decoded.includes('\n') || decoded.includes('\r')) {
            replacement = '`' + decoded.replace(/`/g, '\\`').replace(/\$/g, '\\$') + '`';
        } else if (decoded.includes("'") && !decoded.includes('"')) {
            replacement = JSON.stringify(decoded);
        } else {
            replacement = `'${decoded.replace(/\\/g, '\\\\').replace(/'/g, "\\'")}'`;
        }

        // 全局替换
        const escapedOriginal = original.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
        const regex = new RegExp(escapedOriginal, 'g');
        const before = code.length;
        code = code.replace(regex, replacement);
        if (code.length !== before) {
            decodedCount++;
        }
    }

    console.log(`  替换了 ${decodedCount} 个不同的字符串`);

    // 移除解码器代码（可选，让代码更干净）
    // 注释掉解码器相关代码而不是删除

    return { code, decodedCount };
}

/**
 * 清理混淆的变量引用
 */
function cleanObfuscatedReferences(code) {
    // 将 obj['property'] 转换为 obj.property
    code = code.replace(/\['([a-zA-Z_$][a-zA-Z0-9_$]*)'\]/g, '.$1');

    // 简化 !![] 为 true
    code = code.replace(/!!\[\]/g, 'true');

    // 简化 ![] 为 false
    code = code.replace(/!\[\]/g, 'false');

    return code;
}

/**
 * AST 清洗
 */
function astClean(code, filename) {
    try {
        console.log(`  [AST] 处理...`);
        return deobfuscateIO(code);
    } catch (e) {
        console.log(`  [AST] 失败: ${e.message}`);
        return code;
    }
}

/**
 * 处理单个文件
 */
async function processFile(inputPath, outputPath, filename) {
    console.log(`\n${'='.repeat(50)}`);
    console.log(`处理: ${filename}`);
    console.log('='.repeat(50));

    let code = fs.readFileSync(inputPath, 'utf8');
    console.log(`原始大小: ${(code.length / 1024).toFixed(2)} KB`);

    // Step 1: 设置解码器
    console.log('\nStep 1: 设置解码器...');
    const decoder = setupFullDecoder(code);

    // Step 2: 解码所有字符串
    console.log('\nStep 2: 解码字符串...');
    const decodeResult = decodeAllStrings(code, decoder);
    code = decodeResult.code;

    // Step 3: 清理混淆引用
    console.log('\nStep 3: 清理混淆引用...');
    code = cleanObfuscatedReferences(code);

    // Step 4: AST 清洗
    console.log('\nStep 4: AST 清洗...');
    code = astClean(code, filename);

    // Step 5: 再次清理（AST 可能引入新的混淆）
    console.log('\nStep 5: 二次清理...');
    code = cleanObfuscatedReferences(code);

    // Step 6: 美化
    console.log('\nStep 6: 美化代码...');
    code = beautify(code, {
        indent_size: 4,
        preserve_newlines: true,
        max_preserve_newlines: 2,
        space_in_empty_paren: true,
        jslint_happy: false,
        brace_style: 'collapse'
    });

    console.log(`\n处理后大小: ${(code.length / 1024).toFixed(2)} KB`);

    // 保存
    ensureDir(path.dirname(outputPath));
    fs.writeFileSync(outputPath, code);
    console.log(`已保存: ${outputPath}`);
}

// 主函数
async function main() {
    console.log('╔════════════════════════════════════════════════════╗');
    console.log('║         完整反混淆脚本 v3                           ║');
    console.log('║   完整解码器执行 + 批量字符串替换 + AST清洗          ║');
    console.log('╚════════════════════════════════════════════════════╝\n');

    const files = [
        { input: 'D:/temp/破解/cursorpro-0.4.5/原版本/extension/out/webview/provider.js', output: 'webview/provider.js' },
        { input: 'D:/temp/破解/cursorpro-0.4.5/原版本/extension/out/extension.js', output: 'extension.js' },
        { input: 'D:/temp/破解/cursorpro-0.4.5/原版本/extension/out/utils/account.js', output: 'utils/account.js' },
        { input: 'D:/temp/破解/cursorpro-0.4.5/原版本/extension/out/utils/sqlite.js', output: 'utils/sqlite.js' },
        { input: 'D:/temp/破解/cursorpro-0.4.5/原版本/extension/out/api/client.js', output: 'api/client.js' },
    ];

    for (const file of files) {
        if (fs.existsSync(file.input)) {
            await processFile(file.input, path.join(outputDir, file.output), file.output);
        } else {
            console.log(`\n跳过: ${file.input} (不存在)`);
        }
    }

    console.log('\n╔════════════════════════════════════════════════════╗');
    console.log('║                    完成！                           ║');
    console.log('╚════════════════════════════════════════════════════╝');
    console.log(`\n输出目录: ${outputDir}`);
}

main().catch(console.error);
