"""Update check command."""

from __future__ import annotations

import json
import os
from datetime import datetime, timezone
from importlib import metadata
from pathlib import Path

import httpx
import typer
from packaging.version import InvalidVersion, Version
from platformdirs import user_cache_dir

from ..config import CACHE_DIR_ENV
from ..update_checker import format_update_notification
from ._context import require_cli_context

PYPI_URL = "https://pypi.org/pypi/ctxme/json"
TIMEOUT_SECONDS = 5.0
CACHE_FILENAME = "update_check.json"


def _get_installed_version() -> str:
    """Return the installed CLI package version or 'dev' fallback."""
    try:
        return metadata.version("ctxme")
    except metadata.PackageNotFoundError:
        return "dev"


def _get_cache_dir() -> Path:
    """Return the cache directory, respecting CTXME_CACHE_DIR override."""
    env_dir = os.getenv(CACHE_DIR_ENV)
    if env_dir:
        return Path(env_dir).expanduser()
    return Path(user_cache_dir("ctxme"))


def _write_cache(latest_version: str, installed_version: str | None = None) -> None:
    """Write the update check result to cache.

    Cache format:
    {
        "last_check": "2024-01-21T12:00:00Z",
        "latest_version": "1.2.3",
        "installed_version": "1.0.0"
    }
    """
    try:
        cache_dir = _get_cache_dir()
        cache_dir.mkdir(parents=True, exist_ok=True)
        cache_path = cache_dir / CACHE_FILENAME

        data = {
            "last_check": datetime.now(timezone.utc).isoformat(),
            "latest_version": latest_version,
        }
        if installed_version is not None:
            data["installed_version"] = installed_version

        cache_path.write_text(json.dumps(data, indent=2) + "\n")
    except OSError:
        # Best effort - don't fail if we can't write cache
        pass


def _fetch_latest_version(client: httpx.Client | None = None) -> str | None:
    """Query PyPI for the latest ctxme version.

    Returns None if the request fails or times out.
    """
    try:
        if client is None:
            with httpx.Client(timeout=TIMEOUT_SECONDS) as c:
                response = c.get(PYPI_URL)
        else:
            response = client.get(PYPI_URL)
        response.raise_for_status()
        data = response.json()
        return data.get("info", {}).get("version")
    except (httpx.HTTPError, KeyError, ValueError):
        return None


def _compare_versions(installed: str, latest: str) -> int:
    """Compare two version strings.

    Returns:
        -1 if installed < latest (update available)
         0 if installed == latest (up to date)
         1 if installed > latest (ahead of release)

    Raises:
        InvalidVersion if either version string is invalid.
    """
    installed_v = Version(installed)
    latest_v = Version(latest)
    if installed_v < latest_v:
        return -1
    if installed_v > latest_v:
        return 1
    return 0


def register(app: typer.Typer) -> None:
    @app.command("update-check", help="Check for newer versions of the CLI.")
    def update_check(ctx: typer.Context) -> None:
        cli_ctx = require_cli_context(ctx)
        console = cli_ctx.console

        installed = _get_installed_version()

        # Handle dev/unknown versions gracefully
        if installed in {"dev", "unknown"}:
            console.print(
                f"[yellow]Running development version ({installed}), "
                "skipping update check.[/yellow]"
            )
            raise typer.Exit(0)

        # Validate installed version is parseable
        try:
            Version(installed)
        except InvalidVersion:
            console.print(
                f"[yellow]Unable to parse installed version '{installed}', "
                "skipping update check.[/yellow]"
            )
            raise typer.Exit(0)

        console.print(f"Checking for updates... (installed: {installed})")

        latest = _fetch_latest_version()
        if latest is None:
            console.print(
                "[yellow]Unable to check for updates. " "Check your network connection.[/yellow]"
            )
            raise typer.Exit(1)

        # Cache successful result for future periodic checks (#184)
        _write_cache(latest, installed)

        try:
            comparison = _compare_versions(installed, latest)
        except InvalidVersion:
            console.print(f"[yellow]Unable to parse latest version '{latest}'.[/yellow]")
            raise typer.Exit(1)

        if comparison < 0:
            message = format_update_notification(installed, latest)
            console.print(f"[green]{message}[/green]")
        elif comparison > 0:
            console.print(
                f"[cyan]You're ahead of the latest release.[/cyan] "
                f"(installed: {installed}, latest: {latest})"
            )
        else:
            console.print(f"[green]You're up to date![/green] ({installed})")
