"""Yeelight device provider — LAN-discoverable Xiaomi smart bulbs."""

from __future__ import annotations

from typing import TYPE_CHECKING, List
from urllib.parse import urlparse

from ledgrab.core.devices.led_client import (
    DeviceHealth,
    DiscoveredDevice,
    LEDClient,
    LEDDeviceProvider,
    ProviderDeps,
)
from ledgrab.core.devices.yeelight_client import (
    YeelightClient,
    discover_yeelight_bulbs,
    parse_yeelight_url,
)
from ledgrab.utils import get_logger

if TYPE_CHECKING:
    from ledgrab.core.devices.device_config import YeelightConfig

logger = get_logger(__name__)


class YeelightDeviceProvider(LEDDeviceProvider):
    """Provider for Yeelight (Xiaomi) LAN bulbs and lightstrips.

    Single-pixel device: the LED client averages the incoming strip down to
    one RGB color before sending. LED count is user-supplied; it controls
    the pixel-source mapping, not anything on the wire.
    """

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

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

    def create_client(self, config: "YeelightConfig", *, deps: ProviderDeps) -> LEDClient:
        return YeelightClient(
            config.device_url,
            led_count=config.led_count,
            min_interval_s=max(0.0, config.yeelight_min_interval_ms / 1000.0),
        )

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

    async def validate_device(self, url: str) -> dict:
        """Validate the URL is parseable. Yeelight bulbs are single-pixel so
        we don't return a led_count — the user fills it in."""
        try:
            host = parse_yeelight_url(url)
        except ValueError as exc:
            raise ValueError(f"Invalid Yeelight URL: {exc}") from exc
        logger.info("Yeelight device URL validated: host=%s", host)
        return {}

    async def discover(self, timeout: float = 3.0) -> List[DiscoveredDevice]:
        """Scan the LAN via Yeelight's SSDP variant on 239.255.255.250:1982."""
        try:
            bulbs = await discover_yeelight_bulbs(timeout=min(timeout, 5.0))
        except (OSError, RuntimeError) as exc:
            # Multicast can fail on Windows when no network is up, on
            # firewalled hosts, or on Android sandboxes. Discovery is
            # best-effort — log and return empty.
            logger.warning("Yeelight discovery failed: %s", exc)
            return []

        results: List[DiscoveredDevice] = []
        for headers in bulbs:
            location = headers.get("location", "")
            if not location:
                continue
            parsed = urlparse(location)
            host = parsed.hostname or ""
            if not host:
                continue
            url = f"yeelight://{host}"
            model = headers.get("model") or "yeelight"
            fw = headers.get("fw_ver") or None
            bulb_id = headers.get("id", "") or host
            results.append(
                DiscoveredDevice(
                    name=f"Yeelight {model}".strip(),
                    url=url,
                    device_type="yeelight",
                    ip=host,
                    mac=bulb_id,  # the bulb's hex id is the closest stable identifier
                    led_count=None,
                    version=fw,
                )
            )
        logger.info("Yeelight SSDP scan found %d bulb(s)", len(results))
        return results
