from __future__ import annotations

import json
import os
import sys
from datetime import datetime, timezone
from pathlib import Path
from urllib.error import URLError
from urllib.request import urlopen

from autosana_cli import __version__
from autosana_cli.models import update_check_path

PYPI_URL = "https://pypi.org/pypi/autosana/json"
CACHE_TTL_SECONDS = 86400  # 24 hours
HTTP_TIMEOUT_SECONDS = 2


def check_for_update() -> None:
    """Print a one-line warning to stderr if a newer version is available on PyPI.

    Silent on any failure. Caches the result for 24 hours.
    """
    try:
        _check_for_update_inner()
    except Exception:
        pass


def _check_for_update_inner() -> None:
    if os.environ.get("AUTOSANA_NO_UPDATE_CHECK") == "1":
        return

    if __version__ == "dev":
        return

    current = _parse_version(__version__)
    if current is None:
        return

    latest_str = _get_latest_version()
    if latest_str is None:
        return

    latest = _parse_version(latest_str)
    if latest is None:
        return

    if latest > current:
        sys.stderr.write(
            f"Warning: autosana {latest_str} is available (you have {__version__}). "
            f"Run: pipx upgrade autosana\n"
        )


def _parse_version(version_str: str) -> tuple[int, ...] | None:
    """Parse '0.2.0' into (0, 2, 0). Returns None if unparseable."""
    try:
        return tuple(int(part) for part in version_str.split("."))
    except (ValueError, AttributeError):
        return None


def _get_latest_version() -> str | None:
    """Return the latest version string from PyPI, using a 24h file cache."""
    cache_file = update_check_path()
    cached = _read_cache(cache_file)
    if cached is not None:
        return cached

    latest = _fetch_latest_from_pypi()
    if latest is not None:
        _write_cache(cache_file, latest)
    return latest


def _read_cache(cache_file: Path) -> str | None:
    """Read cached version if the file exists and is fresh (< 24h old)."""
    if not cache_file.exists():
        return None
    raw = json.loads(cache_file.read_text(encoding="utf-8"))
    checked_at = datetime.fromisoformat(raw["checked_at"])
    age_seconds = (datetime.now(timezone.utc) - checked_at).total_seconds()
    if age_seconds > CACHE_TTL_SECONDS:
        return None
    return raw["latest_version"]


def _write_cache(cache_file: Path, latest_version: str) -> None:
    cache_file.parent.mkdir(parents=True, exist_ok=True)
    payload = {
        "latest_version": latest_version,
        "checked_at": datetime.now(timezone.utc).isoformat(),
    }
    cache_file.write_text(json.dumps(payload), encoding="utf-8")


def _fetch_latest_from_pypi() -> str | None:
    """Hit PyPI JSON API with a 2s timeout. Returns version string or None."""
    try:
        with urlopen(PYPI_URL, timeout=HTTP_TIMEOUT_SECONDS) as response:
            data = json.loads(response.read().decode("utf-8"))
        return data["info"]["version"]
    except (URLError, TimeoutError, json.JSONDecodeError, KeyError, OSError):
        return None
