"""版本检查 + agent 可识别的新版本提示（npm update-notifier 的 Python 手搓版）。

流程：
1. cli.py `@cli.group()` 入口调 print_update_notice_if_needed()
2. 读 ~/.xiangqin/version-cache.json
3. 缓存 <24h → 直接比较本地 vs remote；本地落后 → stderr 打印
4. 缓存过期 or 不存在 → subprocess detach 后台查 PyPI + 写缓存（不阻塞当前命令）

环境变量关闭：
- XIANGQIN_NO_UPDATE_CHECK=1 —— 用户显式关
- CI=true —— 自动关（避免 CI 噪音）

agent 识别：stderr 提示固定前缀 `[xq] 新版本`。
"""

from __future__ import annotations

import json
import os
import re
import subprocess
import sys
import time
import urllib.request
from pathlib import Path

from . import __version__

PACKAGE_NAME = "acong-tech-xiangqin"
PYPI_URL = f"https://pypi.org/pypi/{PACKAGE_NAME}/json"
CACHE_TTL_SEC = 24 * 3600
NETWORK_TIMEOUT_SEC = 3


def _cache_path() -> Path:
    config_dir = Path(os.environ.get("XIANGQIN_CONFIG_DIR", str(Path.home() / ".xiangqin")))
    return config_dir / "version-cache.json"


def _load_cache() -> dict | None:
    p = _cache_path()
    if not p.exists():
        return None
    try:
        return json.loads(p.read_text())
    except (OSError, json.JSONDecodeError):
        return None


def _save_cache(remote: str) -> None:
    p = _cache_path()
    p.parent.mkdir(mode=0o700, exist_ok=True)
    p.write_text(
        json.dumps(
            {"last_checked_at": int(time.time()), "remote_latest": remote},
            ensure_ascii=False,
        )
    )


def _fetch_remote() -> str | None:
    """调 PyPI JSON API 取 latest stable 版本号。失败返 None。"""
    try:
        req = urllib.request.Request(
            PYPI_URL,
            headers={"Accept": "application/json", "User-Agent": f"xq/{__version__}"},
        )
        with urllib.request.urlopen(req, timeout=NETWORK_TIMEOUT_SEC) as resp:
            data = json.loads(resp.read().decode("utf-8"))
        return data.get("info", {}).get("version")
    except Exception:  # noqa: BLE001 - 任何失败都静默
        return None


def _parse_version(s: str) -> tuple[int, ...]:
    """保守解析：'0.3.1' → (0, 3, 1)。只取前 3 个数字段，忽略 -rc / .dev 后缀。"""
    nums = re.findall(r"\d+", s or "")[:3]
    return tuple(int(n) for n in nums)


def _version_less(a: str, b: str) -> bool:
    return _parse_version(a) < _parse_version(b)


def _spawn_background_refresh() -> None:
    """fork detach 子进程跑 --refresh，不阻塞当前命令。"""
    try:
        subprocess.Popen(
            [sys.executable, "-m", "xiangqin.version_check", "--refresh"],
            stdout=subprocess.DEVNULL,
            stderr=subprocess.DEVNULL,
            stdin=subprocess.DEVNULL,
            start_new_session=True,
            close_fds=True,
        )
    except Exception:  # noqa: BLE001
        pass


def print_update_notice_if_needed() -> None:
    """cli.py 入口调用。远程有新版本则 stderr 打印一行；失败 / 被关闭 / 无缓存静默。"""
    if os.environ.get("XIANGQIN_NO_UPDATE_CHECK"):
        return
    if os.environ.get("CI"):
        return

    try:
        cache = _load_cache()
        now = time.time()
        need_refresh = cache is None or (now - cache.get("last_checked_at", 0)) > CACHE_TTL_SEC

        if need_refresh:
            _spawn_background_refresh()

        if cache is None:
            return  # 第一次跑，下次生效

        remote = cache.get("remote_latest")
        if remote and _version_less(__version__, remote):
            sys.stderr.write(
                f"[xq] 新版本 {remote} 可用（当前 {__version__}）。"
                f"跑 `pip install -U {PACKAGE_NAME}` 更新。\n"
            )
    except Exception:  # noqa: BLE001
        pass  # 任何失败都静默，不阻塞 CLI


def _main() -> None:
    """子进程入口：python -m xiangqin.version_check --refresh"""
    if "--refresh" in sys.argv:
        remote = _fetch_remote()
        if remote:
            try:
                _save_cache(remote)
            except Exception:  # noqa: BLE001
                pass


if __name__ == "__main__":
    _main()
