"""Per-provider typed device config hierarchy.

Each provider owns its config subclass. Device.to_config() is the single place
that maps the flat Device storage model to the right typed config.
"""

from dataclasses import dataclass, field
from typing import List, Literal


@dataclass(frozen=True)
class BaseDeviceConfig:
    device_id: str
    device_url: str
    led_count: int
    software_brightness: int = 255
    test_mode_active: bool = False
    auto_shutdown: bool = False
    rgbw: bool = False


@dataclass(frozen=True)
class WLEDConfig(BaseDeviceConfig):
    device_type: Literal["wled"] = "wled"
    use_ddp: bool = False


@dataclass(frozen=True)
class DDPConfig(BaseDeviceConfig):
    """Standalone DDP receiver (Pixelblaze, ESPixelStick, Falcon, …).

    ``ddp_port`` of 0 means "use the protocol default" (4048). ``ddp_color_order``
    follows the WLED enum (0=GRB, 1=RGB, 2=BRG, 3=RBG, 4=BGR, 5=GBR). Most
    non-WLED DDP receivers expect raw RGB (1).
    """

    device_type: Literal["ddp"] = "ddp"
    ddp_port: int = 0
    ddp_destination_id: int = 1
    ddp_color_order: int = 1


@dataclass(frozen=True)
class AdalightConfig(BaseDeviceConfig):
    device_type: Literal["adalight"] = "adalight"
    baud_rate: int | None = None


@dataclass(frozen=True)
class AmbiLEDConfig(BaseDeviceConfig):
    device_type: Literal["ambiled"] = "ambiled"
    baud_rate: int | None = None


@dataclass(frozen=True)
class DMXConfig(BaseDeviceConfig):
    device_type: Literal["dmx"] = "dmx"
    dmx_protocol: str = "artnet"
    dmx_start_universe: int = 0
    dmx_start_channel: int = 1


@dataclass(frozen=True)
class ESPNowConfig(BaseDeviceConfig):
    device_type: Literal["espnow"] = "espnow"
    baud_rate: int | None = None
    espnow_peer_mac: str = ""
    espnow_channel: int = 1


@dataclass(frozen=True)
class HueConfig(BaseDeviceConfig):
    device_type: Literal["hue"] = "hue"
    hue_username: str = ""
    hue_client_key: str = ""
    hue_entertainment_group_id: str = ""


@dataclass(frozen=True)
class YeelightConfig(BaseDeviceConfig):
    """Yeelight (Xiaomi) LAN bulb / lightstrip.

    ``yeelight_min_interval_ms`` rate-limits outbound commands client-side
    so the bulb's per-second cap isn't exceeded. Default 500 ms ≈ 2 Hz.
    """

    device_type: Literal["yeelight"] = "yeelight"
    yeelight_min_interval_ms: int = 500


@dataclass(frozen=True)
class WiZConfig(BaseDeviceConfig):
    """WiZ Connected (Philips budget-tier) UDP LAN bulb.

    ``wiz_min_interval_ms`` is a client-side rate gate. WiZ tolerates much
    higher rates than Yeelight (UDP, no ack) so the default is 50 ms ≈ 20 Hz.
    """

    device_type: Literal["wiz"] = "wiz"
    wiz_min_interval_ms: int = 50


@dataclass(frozen=True)
class LIFXConfig(BaseDeviceConfig):
    """LIFX LAN bulb / lightstrip.

    LIFX recommends ≤20 commands/sec per device. ``lifx_min_interval_ms``
    defaults to 50 ms so we stay just under that ceiling.
    """

    device_type: Literal["lifx"] = "lifx"
    lifx_min_interval_ms: int = 50


@dataclass(frozen=True)
class GoveeConfig(BaseDeviceConfig):
    """Govee Wi-Fi bulb / ambient kit reachable via the LAN API.

    Each device needs "LAN Control" toggled ON in the Govee Home app before
    it answers discovery or commands. UDP fire-and-forget tolerates ~20 Hz.
    """

    device_type: Literal["govee"] = "govee"
    govee_min_interval_ms: int = 50


@dataclass(frozen=True)
class OPCConfig(BaseDeviceConfig):
    """Open Pixel Control receiver (Fadecandy, OPC bridges, hobbyist drivers).

    ``opc_channel`` of 0 broadcasts to every channel on the OPC server;
    1-255 addresses a specific output on multi-channel servers.
    """

    device_type: Literal["opc"] = "opc"
    opc_channel: int = 0


@dataclass(frozen=True)
class NanoleafConfig(BaseDeviceConfig):
    """Nanoleaf controller (Light Panels / Canvas / Shapes / Lines / Elements).

    ``nanoleaf_token`` is the long-lived auth token returned by the pairing
    handshake. Without it the controller rejects every state-mutating call,
    so device creation should be preceded by a successful pairing flow.
    """

    device_type: Literal["nanoleaf"] = "nanoleaf"
    nanoleaf_token: str = ""
    nanoleaf_min_interval_ms: int = 100


@dataclass(frozen=True)
class SPIConfig(BaseDeviceConfig):
    device_type: Literal["spi"] = "spi"
    spi_speed_hz: int = 800000
    spi_led_type: str = "WS2812B"


@dataclass(frozen=True)
class ChromaConfig(BaseDeviceConfig):
    device_type: Literal["chroma"] = "chroma"
    chroma_device_type: str = "chromalink"


@dataclass(frozen=True)
class GameSenseConfig(BaseDeviceConfig):
    device_type: Literal["gamesense"] = "gamesense"
    gamesense_device_type: str = "keyboard"


@dataclass(frozen=True)
class BLEConfig(BaseDeviceConfig):
    device_type: Literal["ble"] = "ble"
    ble_family: str = ""
    ble_govee_key: str = ""


@dataclass(frozen=True)
class GroupConfig(BaseDeviceConfig):
    device_type: Literal["group"] = "group"
    group_mode: str = "sequence"
    group_device_ids: List[str] = field(default_factory=list)


@dataclass(frozen=True)
class OpenRGBConfig(BaseDeviceConfig):
    device_type: Literal["openrgb"] = "openrgb"
    zone_mode: str = "combined"


@dataclass(frozen=True)
class MockConfig(BaseDeviceConfig):
    device_type: Literal["mock"] = "mock"
    send_latency_ms: int = 0


@dataclass(frozen=True)
class DemoConfig(BaseDeviceConfig):
    device_type: Literal["demo"] = "demo"
    send_latency_ms: int = 0


@dataclass(frozen=True)
class MQTTConfig(BaseDeviceConfig):
    device_type: Literal["mqtt"] = "mqtt"
    mqtt_source_id: str = ""  # references an MQTTSource (multi-broker)


@dataclass(frozen=True)
class WSConfig(BaseDeviceConfig):
    device_type: Literal["ws"] = "ws"


@dataclass(frozen=True)
class USBHIDConfig(BaseDeviceConfig):
    device_type: Literal["usbhid"] = "usbhid"


DeviceConfig = (
    WLEDConfig
    | DDPConfig
    | YeelightConfig
    | WiZConfig
    | LIFXConfig
    | GoveeConfig
    | OPCConfig
    | NanoleafConfig
    | AdalightConfig
    | AmbiLEDConfig
    | DMXConfig
    | ESPNowConfig
    | HueConfig
    | SPIConfig
    | ChromaConfig
    | GameSenseConfig
    | BLEConfig
    | GroupConfig
    | MQTTConfig
    | WSConfig
    | USBHIDConfig
    | OpenRGBConfig
    | MockConfig
    | DemoConfig
)
