"""Device health monitoring mixin for ProcessorManager.

Extracted from processor_manager.py to keep files under 800 lines.
"""

import asyncio
from datetime import datetime, timezone

from wled_controller.core.devices.led_client import (
    DeviceHealth,
    check_device_health,
    get_device_capabilities,
)
from wled_controller.utils import get_logger

logger = get_logger(__name__)


class DeviceHealthMixin:
    """Mixin providing health monitoring loop and health check methods.

    Requires the host class to have:
        _devices: Dict[str, DeviceState]
        _processors: Dict[str, TargetProcessor]
        _health_monitoring_active: bool
        _http_client: Optional[httpx.AsyncClient]
        _device_store: object
        fire_event(event: dict) -> None
        _get_http_client() -> httpx.AsyncClient
    """

    # ===== HEALTH MONITORING =====

    async def start_health_monitoring(self):
        """Start background health checks for all registered devices."""
        self._health_monitoring_active = True
        for device_id in self._devices:
            self._start_device_health_check(device_id)
        await self._metrics_history.start()
        logger.info("Started health monitoring for all devices")

    async def stop_health_monitoring(self):
        """Stop all background health checks."""
        self._health_monitoring_active = False
        for device_id in list(self._devices.keys()):
            self._stop_device_health_check(device_id)
        logger.info("Stopped health monitoring for all devices")

    def _start_device_health_check(self, device_id: str):
        state = self._devices.get(device_id)
        if not state:
            return
        # Skip periodic health checks for virtual devices (always online)
        if "health_check" not in get_device_capabilities(state.device_type):
            state.health = DeviceHealth(
                online=True, latency_ms=0.0, last_checked=datetime.now(timezone.utc)
            )
            return
        if state.health_task and not state.health_task.done():
            return
        state.health_task = asyncio.create_task(self._health_check_loop(device_id))

    def _stop_device_health_check(self, device_id: str):
        state = self._devices.get(device_id)
        if not state or not state.health_task:
            return
        state.health_task.cancel()
        state.health_task = None

    def _device_is_processing(self, device_id: str) -> bool:
        """Check if any target is actively streaming to this device."""
        return any(
            p.device_id == device_id and p.is_running
            for p in self._processors.values()
        )

    def _is_device_streaming(self, device_id: str) -> bool:
        """Check if any running processor targets this device."""
        for proc in self._processors.values():
            if getattr(proc, 'device_id', None) == device_id and proc.is_running:
                return True
        return False

    async def _health_check_loop(self, device_id: str):
        """Background loop that periodically checks a device.

        Uses adaptive intervals: 10s for actively streaming devices,
        60s for idle devices, to balance responsiveness with overhead.
        """
        state = self._devices.get(device_id)
        if not state:
            return

        ACTIVE_INTERVAL = 10   # streaming devices — faster detection
        IDLE_INTERVAL = 60     # idle devices — less overhead

        try:
            while self._health_monitoring_active:
                await self._check_device_health(device_id)
                interval = ACTIVE_INTERVAL if self._is_device_streaming(device_id) else IDLE_INTERVAL
                await asyncio.sleep(interval)
        except asyncio.CancelledError:
            logger.debug("Device health monitor cancelled for %s", device_id)
            pass
        except Exception as e:
            logger.error(f"Fatal error in health check loop for {device_id}: {e}")

    async def _check_device_health(self, device_id: str):
        """Check device health. Also auto-syncs LED count if changed."""
        state = self._devices.get(device_id)
        if not state:
            return
        prev_online = state.health.online
        client = await self._get_http_client()
        state.health = await check_device_health(
            state.device_type, state.device_url, client, state.health,
        )

        # Fire event when online status changes
        if state.health.online != prev_online:
            self.fire_event({
                "type": "device_health_changed",
                "device_id": device_id,
                "online": state.health.online,
                "latency_ms": state.health.latency_ms,
            })

        # Auto-sync LED count
        reported = state.health.device_led_count
        if reported and reported != state.led_count and self._device_store:
            old_count = state.led_count
            logger.info(
                f"Device {device_id} LED count changed: {old_count} → {reported}"
            )
            try:
                self._device_store.update_device(device_id, led_count=reported)
                state.led_count = reported
            except Exception as e:
                logger.error(f"Failed to sync LED count for {device_id}: {e}")

    async def force_device_health_check(self, device_id: str) -> dict:
        """Run an immediate health check for a device and return the result."""
        if device_id not in self._devices:
            raise ValueError(f"Device {device_id} not found")
        await self._check_device_health(device_id)
        return self.get_device_health_dict(device_id)
