#!/usr/bin/env python3
# build_universal.py - 通用打包脚本
# 支持CLI和GUI的跨平台打包

import os
import sys
import platform
import subprocess
import argparse
from pathlib import Path


def get_platform_config():
    """获取平台特定配置"""
    system = platform.system().lower()

    configs = {
        'darwin': {  # macOS
            'icon': 'icon.icns',
            'gui_base': None,
            'extension': '.app',
            'separator': ':'
        },
        'windows': {  # Windows
            'icon': 'icon.ico',
            'gui_base': 'Win32GUI',
            'extension': '.exe',
            'separator': ';'
        },
        'linux': {   # Linux
            'icon': 'icon.png',
            'gui_base': None,
            'extension': '',
            'separator': ':'
        }
    }

    return configs.get(system, configs['linux'])


def check_environment():
    """检查环境和依赖"""
    print("🔍 检查环境...")

    # 获取项目根目录路径
    script_dir = Path(__file__).parent
    project_root = script_dir.parent

    print(f"📁 脚本目录: {script_dir}")
    print(f"📁 项目根目录: {project_root}")

    # 切换到项目根目录
    os.chdir(project_root)
    print(f"📁 工作目录: {os.getcwd()}")

    # 检查Python版本
    python_version = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
    print(f"🐍 Python版本: {python_version}")

    # 检查是否在虚拟环境中
    if hasattr(sys, 'real_prefix') or (hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix):
        print("✅ 已在虚拟环境中")
    else:
        print("⚠️  警告：不在虚拟环境中，建议使用虚拟环境")

    # 检查必要文件
    required_files = ['oc_cli.py', 'oc_gui.py', 'ObjectiveC', '配置文件']
    for file in required_files:
        if os.path.exists(file):
            print(f"✅ 找到: {file}")
        else:
            print(f"❌ 缺失: {file}")
            return False

    return True


def install_pyinstaller():
    """安装PyInstaller"""
    print("📦 安装PyInstaller...")
    try:
        subprocess.run([sys.executable, '-m', 'pip', 'install', 'pyinstaller'],
                       check=True, capture_output=True)
        print("✅ PyInstaller安装完成")
        return True
    except subprocess.CalledProcessError as e:
        print(f"❌ PyInstaller安装失败: {e}")
        return False


def build_cli():
    """打包CLI工具"""
    print("🔧 打包CLI工具...")

    config = get_platform_config()

    # 清理之前的构建
    import shutil
    if os.path.exists('dist/oc_cli'):
        shutil.rmtree('dist/oc_cli', ignore_errors=True)
    if os.path.exists('build/oc_cli'):
        shutil.rmtree('build/oc_cli', ignore_errors=True)
    if os.path.exists('oc_cli.spec'):
        os.remove('oc_cli.spec')

    cmd = [
        'pyinstaller',
        '--onefile',
        '--console',
        '--name=oc_cli',
        f'--add-data=ObjectiveC{config["separator"]}ObjectiveC',
        f'--add-data=配置文件{config["separator"]}配置文件',
        '--exclude-module=tkinter',
        '--exclude-module=matplotlib',
        '--exclude-module=pandas',
        '--hidden-import=yaml',
        '--hidden-import=pbxproj',
        '--hidden-import=numpy',
        '--hidden-import=markdown',
        '--hidden-import=bs4',
        '--hidden-import=pygments',
        '--hidden-import=PIL',
        '--hidden-import=PIL.Image',
        '--clean',
        'oc_cli.py'
    ]

    try:
        result = subprocess.run(
            cmd, check=True, capture_output=True, text=True)

        # 检查输出文件
        cli_path = f'dist/oc_cli{config["extension"] if config["extension"] != ".app" else ""}'
        if os.path.exists(cli_path):
            # 设置执行权限
            if platform.system() != 'Windows':
                os.chmod(cli_path, 0o755)

            file_size = os.path.getsize(cli_path) / (1024 * 1024)  # MB
            print(f"✅ CLI工具打包完成")
            print(f"📁 输出位置: {cli_path}")
            print(f"📊 文件大小: {file_size:.1f} MB")

            # 测试可执行文件
            try:
                test_result = subprocess.run([cli_path, '--help'],
                                             capture_output=True, timeout=10)
                if test_result.returncode == 0:
                    print("✅ CLI工具测试通过")
                else:
                    print("⚠️  CLI工具测试失败")
            except:
                print("⚠️  无法测试CLI工具")

            return True
        else:
            print("❌ CLI打包失败 - 未找到输出文件")
            return False

    except subprocess.CalledProcessError as e:
        print(f"❌ CLI打包失败: {e}")
        if e.stderr:
            print(f"错误信息: {e.stderr}")
        return False


def build_gui():
    """打包GUI应用"""
    print("🎨 打包GUI应用...")

    config = get_platform_config()

    # 清理之前的构建
    import shutil
    for pattern in ['*混淆工具*', 'OC*']:
        import glob
        for path in glob.glob(f'dist/{pattern}'):
            if os.path.isdir(path):
                shutil.rmtree(path, ignore_errors=True)
            else:
                os.remove(path)
        for path in glob.glob(f'build/{pattern}'):
            if os.path.isdir(path):
                shutil.rmtree(path, ignore_errors=True)

    if os.path.exists('oc_gui.spec'):
        os.remove('oc_gui.spec')

    app_name = "闲闲SDK混淆工具"

    cmd = [
        'pyinstaller',
        '--onefile',
        '--windowed',
        f'--name={app_name}',
        f'--add-data=ObjectiveC{config["separator"]}ObjectiveC',
        f'--add-data=配置文件{config["separator"]}配置文件',
        '--hidden-import=yaml',
        '--hidden-import=pbxproj',
        '--hidden-import=numpy',
        '--hidden-import=tkinter',
        '--hidden-import=markdown',
        '--hidden-import=bs4',
        '--hidden-import=pygments',
        '--clean',
        'oc_gui.py'
    ]

    # 添加图标
    if os.path.exists(config['icon']):
        cmd.extend(['--icon', config['icon']])
        print(f"🎨 使用图标: {config['icon']}")

    # Windows特殊设置
    if config['gui_base']:
        cmd.extend(['--console'])  # 保留控制台以显示调试信息

    try:
        result = subprocess.run(
            cmd, check=True, capture_output=True, text=True)

        # 检查输出文件
        if platform.system() == 'Darwin':
            gui_path = f'dist/{app_name}.app'
        else:
            gui_path = f'dist/{app_name}{config["extension"]}'

        if os.path.exists(gui_path):
            # 设置执行权限
            if platform.system() != 'Windows' and not gui_path.endswith('.app'):
                os.chmod(gui_path, 0o755)

            if os.path.isdir(gui_path):
                import shutil
                size_mb = sum(os.path.getsize(os.path.join(dirpath, filename))
                              for dirpath, dirnames, filenames in os.walk(gui_path)
                              for filename in filenames) / (1024 * 1024)
            else:
                size_mb = os.path.getsize(gui_path) / (1024 * 1024)

            print(f"✅ GUI应用打包完成")
            print(f"📁 输出位置: {gui_path}")
            print(f"📊 应用大小: {size_mb:.1f} MB")

            return True
        else:
            print("❌ GUI打包失败 - 未找到输出文件")
            return False

    except subprocess.CalledProcessError as e:
        print(f"❌ GUI打包失败: {e}")
        if e.stderr:
            print(f"错误信息: {e.stderr}")
        return False


def create_backend_examples():
    """创建后端集成示例"""
    print("📝 创建后端集成示例...")

    # PHP示例
    php_example = '''<?php
/**
 * PHP后端调用OC混淆工具示例
 */

class OCObfuscator {
    private $cli_path;
    
    public function __construct($cli_path = './oc_cli') {
        $this->cli_path = $cli_path;
    }
    
    /**
     * 执行代码混淆
     */
    public function obfuscate($options = []) {
        $cmd = escapeshellcmd($this->cli_path);
        
        // 添加参数
        if (isset($options['input_path'])) {
            $cmd .= ' --input-path ' . escapeshellarg($options['input_path']);
        }
        if (isset($options['sdk_region'])) {
            $cmd .= ' --sdk-region ' . escapeshellarg($options['sdk_region']);
        }
        if (isset($options['new_project_name'])) {
            $cmd .= ' --new-project-name ' . escapeshellarg($options['new_project_name']);
        }
        if (isset($options['functions'])) {
            $cmd .= ' --functions ' . escapeshellarg($options['functions']);
        }
        
        $output = [];
        $return_code = 0;
        exec($cmd . ' 2>&1', $output, $return_code);
        
        return [
            'success' => $return_code === 0,
            'output' => implode("\\n", $output),
            'return_code' => $return_code,
            'command' => $cmd
        ];
    }
}

// 使用示例
try {
    $obfuscator = new OCObfuscator('./dist/oc_cli');
    
    $result = $obfuscator->obfuscate([
        'input_path' => '/path/to/ios/project',
        'sdk_region' => '2',
        'functions' => '1,2,3'
    ]);
    
    if ($result['success']) {
        echo "混淆成功!\\n";
        echo $result['output'];
    } else {
        echo "混淆失败: " . $result['output'];
    }
    
} catch (Exception $e) {
    echo "错误: " . $e->getMessage();
}
?>'''

    # Node.js示例
    nodejs_example = '''/**
 * Node.js后端调用OC混淆工具示例
 */

const { spawn, exec } = require('child_process');
const path = require('path');

class OCObfuscator {
    constructor(cliPath = './oc_cli') {
        this.cliPath = cliPath;
    }
    
    /**
     * 执行代码混淆
     */
    obfuscate(options = {}) {
        return new Promise((resolve, reject) => {
            let cmd = this.cliPath;
            
            // 添加参数
            if (options.input_path) {
                cmd += ` --input-path "${options.input_path}";
            }
            if (options.sdk_region) {
                cmd += ` --sdk-region "${options.sdk_region}";
            }
            if (options.new_project_name) {
                cmd += ` --new-project-name "${options.new_project_name}";
            }
            if (options.functions) {
                cmd += ` --functions "${options.functions}";
            }
            
            exec(cmd, (error, stdout, stderr) => {
                if (error) {
                    reject({
                        success: false,
                        error: error.message,
                        stderr: stderr,
                        command: cmd
                    });
                } else {
                    resolve({
                        success: true,
                        output: stdout,
                        stderr: stderr,
                        command: cmd
                    });
                }
            });
        });
    }
    
    /**
     * 使用spawn方式，支持实时输出
     */
    obfuscateWithProgress(options = {}, onProgress = null) {
        return new Promise((resolve, reject) => {
            const args = [];
            
            if (options.input_path) {
                args.push('--input-path', options.input_path);
            }
            if (options.sdk_region) {
                args.push('--sdk-region', options.sdk_region);
            }
            if (options.new_project_name) {
                args.push('--new-project-name', options.new_project_name);
            }
            if (options.functions) {
                args.push('--functions', options.functions);
            }
            
            const child = spawn(this.cliPath, args);
            
            let output = '';
            let error = '';
            
            child.stdout.on('data', (data) => {
                const text = data.toString();
                output += text;
                if (onProgress) {
                    onProgress(text);
                }
            });
            
            child.stderr.on('data', (data) => {
                error += data.toString();
            });
            
            child.on('close', (code) => {
                if (code === 0) {
                    resolve({
                        success: true,
                        output: output,
                        error: error
                    });
                } else {
                    reject({
                        success: false,
                        output: output,
                        error: error,
                        code: code
                    });
                }
            });
        });
    }
}

// 使用示例
async function main() {
    try {
        const obfuscator = new OCObfuscator('./dist/oc_cli');
        
        console.log('开始混淆...');
        
        const result = await obfuscator.obfuscateWithProgress({
            input_path: '/path/to/ios/project',
            sdk_region: '2', 
            functions: '1,2,3'
        }, (progress) => {
            process.stdout.write(progress);
        });
        
        if (result.success) {
            console.log('\\n混淆成功!');
        } else {
            console.error('混淆失败:', result.error);
        }
        
    } catch (error) {
        console.error('错误:', error);
    }
}

// 导出类
module.exports = OCObfuscator;

// 如果直接运行此文件
if (require.main === module) {
    main();
}'''

    # 保存示例文件
    with open('backend_examples/php_example.php', 'w', encoding='utf-8') as f:
        f.write(php_example)

    with open('backend_examples/nodejs_example.js', 'w', encoding='utf-8') as f:
        f.write(nodejs_example)

    print("✅ 后端集成示例已创建在 backend_examples/ 目录")


def main():
    """主函数"""
    parser = argparse.ArgumentParser(description='OC混淆工具通用打包脚本')
    parser.add_argument('--cli', action='store_true', help='只打包CLI工具')
    parser.add_argument('--gui', action='store_true', help='只打包GUI应用')
    parser.add_argument('--examples', action='store_true', help='创建后端集成示例')
    parser.add_argument('--all', action='store_true', help='打包所有内容')

    args = parser.parse_args()

    print("🚀 OC混淆工具通用打包脚本")
    print(f"🖥️  当前平台: {platform.system()} {platform.machine()}")
    print("=" * 50)

    # 检查环境
    if not check_environment():
        print("❌ 环境检查失败")
        sys.exit(1)

    # 安装PyInstaller
    if not install_pyinstaller():
        print("❌ PyInstaller安装失败")
        sys.exit(1)

    # 创建输出目录
    os.makedirs('dist', exist_ok=True)
    os.makedirs('backend_examples', exist_ok=True)

    success = True

    if args.cli or args.all or (not args.gui and not args.examples):
        success &= build_cli()

    if args.gui or args.all:
        success &= build_gui()

    if args.examples or args.all:
        create_backend_examples()

    print("=" * 50)
    if success:
        print("🎉 打包完成！")
        print("📁 输出目录: dist/")
        print("")
        print("💡 使用说明:")
        print("   CLI工具: ./dist/oc_cli --help")
        if args.gui or args.all:
            if platform.system() == 'Darwin':
                print("   GUI应用: open dist/闲闲SDK混淆工具.app")
            else:
                print("   GUI应用: ./dist/闲闲SDK混淆工具")
        print("   后端示例: backend_examples/")
    else:
        print("❌ 打包过程中出现错误")
        sys.exit(1)


if __name__ == '__main__':
    main()
