"""Non-blocking version upgrade checking and auto-update for Hikaflow CLI.

Checks PyPI for newer versions with a 24-hour cache to avoid
excessive network requests. Can auto-install updates and restart.
"""

from __future__ import annotations

import json
import os
import subprocess
import sys
import time
from pathlib import Path
from typing import Optional

# Cache settings
CACHE_FILE = Path.home() / ".hikaflow" / "version_cache.json"
CHECK_INTERVAL = 86400  # 24 hours
AUTO_UPDATE_MARKER = Path.home() / ".hikaflow" / "auto_update"


def check_for_upgrade(current_version: str) -> Optional[str]:
    """Check PyPI for a newer version of autodebug.

    Uses a local cache to avoid checking on every invocation.
    Fails silently - never interrupts the user's workflow.

    Args:
        current_version: The currently installed version string.

    Returns:
        The newer version string if available, or None.
    """
    try:
        # Check cache first
        if CACHE_FILE.exists():
            cache = json.loads(CACHE_FILE.read_text())
            if time.time() - cache.get("checked_at", 0) < CHECK_INTERVAL:
                latest = cache.get("latest")
                if latest and latest != current_version:
                    return latest
                return None

        # Fetch from PyPI with a short timeout
        import requests

        resp = requests.get(
            "https://pypi.org/pypi/hikaflow/json",
            timeout=2,
        )
        if resp.ok:
            latest = resp.json()["info"]["version"]
            # Cache result
            CACHE_FILE.parent.mkdir(parents=True, exist_ok=True)
            CACHE_FILE.write_text(
                json.dumps({"latest": latest, "checked_at": time.time()})
            )
            if latest != current_version and _is_newer(latest, current_version):
                return latest
    except Exception:
        pass  # Never fail the user's command over a version check

    return None


def auto_upgrade(current_version: str) -> bool:
    """Check for updates and auto-install if available.

    Installs the new version via pip and returns True if the CLI
    should restart. Uses a marker file to prevent restart loops.

    Args:
        current_version: The currently installed version string.

    Returns:
        True if a new version was installed and the CLI should restart.
    """
    # Prevent restart loop: if we just restarted after an update, skip
    if AUTO_UPDATE_MARKER.exists():
        try:
            marker_data = json.loads(AUTO_UPDATE_MARKER.read_text())
            # If we updated in the last 60 seconds, don't check again
            if time.time() - marker_data.get("updated_at", 0) < 60:
                return False
        except (json.JSONDecodeError, OSError):
            pass
        finally:
            # Clean up marker after reading
            try:
                AUTO_UPDATE_MARKER.unlink()
            except OSError:
                pass

    new_version = check_for_upgrade(current_version)
    if not new_version:
        return False

    # Auto-update is opt-out: users can set HIKAFLOW_NO_AUTO_UPDATE=1 to disable
    if os.environ.get("HIKAFLOW_NO_AUTO_UPDATE"):
        return False

    try:
        # Install the new version quietly
        result = subprocess.run(
            [sys.executable, "-m", "pip", "install", "-U", "hikaflow", "-q"],
            capture_output=True,
            text=True,
            timeout=60,
        )
        if result.returncode != 0:
            return False

        # Write marker to prevent restart loop
        AUTO_UPDATE_MARKER.parent.mkdir(parents=True, exist_ok=True)
        AUTO_UPDATE_MARKER.write_text(
            json.dumps({"updated_at": time.time(), "from": current_version, "to": new_version})
        )

        # Clear the version cache so next run doesn't re-trigger
        try:
            CACHE_FILE.unlink()
        except OSError:
            pass

        return True

    except (subprocess.TimeoutExpired, OSError, Exception):
        return False


def restart_cli():
    """Re-exec the current CLI process after an update.

    Replaces the current process with a fresh one running the
    updated code. Falls back to a no-op if exec fails.
    """
    try:
        os.execv(sys.executable, [sys.executable] + sys.argv)
    except OSError:
        pass  # If re-exec fails, just continue with current version


def _is_newer(latest: str, current: str) -> bool:
    """Compare version strings to check if latest > current.

    Simple tuple comparison of version parts.
    """
    try:
        latest_parts = tuple(int(x) for x in latest.split(".")[:3])
        current_parts = tuple(int(x) for x in current.split(".")[:3])
        return latest_parts > current_parts
    except (ValueError, IndexError):
        return False
