"""
以“计划任务(最高权限)”方式执行静默覆盖更新。

工作流：
- 主程序（普通权限）下载新 exe 到可写目录（建议 ProgramData）并写入 update_request.json
- 计划任务以 SYSTEM/管理员身份运行本程序
- 本程序读取请求文件 -> 等待主程序退出 -> 覆盖安装目录 exe -> 重启 -> 清理请求文件
"""

from __future__ import annotations

import json
import os
import subprocess
import sys
import time


APP_NAME = "CleanDesktopOrganizer"
REQUEST_FILE = "update_request.json"


def _programdata_dir() -> str:
    base = os.environ.get("PROGRAMDATA") or r"C:\ProgramData"
    return os.path.join(base, APP_NAME)


def _request_path() -> str:
    return os.path.join(_programdata_dir(), REQUEST_FILE)


def _read_request() -> dict:
    p = _request_path()
    with open(p, "r", encoding="utf-8") as f:
        return json.load(f)


def _pid_exists(pid: int) -> bool:
    if pid <= 0:
        return False
    try:
        # tasklist 不需要管理员权限，SYSTEM 下也可用
        out = subprocess.run(
            ["tasklist", "/FI", f"PID eq {pid}"],
            capture_output=True,
            text=True,
            encoding="utf-8",
            errors="replace",
            creationflags=subprocess.CREATE_NO_WINDOW,
        ).stdout
        return str(pid) in out
    except Exception:
        return False


def _copy_with_retries(src: str, dst: str, retries: int = 10) -> bool:
    for _ in range(max(1, retries)):
        try:
            os.makedirs(os.path.dirname(dst), exist_ok=True)
            if os.path.exists(dst):
                try:
                    os.chmod(dst, 0o666)
                except Exception:
                    pass
            # 用二进制流复制，避免权限/锁问题时直接抛错
            with open(src, "rb") as rf:
                data = rf.read()
            with open(dst, "wb") as wf:
                wf.write(data)
            return True
        except Exception:
            time.sleep(0.8)
    return False


def main() -> int:
    try:
        req = _read_request()
    except FileNotFoundError:
        return 0
    except Exception:
        return 2

    src = str(req.get("src") or "")
    dst = str(req.get("dst") or "")
    pid = int(req.get("pid") or 0)
    restart = bool(req.get("restart", True))

    if not src or not dst:
        return 3

    # 等待主程序退出，最多 60 秒
    waited = 0.0
    while _pid_exists(pid) and waited < 60.0:
        time.sleep(0.5)
        waited += 0.5

    ok = _copy_with_retries(src, dst, retries=15)
    try:
        os.remove(src)
    except Exception:
        pass

    # 清理请求文件（成功/失败都尽量清掉，避免重复执行）
    try:
        os.remove(_request_path())
    except Exception:
        pass

    if ok and restart:
        try:
            subprocess.Popen([dst], creationflags=subprocess.DETACHED_PROCESS | subprocess.CREATE_NO_WINDOW)
        except Exception:
            pass
        return 0

    return 4


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

