from __future__ import annotations

import os
import shutil
import subprocess
import sys
from pathlib import Path
from zipfile import ZIP_DEFLATED, ZipFile


PROJECT = Path(__file__).resolve().parents[1]
APP_NAME = "只属于你"
TEMP_ROOT = PROJECT / ".tmp" / "独立版构建"
RELEASE_ROOT = PROJECT / "发布包"


def require_pyinstaller():
    try:
        from PyInstaller.__main__ import run
    except ImportError as exc:
        raise RuntimeError("缺少 PyInstaller，请先运行：python -m pip install pyinstaller") from exc
    return run


def reset_build_dirs() -> tuple[Path, Path, Path]:
    dist = TEMP_ROOT / "dist"
    work = TEMP_ROOT / "build"
    spec = TEMP_ROOT / "spec"
    for path in (dist, work, spec):
        if path.exists():
            shutil.rmtree(path)
        path.mkdir(parents=True, exist_ok=True)
    return dist, work, spec


def add_support_files(target: Path) -> None:
    for name in ("使用说明.txt", "README.md", "README_MAC.md"):
        source = PROJECT / name
        if source.exists():
            shutil.copy2(source, target / name)
    version_note = RELEASE_ROOT / "版本说明.txt"
    if version_note.exists():
        shutil.copy2(version_note, target / "版本说明.txt")


def build_windows(run, dist: Path, work: Path, spec: Path) -> Path:
    run(
        [
            str(PROJECT / "launch_pet.py"),
            f"--name={APP_NAME}",
            "--windowed",
            "--noconfirm",
            "--clean",
            f"--distpath={dist}",
            f"--workpath={work}",
            f"--specpath={spec}",
            f"--add-data={PROJECT / 'bibi'}{os.pathsep}bibi",
            "--hidden-import=control_panel",
            "--hidden-import=PIL.ImageTk",
        ]
    )
    app_dir = dist / APP_NAME
    if not (app_dir / f"{APP_NAME}.exe").exists():
        raise RuntimeError("Windows 独立程序未生成")
    add_support_files(app_dir)
    launcher = app_dir / "打开控制面板.vbs"
    launcher.write_text(
        'Set shell = CreateObject("WScript.Shell")\n'
        'Set fso = CreateObject("Scripting.FileSystemObject")\n'
        'root = fso.GetParentFolderName(WScript.ScriptFullName)\n'
        f'shell.Run Chr(34) & root & "\\{APP_NAME}.exe" & Chr(34) & " --control-panel", 0, False\n',
        encoding="utf-8-sig",
    )
    return app_dir


def build_mac(run, dist: Path, work: Path, spec: Path) -> Path:
    run(
        [
            str(PROJECT / "launch_pet.py"),
            f"--name={APP_NAME}",
            "--windowed",
            "--noconfirm",
            "--clean",
            "--osx-bundle-identifier=local.onlyyours.desktoppet",
            f"--distpath={dist}",
            f"--workpath={work}",
            f"--specpath={spec}",
            f"--add-data={PROJECT / 'bibi'}{os.pathsep}bibi",
            "--hidden-import=control_panel",
            "--hidden-import=PIL.ImageTk",
        ]
    )
    app = dist / f"{APP_NAME}.app"
    if not app.exists():
        raise RuntimeError("Mac 独立程序未生成")
    subprocess.run(
        ["codesign", "--force", "--deep", "--sign", "-", str(app)],
        check=True,
        capture_output=True,
        text=True,
    )
    return app


def zip_tree(source: Path, target: Path, root_name: str | None = None) -> None:
    target.parent.mkdir(parents=True, exist_ok=True)
    with ZipFile(target, "w", ZIP_DEFLATED) as zf:
        if source.is_file():
            zf.write(source, source.name)
            return
        for path in sorted(source.rglob("*")):
            if not path.is_file():
                continue
            relative = path.relative_to(source)
            arcname = Path(root_name) / relative if root_name else relative
            zf.write(path, str(arcname).replace("\\", "/"))


def main() -> int:
    run = require_pyinstaller()
    dist, work, spec = reset_build_dirs()
    RELEASE_ROOT.mkdir(parents=True, exist_ok=True)
    if sys.platform.startswith("win"):
        app = build_windows(run, dist, work, spec)
        target = RELEASE_ROOT / f"Windows独立版_{APP_NAME}.zip"
        zip_tree(app, target, root_name=APP_NAME)
    elif sys.platform == "darwin":
        app = build_mac(run, dist, work, spec)
        target = RELEASE_ROOT / f"Mac独立版_{APP_NAME}.zip"
        zip_tree(app, target, root_name=app.name)
    else:
        raise RuntimeError("当前只支持在 Windows 或 Mac 上构建独立版")
    print(f"standalone={target} bytes={target.stat().st_size}")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
