"""Device-related schemas (CRUD, calibration, device state)."""

from datetime import datetime
from typing import Dict, List, Literal, Optional

from pydantic import BaseModel, Field


class DeviceCreate(BaseModel):
    """Request to create/attach an LED device."""

    name: str = Field(description="Device name", min_length=1, max_length=100)
    url: str = Field(description="Device URL (e.g., http://192.168.1.100 or COM3)")
    device_type: str = Field(default="wled", description="LED device type (e.g., wled, adalight)")
    led_count: Optional[int] = Field(None, ge=1, le=10000, description="Number of LEDs (required for adalight)")
    baud_rate: Optional[int] = Field(None, description="Serial baud rate (for adalight devices)")
    auto_shutdown: Optional[bool] = Field(default=None, description="Turn off device when server stops (defaults to true for adalight)")
    send_latency_ms: Optional[int] = Field(None, ge=0, le=5000, description="Simulated send latency in ms (mock devices)")
    rgbw: Optional[bool] = Field(None, description="RGBW mode (mock devices)")
    zone_mode: Optional[str] = Field(None, description="OpenRGB zone mode: combined or separate")
    tags: List[str] = Field(default_factory=list, description="User-defined tags")
    # DMX (Art-Net / sACN) fields
    dmx_protocol: Optional[str] = Field(None, description="DMX protocol: artnet or sacn")
    dmx_start_universe: Optional[int] = Field(None, ge=0, le=32767, description="DMX start universe")
    dmx_start_channel: Optional[int] = Field(None, ge=1, le=512, description="DMX start channel (1-512)")
    # ESP-NOW fields
    espnow_peer_mac: Optional[str] = Field(None, description="ESP-NOW peer MAC address (e.g. AA:BB:CC:DD:EE:FF)")
    espnow_channel: Optional[int] = Field(None, ge=1, le=14, description="ESP-NOW WiFi channel (1-14)")
    # Philips Hue fields
    hue_username: Optional[str] = Field(None, description="Hue bridge username (from pairing)")
    hue_client_key: Optional[str] = Field(None, description="Hue entertainment client key (hex)")
    hue_entertainment_group_id: Optional[str] = Field(None, description="Hue entertainment group/zone ID")
    # SPI Direct fields
    spi_speed_hz: Optional[int] = Field(None, ge=100000, le=4000000, description="SPI clock speed in Hz")
    spi_led_type: Optional[str] = Field(None, description="LED chipset: WS2812, WS2812B, WS2811, SK6812, SK6812_RGBW")
    # Razer Chroma fields
    chroma_device_type: Optional[str] = Field(None, description="Chroma peripheral type: keyboard, mouse, mousepad, headset, chromalink, keypad")
    # SteelSeries GameSense fields
    gamesense_device_type: Optional[str] = Field(None, description="GameSense device type: keyboard, mouse, headset, mousepad, indicator")
    default_css_processing_template_id: Optional[str] = Field(None, description="Default color strip processing template ID")


class DeviceUpdate(BaseModel):
    """Request to update device information."""

    name: Optional[str] = Field(None, description="Device name", min_length=1, max_length=100)
    url: Optional[str] = Field(None, description="Device URL or serial port")
    enabled: Optional[bool] = Field(None, description="Whether device is enabled")
    led_count: Optional[int] = Field(None, ge=1, le=10000, description="Number of LEDs (for devices with manual_led_count capability)")
    baud_rate: Optional[int] = Field(None, description="Serial baud rate (for adalight devices)")
    auto_shutdown: Optional[bool] = Field(None, description="Turn off device when server stops")
    send_latency_ms: Optional[int] = Field(None, ge=0, le=5000, description="Simulated send latency in ms (mock devices)")
    rgbw: Optional[bool] = Field(None, description="RGBW mode (mock devices)")
    zone_mode: Optional[str] = Field(None, description="OpenRGB zone mode: combined or separate")
    tags: Optional[List[str]] = None
    dmx_protocol: Optional[str] = Field(None, description="DMX protocol: artnet or sacn")
    dmx_start_universe: Optional[int] = Field(None, ge=0, le=32767, description="DMX start universe")
    dmx_start_channel: Optional[int] = Field(None, ge=1, le=512, description="DMX start channel (1-512)")
    espnow_peer_mac: Optional[str] = Field(None, description="ESP-NOW peer MAC address")
    espnow_channel: Optional[int] = Field(None, ge=1, le=14, description="ESP-NOW WiFi channel")
    hue_username: Optional[str] = Field(None, description="Hue bridge username")
    hue_client_key: Optional[str] = Field(None, description="Hue entertainment client key")
    hue_entertainment_group_id: Optional[str] = Field(None, description="Hue entertainment group ID")
    spi_speed_hz: Optional[int] = Field(None, ge=100000, le=4000000, description="SPI clock speed")
    spi_led_type: Optional[str] = Field(None, description="LED chipset type")
    chroma_device_type: Optional[str] = Field(None, description="Chroma peripheral type")
    gamesense_device_type: Optional[str] = Field(None, description="GameSense device type")
    default_css_processing_template_id: Optional[str] = Field(None, description="Default color strip processing template ID")


class CalibrationLineSchema(BaseModel):
    """One LED line in advanced calibration."""

    picture_source_id: str = Field(description="Picture source (monitor) to sample from")
    edge: Literal["top", "right", "bottom", "left"] = Field(description="Screen edge to sample")
    led_count: int = Field(ge=1, description="Number of LEDs in this line")
    span_start: float = Field(default=0.0, ge=0.0, le=1.0, description="Start fraction along edge")
    span_end: float = Field(default=1.0, ge=0.0, le=1.0, description="End fraction along edge")
    reverse: bool = Field(default=False, description="Reverse LED direction")
    border_width: int = Field(default=10, ge=1, le=100, description="Sampling depth in pixels")


class Calibration(BaseModel):
    """Calibration configuration for pixel-to-LED mapping."""

    mode: Literal["simple", "advanced"] = Field(
        default="simple",
        description="Calibration mode: simple (4-edge) or advanced (multi-source lines)"
    )
    # Advanced mode: ordered list of lines
    lines: Optional[List[CalibrationLineSchema]] = Field(
        default=None,
        description="Line list for advanced mode (ignored in simple mode)"
    )
    # Simple mode fields
    layout: Literal["clockwise", "counterclockwise"] = Field(
        default="clockwise",
        description="LED strip layout direction"
    )
    start_position: Literal["top_left", "top_right", "bottom_left", "bottom_right"] = Field(
        default="bottom_left",
        description="Starting corner of the LED strip"
    )
    offset: int = Field(
        default=0,
        ge=0,
        description="Number of LEDs from physical LED 0 to start corner (along strip direction)"
    )
    leds_top: int = Field(default=0, ge=0, description="Number of LEDs on the top edge")
    leds_right: int = Field(default=0, ge=0, description="Number of LEDs on the right edge")
    leds_bottom: int = Field(default=0, ge=0, description="Number of LEDs on the bottom edge")
    leds_left: int = Field(default=0, ge=0, description="Number of LEDs on the left edge")
    # Per-edge span: fraction of screen side covered by LEDs (0.0-1.0)
    span_top_start: float = Field(default=0.0, ge=0.0, le=1.0, description="Start of top edge coverage")
    span_top_end: float = Field(default=1.0, ge=0.0, le=1.0, description="End of top edge coverage")
    span_right_start: float = Field(default=0.0, ge=0.0, le=1.0, description="Start of right edge coverage")
    span_right_end: float = Field(default=1.0, ge=0.0, le=1.0, description="End of right edge coverage")
    span_bottom_start: float = Field(default=0.0, ge=0.0, le=1.0, description="Start of bottom edge coverage")
    span_bottom_end: float = Field(default=1.0, ge=0.0, le=1.0, description="End of bottom edge coverage")
    span_left_start: float = Field(default=0.0, ge=0.0, le=1.0, description="Start of left edge coverage")
    span_left_end: float = Field(default=1.0, ge=0.0, le=1.0, description="End of left edge coverage")
    # Skip LEDs at start/end of strip
    skip_leds_start: int = Field(default=0, ge=0, description="LEDs to skip (black out) at the start of the strip")
    skip_leds_end: int = Field(default=0, ge=0, description="LEDs to skip (black out) at the end of the strip")
    border_width: int = Field(default=10, ge=1, le=100, description="Border width in pixels for edge sampling")


class CalibrationTestModeRequest(BaseModel):
    """Request to set calibration test mode with multiple edges."""

    edges: Dict[str, List[int]] = Field(
        default_factory=dict,
        description="Map of active edge names to RGB colors. "
                    "E.g. {'top': [255, 0, 0], 'left': [255, 255, 0]}. "
                    "Empty dict = exit test mode."
    )


class CalibrationTestModeResponse(BaseModel):
    """Response for calibration test mode."""

    test_mode: bool = Field(description="Whether test mode is active")
    active_edges: List[str] = Field(default_factory=list, description="Currently lit edges")
    device_id: str = Field(description="Device ID")


class BrightnessRequest(BaseModel):
    """Request to set device brightness."""

    brightness: int = Field(ge=0, le=255, description="Brightness level (0-255)")


class PowerRequest(BaseModel):
    """Request to set device power state."""

    power: bool = Field(description="Whether the device should be on (true) or off (false)")


class DeviceResponse(BaseModel):
    """Device information response."""

    id: str = Field(description="Device ID")
    name: str = Field(description="Device name")
    url: str = Field(description="Device URL")
    device_type: str = Field(default="wled", description="LED device type")
    led_count: int = Field(description="Total number of LEDs")
    enabled: bool = Field(description="Whether device is enabled")
    baud_rate: Optional[int] = Field(None, description="Serial baud rate")
    auto_shutdown: bool = Field(default=False, description="Restore device to idle state when targets stop")
    send_latency_ms: int = Field(default=0, description="Simulated send latency in ms (mock devices)")
    rgbw: bool = Field(default=False, description="RGBW mode (mock devices)")
    zone_mode: str = Field(default="combined", description="OpenRGB zone mode: combined or separate")
    capabilities: List[str] = Field(default_factory=list, description="Device type capabilities")
    tags: List[str] = Field(default_factory=list, description="User-defined tags")
    dmx_protocol: str = Field(default="artnet", description="DMX protocol: artnet or sacn")
    dmx_start_universe: int = Field(default=0, description="DMX start universe")
    dmx_start_channel: int = Field(default=1, description="DMX start channel (1-512)")
    espnow_peer_mac: str = Field(default="", description="ESP-NOW peer MAC address")
    espnow_channel: int = Field(default=1, description="ESP-NOW WiFi channel")
    hue_username: str = Field(default="", description="Hue bridge username")
    hue_client_key: str = Field(default="", description="Hue entertainment client key")
    hue_entertainment_group_id: str = Field(default="", description="Hue entertainment group ID")
    spi_speed_hz: int = Field(default=800000, description="SPI clock speed in Hz")
    spi_led_type: str = Field(default="WS2812B", description="LED chipset type")
    chroma_device_type: str = Field(default="chromalink", description="Chroma peripheral type")
    gamesense_device_type: str = Field(default="keyboard", description="GameSense device type")
    default_css_processing_template_id: str = Field(default="", description="Default color strip processing template ID")
    created_at: datetime = Field(description="Creation timestamp")
    updated_at: datetime = Field(description="Last update timestamp")


class DeviceListResponse(BaseModel):
    """List of devices response."""

    devices: List[DeviceResponse] = Field(description="List of devices")
    count: int = Field(description="Number of devices")


class DeviceStateResponse(BaseModel):
    """Device health/connection state response."""

    device_id: str = Field(description="Device ID")
    device_type: str = Field(default="wled", description="LED device type")
    device_online: bool = Field(default=False, description="Whether device is reachable")
    device_latency_ms: Optional[float] = Field(None, description="Health check latency in ms")
    device_name: Optional[str] = Field(None, description="Device name reported by firmware")
    device_version: Optional[str] = Field(None, description="Firmware version")
    device_led_count: Optional[int] = Field(None, description="LED count reported by device")
    device_rgbw: Optional[bool] = Field(None, description="Whether device uses RGBW LEDs")
    device_led_type: Optional[str] = Field(None, description="LED chip type (e.g. WS2812B, SK6812 RGBW)")
    device_fps: Optional[int] = Field(None, description="Device-reported FPS (WLED internal refresh rate)")
    device_last_checked: Optional[datetime] = Field(None, description="Last health check time")
    device_error: Optional[str] = Field(None, description="Last health check error")
    test_mode: bool = Field(default=False, description="Whether calibration test mode is active")
    test_mode_edges: List[str] = Field(default_factory=list, description="Currently lit edges in test mode")


class DiscoveredDeviceResponse(BaseModel):
    """A single device found via network discovery."""

    name: str = Field(description="Device name (from mDNS or firmware)")
    url: str = Field(description="Device URL")
    device_type: str = Field(default="wled", description="Device type")
    ip: str = Field(description="IP address")
    mac: str = Field(default="", description="MAC address")
    led_count: Optional[int] = Field(None, description="LED count (if reachable)")
    version: Optional[str] = Field(None, description="Firmware version")
    already_added: bool = Field(default=False, description="Whether this device is already in the system")


class DiscoverDevicesResponse(BaseModel):
    """Response from device discovery scan."""

    devices: List[DiscoveredDeviceResponse] = Field(description="Discovered devices")
    count: int = Field(description="Total devices found")
    scan_duration_ms: float = Field(description="How long the scan took in milliseconds")


class OpenRGBZoneResponse(BaseModel):
    """A single zone on an OpenRGB device."""

    name: str = Field(description="Zone name (e.g. JRAINBOW2)")
    led_count: int = Field(description="Number of LEDs in this zone")
    zone_type: str = Field(description="Zone type (linear, single, matrix)")


class OpenRGBZonesResponse(BaseModel):
    """Response from OpenRGB zone listing."""

    device_name: str = Field(description="OpenRGB device name")
    zones: List[OpenRGBZoneResponse] = Field(description="Available zones")
