"""OpenRGB device provider — factory, validation, health checks, discovery."""

import asyncio
from typing import List, Tuple

from wled_controller.core.devices.led_client import (
    DeviceHealth,
    DiscoveredDevice,
    LEDClient,
    LEDDeviceProvider,
)
from wled_controller.core.devices.openrgb_client import (
    OpenRGBLEDClient,
    parse_openrgb_url,
)
from wled_controller.utils import get_logger

logger = get_logger(__name__)


class OpenRGBDeviceProvider(LEDDeviceProvider):
    """Provider for OpenRGB-controlled PC peripherals."""

    @property
    def device_type(self) -> str:
        return "openrgb"

    @property
    def capabilities(self) -> set:
        return {"health_check", "auto_restore", "static_color", "brightness_control"}

    def create_client(self, url: str, **kwargs) -> LEDClient:
        return OpenRGBLEDClient(
            url,
            zone_mode=kwargs.get("zone_mode", "combined"),
        )

    async def check_health(self, url: str, http_client, prev_health=None) -> DeviceHealth:
        return await OpenRGBLEDClient.check_health(url, http_client, prev_health)

    async def validate_device(self, url: str) -> dict:
        """Validate an OpenRGB device URL by connecting and reading device info.

        Returns:
            dict with 'led_count' key.

        Raises:
            Exception on validation failure.
        """
        host, port, device_index, zone_names = parse_openrgb_url(url)

        def _validate_sync():
            from openrgb import OpenRGBClient

            client = OpenRGBClient(host, port, name="WLED Controller (validate)")
            try:
                devices = client.devices
                if device_index >= len(devices):
                    raise ValueError(
                        f"Device index {device_index} out of range "
                        f"(server has {len(devices)} device(s))"
                    )
                device = devices[device_index]

                if zone_names:
                    filt_set = {n.lower() for n in zone_names}
                    matching = [z for z in device.zones if z.name.lower() in filt_set]
                    if not matching:
                        avail = [z.name for z in device.zones]
                        raise ValueError(
                            f"Zone(s) {zone_names} not found on '{device.name}'. "
                            f"Available zones: {avail}"
                        )
                    led_count = sum(len(z.leds) for z in matching)
                    logger.info(
                        f"OpenRGB device validated: '{device.name}' "
                        f"zones {zone_names} ({led_count} LEDs)"
                    )
                else:
                    led_count = len(device.leds)
                    logger.info(
                        f"OpenRGB device validated: '{device.name}' ({led_count} LEDs)"
                    )
                return {"led_count": led_count}
            finally:
                client.disconnect()

        return await asyncio.to_thread(_validate_sync)

    async def discover(self, timeout: float = 3.0) -> List[DiscoveredDevice]:
        """Discover OpenRGB devices on localhost."""

        def _discover_sync():
            from openrgb import OpenRGBClient

            results = []
            try:
                client = OpenRGBClient("localhost", 6742, name="WLED Controller (discover)")
            except Exception as e:
                logger.debug(f"OpenRGB discovery failed (server not running?): {e}")
                return results

            try:
                for idx, device in enumerate(client.devices):
                    results.append(
                        DiscoveredDevice(
                            name=f"{device.name}",
                            url=f"openrgb://localhost:6742/{idx}",
                            device_type="openrgb",
                            ip="localhost",
                            mac="",
                            led_count=len(device.leds),
                            version=None,
                        )
                    )
            except Exception as e:
                logger.warning(f"OpenRGB discovery error enumerating devices: {e}")
            finally:
                try:
                    client.disconnect()
                except Exception:
                    pass

            return results

        return await asyncio.to_thread(_discover_sync)

    async def set_color(self, url: str, color: Tuple[int, int, int], **kwargs) -> None:
        """Set all LEDs on the OpenRGB device (or target zone) to a solid color."""
        host, port, device_index, zone_names = parse_openrgb_url(url)

        def _set_color_sync():
            from openrgb import OpenRGBClient
            from openrgb.utils import RGBColor

            client = OpenRGBClient(host, port, name="WLED Controller (color)")
            try:
                devices = client.devices
                if device_index >= len(devices):
                    raise ValueError(f"Device index {device_index} out of range")
                device = devices[device_index]
                color_obj = RGBColor(color[0], color[1], color[2])
                # Use zone-level calls for better motherboard compatibility
                if zone_names:
                    filt_set = {n.lower() for n in zone_names}
                    for zone in device.zones:
                        if zone.name.lower() in filt_set:
                            zone.set_color(color_obj)
                else:
                    for zone in device.zones:
                        zone.set_color(color_obj)
            finally:
                client.disconnect()

        await asyncio.to_thread(_set_color_sync)
