# -*- coding: utf-8 -*-
"""
ETL GUI 打包脚本

使用 PyInstaller 将 GUI 应用打包为 Windows EXE

用法:
    python build_exe.py [--onefile] [--console] [--clean]

参数:
    --onefile   打包为单个 EXE 文件（默认为目录模式）
    --console   显示控制台窗口（调试用）
    --clean     打包前清理旧的构建文件
"""

import os
import sys
import shutil
import subprocess
from pathlib import Path


def get_project_root() -> Path:
    """获取项目根目录"""
    return Path(__file__).resolve().parent


def clean_build():
    """清理旧的构建文件"""
    project_root = get_project_root()
    
    dirs_to_clean = [
        project_root / "build",
        project_root / "dist",
    ]
    
    files_to_clean = [
        project_root / "etl_gui.spec",
    ]
    
    for d in dirs_to_clean:
        if d.exists():
            print(f"清理目录: {d}")
            shutil.rmtree(d)
    
    for f in files_to_clean:
        if f.exists():
            print(f"清理文件: {f}")
            f.unlink()


def build_exe(onefile: bool = False, console: bool = False):
    """构建 EXE"""
    project_root = get_project_root()
    
    # 主入口
    main_script = project_root / "gui" / "main.py"
    
    # 资源文件
    resources_dir = project_root / "gui" / "resources"
    database_dir = project_root / "database"
    
    # 构建 PyInstaller 命令
    # 使用 ASCII 名称避免 Windows 控制台编码问题
    cmd = [
        sys.executable, "-m", "PyInstaller",
        "--name", "ETL_Manager",
        "--noconfirm",
    ]
    
    # 单文件或目录模式
    if onefile:
        cmd.append("--onefile")
    else:
        cmd.append("--onedir")
    
    # 窗口模式
    if not console:
        cmd.append("--windowed")
    
    # 添加数据文件
    # 样式表
    if resources_dir.exists():
        cmd.extend(["--add-data", f"{resources_dir};gui/resources"])
    
    # 数据库 SQL 文件
    if database_dir.exists():
        for sql_file in database_dir.glob("*.sql"):
            cmd.extend(["--add-data", f"{sql_file};database"])
    
    # 隐式导入
    hidden_imports = [
        # PySide6 核心模块
        "PySide6.QtCore",
        "PySide6.QtGui",
        "PySide6.QtWidgets",
        # 数据库
        "psycopg2",
        "psycopg2.extras",
        "psycopg2.extensions",
        # GUI 模块
        "gui.models.task_model",
        "gui.models.schedule_model",
        "gui.utils.cli_builder",
        "gui.utils.config_helper",
        "gui.utils.app_settings",
        "gui.workers.task_worker",
        "gui.workers.db_worker",
        "gui.widgets.settings_dialog",
    ]
    for imp in hidden_imports:
        cmd.extend(["--hidden-import", imp])
    
    # 排除不需要的模块（减小体积）
    excludes = [
        "matplotlib",
        "numpy",
        "pandas",
        "scipy",
        "PIL",
        "cv2",
        "tkinter",
    ]
    for exc in excludes:
        cmd.extend(["--exclude-module", exc])
    
    # 工作目录
    cmd.extend(["--workpath", str(project_root / "build")])
    cmd.extend(["--distpath", str(project_root / "dist")])
    cmd.extend(["--specpath", str(project_root)])
    
    # 主脚本
    cmd.append(str(main_script))
    
    print("执行命令:")
    print(" ".join(cmd))
    print()
    
    # 执行打包
    result = subprocess.run(cmd, cwd=str(project_root))
    
    if result.returncode == 0:
        # 打包后精简：删除不需要的文件
        slim_dist(project_root / "dist" / "ETL_Manager" / "_internal")
        
        print()
        print("=" * 50)
        print("打包成功!")
        print(f"输出目录: {project_root / 'dist'}")
        print("=" * 50)
    else:
        print()
        print("打包失败，请检查错误信息")
        sys.exit(1)


def slim_dist(internal_dir: Path):
    """精简打包后的文件，删除不需要的内容"""
    if not internal_dir.exists():
        return
    
    print()
    print("精简打包文件...")
    
    removed_size = 0
    
    # 1. 删除不需要的翻译文件（只保留中文和英文）
    translations_dir = internal_dir / "PySide6" / "translations"
    if translations_dir.exists():
        keep_langs = {"zh_CN", "zh_TW", "en"}
        for qm_file in translations_dir.glob("*.qm"):
            # 检查是否是需要保留的语言
            keep = False
            for lang in keep_langs:
                if lang in qm_file.name:
                    keep = True
                    break
            if not keep:
                size = qm_file.stat().st_size
                qm_file.unlink()
                removed_size += size
    
    # 2. 删除 opengl32sw.dll（软件渲染，20MB，通常不需要）
    opengl_sw = internal_dir / "PySide6" / "opengl32sw.dll"
    if opengl_sw.exists():
        size = opengl_sw.stat().st_size
        opengl_sw.unlink()
        removed_size += size
        print(f"  删除: opengl32sw.dll ({size / 1024 / 1024:.1f} MB)")
    
    # 3. 删除不需要的 Qt 模块 DLL（如果存在）
    unnecessary_dlls = [
        "Qt6Pdf.dll",           # PDF 支持
        "Qt6Qml.dll",           # QML 引擎
        "Qt6QmlMeta.dll",
        "Qt6QmlModels.dll",
        "Qt6QmlWorkerScript.dll",
        "Qt6Quick.dll",         # Quick UI
        "Qt6VirtualKeyboard.dll",  # 虚拟键盘
    ]
    pyside6_dir = internal_dir / "PySide6"
    for dll_name in unnecessary_dlls:
        dll_path = pyside6_dir / dll_name
        if dll_path.exists():
            size = dll_path.stat().st_size
            dll_path.unlink()
            removed_size += size
            print(f"  删除: {dll_name} ({size / 1024 / 1024:.1f} MB)")
    
    # 4. 删除不需要的插件目录
    unnecessary_plugins = [
        "networkinformation",  # 网络信息
        "tls",                 # TLS 支持（数据库已有）
    ]
    plugins_dir = pyside6_dir / "plugins"
    if plugins_dir.exists():
        for plugin_name in unnecessary_plugins:
            plugin_path = plugins_dir / plugin_name
            if plugin_path.exists():
                size = sum(f.stat().st_size for f in plugin_path.rglob("*") if f.is_file())
                shutil.rmtree(plugin_path)
                removed_size += size
                print(f"  删除插件: {plugin_name}")
    
    print(f"共节省: {removed_size / 1024 / 1024:.1f} MB")


def main():
    """主函数"""
    import argparse
    
    parser = argparse.ArgumentParser(description="ETL GUI 打包工具")
    parser.add_argument("--onefile", action="store_true", help="打包为单个 EXE")
    parser.add_argument("--console", action="store_true", help="显示控制台窗口")
    parser.add_argument("--clean", action="store_true", help="打包前清理")
    
    args = parser.parse_args()
    
    # 检查 PyInstaller
    try:
        import PyInstaller
        print(f"PyInstaller 版本: {PyInstaller.__version__}")
    except ImportError:
        print("错误: 未安装 PyInstaller")
        print("请运行: pip install pyinstaller")
        sys.exit(1)
    
    # 清理
    if args.clean:
        clean_build()
    
    # 构建
    build_exe(onefile=args.onefile, console=args.console)


if __name__ == "__main__":
    main()
