"""Process start/stop watcher for auto-boost triggering.

Uses WMI event subscription on Windows to detect when games launch
or exit, triggering automatic boost activation/deactivation.
"""
from __future__ import annotations

import asyncio
import logging
import threading
import time
from typing import Callable, Optional

from mesh_boost.optimizer.base import IS_WINDOWS, run_powershell
from mesh_boost.detector.game_detector import GameDetector

logger = logging.getLogger(__name__)


class ProcessWatcher:
    """Watch for game process start/stop events.

    Uses polling-based detection (checks every N seconds) which is
    more reliable across Windows versions than WMI event subscriptions.
    """

    def __init__(
        self,
        detector: Optional[GameDetector] = None,
        poll_interval: float = 3.0,
        on_game_start: Optional[Callable[[dict], None]] = None,
        on_game_stop: Optional[Callable[[dict], None]] = None,
    ):
        self._detector = detector or GameDetector()
        self._poll_interval = poll_interval
        self._on_game_start = on_game_start
        self._on_game_stop = on_game_stop
        self._running = False
        self._thread: Optional[threading.Thread] = None
        self._current_game: Optional[dict] = None

    def start(self):
        """Start watching for game processes in a background thread."""
        if self._running:
            return

        self._running = True
        self._thread = threading.Thread(target=self._poll_loop, daemon=True)
        self._thread.start()
        logger.info("Process watcher started (poll every %.1fs)", self._poll_interval)

    def stop(self):
        """Stop watching."""
        self._running = False
        if self._thread:
            self._thread.join(timeout=5)
            self._thread = None
        logger.info("Process watcher stopped")

    @property
    def current_game(self) -> Optional[dict]:
        """The currently detected game, or None."""
        return self._current_game

    @property
    def is_running(self) -> bool:
        return self._running

    def _poll_loop(self):
        """Main polling loop — runs in background thread."""
        while self._running:
            try:
                game = self._detector.get_running_game()

                if game and not self._current_game:
                    # Game just started
                    self._current_game = game
                    logger.info("Game detected: %s (PID %s)", game.get("name"), game.get("pid"))
                    if self._on_game_start:
                        try:
                            self._on_game_start(game)
                        except Exception as e:
                            logger.error("on_game_start callback error: %s", e)

                elif not game and self._current_game:
                    # Game just stopped
                    old_game = self._current_game
                    self._current_game = None
                    logger.info("Game stopped: %s", old_game.get("name"))
                    if self._on_game_stop:
                        try:
                            self._on_game_stop(old_game)
                        except Exception as e:
                            logger.error("on_game_stop callback error: %s", e)

                elif game and self._current_game:
                    # Check if it's a different game
                    if game.get("name") != self._current_game.get("name"):
                        old = self._current_game
                        self._current_game = game
                        logger.info("Game switched: %s -> %s", old.get("name"), game.get("name"))
                        if self._on_game_stop:
                            try:
                                self._on_game_stop(old)
                            except Exception:
                                pass
                        if self._on_game_start:
                            try:
                                self._on_game_start(game)
                            except Exception:
                                pass

            except Exception as e:
                logger.error("Poll error: %s", e)

            time.sleep(self._poll_interval)


class AsyncProcessWatcher:
    """Async version of ProcessWatcher for integration with asyncio agents."""

    def __init__(
        self,
        detector: Optional[GameDetector] = None,
        poll_interval: float = 3.0,
        on_game_start: Optional[Callable] = None,
        on_game_stop: Optional[Callable] = None,
    ):
        self._detector = detector or GameDetector()
        self._poll_interval = poll_interval
        self._on_game_start = on_game_start
        self._on_game_stop = on_game_stop
        self._running = False
        self._current_game: Optional[dict] = None

    async def start(self):
        """Start watching as an async task."""
        self._running = True
        logger.info("Async process watcher started")
        while self._running:
            try:
                game = await asyncio.get_event_loop().run_in_executor(
                    None, self._detector.get_running_game
                )

                if game and not self._current_game:
                    self._current_game = game
                    logger.info("Game detected: %s", game.get("name"))
                    if self._on_game_start:
                        if asyncio.iscoroutinefunction(self._on_game_start):
                            await self._on_game_start(game)
                        else:
                            self._on_game_start(game)

                elif not game and self._current_game:
                    old = self._current_game
                    self._current_game = None
                    logger.info("Game stopped: %s", old.get("name"))
                    if self._on_game_stop:
                        if asyncio.iscoroutinefunction(self._on_game_stop):
                            await self._on_game_stop(old)
                        else:
                            self._on_game_stop(old)

            except Exception as e:
                logger.error("Async poll error: %s", e)

            await asyncio.sleep(self._poll_interval)

    def stop(self):
        self._running = False

    @property
    def current_game(self) -> Optional[dict]:
        return self._current_game
