#!/usr/bin/env python3
"""
GEO Tool — 跨平台打包构建脚本

用法:
    python build_scripts/build.py            # 打包 Python 可执行文件
    python build_scripts/build.py --clean    # 先清理旧构建再打包
    python build_scripts/build.py tauri      # 打包 Python 可执行文件 + 构建 Tauri 桌面应用

输出:
    dist_python/geo_tool_app          (macOS / Linux)
    dist_python/geo_tool_app.exe      (Windows)
    src-tauri/target/release/GEO工具    (Tauri 桌面应用)

依赖:
    pip install pyinstaller
    cargo tauri build (for tauri 命令)
"""

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

# ── 项目根目录 ────────────────────────────────────────────────
PROJECT_ROOT = Path(__file__).resolve().parent.parent
SPEC_FILE = PROJECT_ROOT / "geo_tool_app.spec"
DIST_DIR = PROJECT_ROOT / "dist_python"
BUILD_DIR = PROJECT_ROOT / "build_python"
TAURI_DIR = PROJECT_ROOT / "src-tauri"


def check_prerequisites():
    """检查必要工具是否已安装"""
    # 检查 PyInstaller
    try:
        import PyInstaller  # noqa: F401
    except ImportError:
        print("正在安装 PyInstaller ...")
        subprocess.check_call(
            [sys.executable, "-m", "pip", "install", "pyinstaller"],
            cwd=str(PROJECT_ROOT),
        )

    # 检查项目依赖是否已安装
    try:
        import streamlit  # noqa: F401
    except ImportError:
        print("正在安装项目依赖 ...")
        # 优先用 uv，否则 pip install pyproject.toml
        for installer in ["uv", "pip"]:
            try:
                subprocess.check_call(
                    [installer, "sync"] if installer == "uv" else [installer, "install", "."],
                    cwd=str(PROJECT_ROOT),
                )
                break
            except (subprocess.CalledProcessError, FileNotFoundError):
                continue


def clean_build():
    """清理之前的构建产物"""
    for d in [DIST_DIR, BUILD_DIR]:
        if d.exists():
            print(f"  清理 {d} ...")
            shutil.rmtree(d)

    # 清理 spec 生成的临时目录
    temp_spec_dir = PROJECT_ROOT / "__pycache__"
    if temp_spec_dir.exists():
        shutil.rmtree(temp_spec_dir)


def build_tauri():
    """先打包 Python 可执行文件，然后构建 Tauri 桌面应用"""
    build()
    print("\n正在构建 Tauri 桌面应用 ...\n")
    result = subprocess.run(
        ["cargo", "tauri", "build"],
        cwd=str(TAURI_DIR),
    )
    if result.returncode != 0:
        print(f"\n[错误] Tauri 构建失败 (返回码: {result.returncode})")
        sys.exit(1)

    print("\n" + "=" * 60)
    print("  Tauri 构建完成!")
    release_dir = TAURI_DIR / "target" / "release"
    if sys.platform == "darwin":
        bundles = list(release_dir.glob("*.dmg")) + list((release_dir / "bundle" / "dmg").glob("*.dmg")) + list((release_dir / "bundle" / "macos").glob("*.app"))
    elif sys.platform == "win32":
        bundles = list(release_dir.glob("*.msi")) + list((release_dir / "bundle" / "msi").glob("*.msi"))
    else:
        bundles = list(release_dir.glob("GEO工具")) + list(release_dir.glob("*.AppImage"))
    for b in bundles:
        size_mb = b.stat().st_size / (1024 * 1024) if b.is_file() else sum(f.stat().st_size for f in b.rglob("*")) / (1024 * 1024)
        print(f"  输出: {b} ({size_mb:.1f} MB)")
    print("=" * 60)


def build():
    """执行 PyInstaller 打包"""
    # 确保 run_bundle.py 存在
    entry_point = PROJECT_ROOT / "run_bundle.py"
    if not entry_point.exists():
        print(f"[错误] 未找到入口文件: {entry_point}")
        sys.exit(1)

    # 检查关键资源是否存在（给出警告而非中断）
    resources = [
        ("geo_tool.py", "file"),
        ("config.json", "file"),
        ("geo_data.db", "file"),
        (".streamlit", "dir"),
        ("modules", "dir"),
    ]
    for path, _kind in resources:
        p = PROJECT_ROOT / path
        if not p.exists():
            print(f"[警告] 资源不存在: {p}")

    # 构建命令
    cmd = [
        sys.executable,
        "-m",
        "PyInstaller",
        str(SPEC_FILE),
        "--distpath",
        str(DIST_DIR),
        "--workpath",
        str(BUILD_DIR),
        "--clean",
        "--noconfirm",
    ]

    print("=" * 60)
    print("  GEO Tool — 打包构建")
    print("=" * 60)
    print(f"  平台:      {sys.platform}")
    print(f"  Python:    {sys.version}")
    print(f"  入口:      {entry_point}")
    print(f"  Spec:      {SPEC_FILE}")
    print(f"  输出目录:  {DIST_DIR}")
    print("=" * 60)

    # 执行打包
    result = subprocess.run(cmd, cwd=str(PROJECT_ROOT))

    if result.returncode != 0:
        print(f"\n[错误] 打包失败 (返回码: {result.returncode})")
        print(f"       请检查 {BUILD_DIR}/ 下的日志文件获取详细错误信息。")
        sys.exit(1)

    # 输出结果
    print("\n" + "=" * 60)
    print("  打包完成!")
    print("=" * 60)

    # 查找产物
    if sys.platform == "win32":
        output = DIST_DIR / "geo_tool_app.exe"
    elif sys.platform == "darwin":
        output = DIST_DIR / "geo_tool_app"
    else:
        output = DIST_DIR / "geo_tool_app"

    if output.exists():
        size_mb = output.stat().st_size / (1024 * 1024)
        print(f"  输出文件: {output}")
        print(f"  文件大小: {size_mb:.1f} MB")
    else:
        # macOS 下可能生成 .app
        app_bundle = DIST_DIR / "geo_tool_app.app"
        if app_bundle.exists():
            size_mb = sum(f.stat().st_size for f in app_bundle.rglob("*")) / (1024 * 1024)
            print(f"  输出文件: {app_bundle}")
            print(f"  文件大小: {size_mb:.1f} MB")

    print(f"  运行方式: 双击 {output.name} 或终端执行 {output}")
    print("=" * 60)


def main():
    parser = argparse.ArgumentParser(
        description="GEO Tool 跨平台打包脚本",
    )
    parser.add_argument(
        "mode",
        nargs="?",
        default="python",
        choices=["python", "tauri"],
        help="打包模式: python (默认) 或 tauri (Python + 桌面壳)",
    )
    parser.add_argument(
        "--clean",
        action="store_true",
        help="构建前清理旧的 dist 和 build 目录",
    )
    parser.add_argument(
        "--skip-deps",
        action="store_true",
        help="跳过依赖检查（仅安装 PyInstaller）",
    )
    args = parser.parse_args()

    # 切换到项目根目录
    os.chdir(str(PROJECT_ROOT))

    # 清理
    if args.clean:
        print("清理旧的构建产物 ...")
        clean_build()

    # 检查环境
    if not args.skip_deps:
        check_prerequisites()

    # 打包
    if args.mode == "tauri":
        build_tauri()
    else:
        build()


if __name__ == "__main__":
    main()
