"""Version checking, PyPI queries, auto-upgrade, revert, global state."""

from __future__ import annotations

import json as json_mod
import subprocess
from datetime import datetime
from pathlib import Path
from typing import Any
from urllib.request import Request, urlopen

import click
import yaml

from synology_mcp import __version__

_PYPI_PACKAGE = "synology-mcp"
_VERSION_CHECK_INTERVAL = 86400  # 24 hours


def _get_current_version() -> str:
    """Return the running version."""
    try:
        from importlib.metadata import PackageNotFoundError
        from importlib.metadata import version as _pkg_version

        return _pkg_version(_PYPI_PACKAGE)
    except (PackageNotFoundError, ImportError):
        return __version__


def _get_latest_pypi_version() -> str | None:
    """Fetch the latest version from PyPI. Returns None on any failure."""
    try:
        req = Request(
            f"https://pypi.org/pypi/{_PYPI_PACKAGE}/json",
            headers={"User-Agent": f"{_PYPI_PACKAGE}/{_get_current_version()}"},
        )
        with urlopen(req, timeout=5) as resp:  # noqa: S310
            data = json_mod.loads(resp.read())
        result: str = data["info"]["version"]
        return result
    except (OSError, KeyError, ValueError):
        return None


def _version_tuple(v: str) -> tuple[int, ...]:
    try:
        return tuple(int(x) for x in v.split("."))
    except (ValueError, AttributeError):
        return (0,)


def _detect_installer() -> str | None:
    """Detect whether the tool was installed via uv tool or pipx."""
    import shutil

    bin_path = shutil.which(_PYPI_PACKAGE) or ""
    if "uv" in bin_path and "tools" in bin_path:
        return "uv"
    if "pipx" in bin_path:
        return "pipx"
    return None


def _load_global_state() -> dict[str, Any]:
    """Load global (non-instance) state for version tracking."""
    path = Path.home() / ".local" / "state" / "synology-mcp" / "global.yaml"
    if not path.exists():
        return {}
    try:
        raw: dict[str, Any] = yaml.safe_load(path.read_text(encoding="utf-8")) or {}
        return raw
    except (OSError, yaml.YAMLError):
        return {}


def _save_global_state(state: dict[str, Any]) -> None:
    """Save global state."""
    path = Path.home() / ".local" / "state" / "synology-mcp" / "global.yaml"
    path.parent.mkdir(parents=True, exist_ok=True)
    raw_text = yaml.dump(state, default_flow_style=False, sort_keys=False)
    path.write_text("# Auto-generated by synology-mcp.\n" + raw_text, encoding="utf-8")


def _check_for_update(state: dict[str, Any], *, force: bool = False) -> str | None:
    """Check PyPI for a newer version. Returns latest version string if newer, else None."""
    now = datetime.now().timestamp()
    if not force:
        last = state.get("last_version_check")
        if last:
            try:
                elapsed = now - datetime.fromisoformat(last).timestamp()
                if elapsed < _VERSION_CHECK_INTERVAL:
                    latest: str | None = state.get("latest_known_version")
                    current = _get_current_version()
                    if latest and _version_tuple(latest) > _version_tuple(current):
                        return latest
                    return None
            except (ValueError, TypeError, AttributeError):
                pass

    latest = _get_latest_pypi_version()
    state["last_version_check"] = datetime.now().isoformat()
    if not latest:
        return None

    state["latest_known_version"] = latest
    current = _get_current_version()

    if _version_tuple(latest) > _version_tuple(current):
        return latest
    return None


def _do_auto_upgrade(state: dict[str, Any]) -> bool:
    """Upgrade to the latest version. Returns True on success."""
    installer = _detect_installer()
    current = _get_current_version()

    if installer == "uv":
        cmd = ["uv", "tool", "install", f"{_PYPI_PACKAGE}@latest"]
    elif installer == "pipx":
        cmd = ["pipx", "upgrade", _PYPI_PACKAGE]
    else:
        click.echo(
            "Cannot detect package manager (uv or pipx). "
            f"Upgrade manually: uv tool upgrade {_PYPI_PACKAGE}"
        )
        return False

    click.echo(f"Upgrading {_PYPI_PACKAGE} via {installer}...")
    result = subprocess.run(cmd, capture_output=True, text=True)  # noqa: S603
    if result.returncode == 0:
        state["previous_version"] = current
        _save_global_state(state)
        click.echo(click.style("Upgrade complete — takes effect on next run.", fg="green"))
        return True
    click.echo(click.style(f"Upgrade failed: {result.stderr.strip()}", fg="red"), err=True)
    return False


def _do_revert(target_version: str | None = None) -> None:
    """Revert to a specific version or the previously recorded one."""
    state = _load_global_state()
    current = _get_current_version()

    prev: str | None
    if target_version:
        prev = target_version
    else:
        prev = state.get("previous_version")
        if not prev:
            click.echo("No previous version recorded — nothing to revert to.")
            click.echo(f"Tip: specify a version explicitly: {_PYPI_PACKAGE} --revert 0.1.0")
            return

    if prev == current:
        click.echo(f"Already running {current} — nothing to do.")
        return

    installer = _detect_installer()

    if installer == "uv":
        cmd = ["uv", "tool", "install", "--force", f"{_PYPI_PACKAGE}=={prev}"]
    elif installer == "pipx":
        cmd = ["pipx", "install", "--force", f"{_PYPI_PACKAGE}=={prev}"]
    else:
        click.echo(
            f"Cannot detect package manager. Install manually:\n"
            f"  uv tool install --force {_PYPI_PACKAGE}=={prev}\n"
            f"  pipx install --force {_PYPI_PACKAGE}=={prev}"
        )
        return

    click.echo(f"Reverting from {current} to {prev}...")
    result = subprocess.run(cmd, capture_output=True, text=True)  # noqa: S603
    if result.returncode == 0:
        state["previous_version"] = None
        state["auto_upgrade"] = False
        _save_global_state(state)
        click.echo(click.style(f"Reverted to {prev}. Takes effect on next run.", fg="green"))
        click.echo("Auto-upgrade has been disabled to prevent immediately re-upgrading.")
        click.echo(f"Re-enable with: {_PYPI_PACKAGE} --auto-upgrade enable")
    else:
        click.echo(click.style(f"Revert failed: {result.stderr.strip()}", fg="red"), err=True)
