"""Background auto-updater for AiCippy.

Checks for updates every 24 hours even during active sessions.
Updates are applied silently without interrupting user work.
"""

from __future__ import annotations

import asyncio
import json
import time
from typing import Final

from aicippy.installer.main import check_latest_version, perform_upgrade
from aicippy.installer.platform_detect import detect_platform
from aicippy.utils.logging import get_logger

logger = get_logger(__name__)

UPDATE_CHECK_INTERVAL_HOURS: Final[int] = 24
UPDATE_STATE_FILE: Final[str] = "update_state.json"


class AutoUpdater:
    """Background auto-updater that checks PyPI every 24 hours."""

    def __init__(self) -> None:
        platform_info = detect_platform()
        self._state_dir = platform_info.config_path / "aicippy"
        self._state_dir.mkdir(parents=True, exist_ok=True)
        self._state_file = self._state_dir / UPDATE_STATE_FILE
        self._running = False
        self._task: asyncio.Task[None] | None = None

    def _load_state(self) -> dict[str, object]:
        """Load last update check state from disk."""
        if self._state_file.exists():
            try:
                return json.loads(self._state_file.read_text(encoding="utf-8"))
            except Exception:
                logger.warning(
                    "update_state_load_failed",
                    path=str(self._state_file),
                    exc_info=True,
                )
                return {}
        return {}

    def _save_state(self, state: dict[str, object]) -> None:
        """Save update check state to disk."""
        try:
            self._state_file.write_text(
                json.dumps(state, indent=2),
                encoding="utf-8",
            )
        except Exception:
            logger.warning("update_state_save_failed", exc_info=True)

    def needs_check(self) -> bool:
        """Check if 24 hours have passed since the last update check."""
        state = self._load_state()
        last_check = state.get("last_check_timestamp", 0)
        if not isinstance(last_check, (int, float)):
            return True
        elapsed_hours = (time.time() - float(last_check)) / 3600
        return elapsed_hours >= UPDATE_CHECK_INTERVAL_HOURS

    async def check_and_update(self) -> bool:
        """Check for update and apply if available.

        Returns:
            True if an update was installed, False otherwise.

        """
        try:
            version_info = await check_latest_version()

            # Record the check regardless of outcome
            self._save_state(
                {
                    "last_check_timestamp": time.time(),
                    "last_version_seen": version_info.latest,
                    "current_version": version_info.current,
                },
            )

            if version_info.update_available and version_info.latest:
                success, _ = await perform_upgrade(silent=True)
                if success:
                    state = self._load_state()
                    state["last_update_timestamp"] = time.time()
                    state["updated_to"] = version_info.latest
                    self._save_state(state)
                    return True
            return False
        except Exception:
            logger.warning("auto_update_check_failed", exc_info=True)
            return False

    async def _background_loop(self) -> None:
        """Background loop that checks every hour if 24h has passed."""
        while self._running:
            if self.needs_check():
                await self.check_and_update()
            # Sleep for 1 hour, then re-check if 24h has passed
            await asyncio.sleep(3600)

    def start_background(self) -> None:
        """Start background update checking.

        Must be called from within a running asyncio event loop.
        If no loop is running, the background check is silently skipped.
        """
        if self._running:
            return
        self._running = True
        try:
            loop = asyncio.get_running_loop()
            self._task = loop.create_task(self._background_loop())
        except RuntimeError:
            # No running event loop; skip background check
            self._running = False

    def stop(self) -> None:
        """Stop background update checking."""
        self._running = False
        if self._task and not self._task.done():
            self._task.cancel()
        self._task = None


# Module-level singleton container (avoids global statement)
_auto_updater_holder: list[AutoUpdater | None] = [None]


class AutoUpdaterInitError(RuntimeError):
    """Exception raised when AutoUpdater cannot be initialized."""

    def __init__(self) -> None:
        """Initialize the exception."""
        super().__init__("Failed to initialize AutoUpdater")


def get_auto_updater() -> AutoUpdater:
    """Get or create the auto-updater singleton."""
    if _auto_updater_holder[0] is None:
        _auto_updater_holder[0] = AutoUpdater()
    instance = _auto_updater_holder[0]
    if instance is None:
        raise AutoUpdaterInitError()
    return instance
