"""Home Assistant light output target — casts LED colors to HA lights."""

from dataclasses import dataclass, field
from datetime import datetime, timezone
from typing import List, Optional

from wled_controller.storage.output_target import OutputTarget


def _resolve_ref(new_val: str, old_val: str) -> str:
    """Resolve entity reference: empty string clears, non-empty replaces."""
    return "" if new_val == "" else (new_val or old_val)


@dataclass
class HALightMapping:
    """Maps an LED range to a single HA light entity."""

    entity_id: str = ""  # e.g. "light.living_room"
    led_start: int = 0  # start LED index (0-based)
    led_end: int = -1  # end LED index (-1 = last)
    brightness_scale: float = 1.0  # 0.0-1.0 multiplier on brightness

    def to_dict(self) -> dict:
        return {
            "entity_id": self.entity_id,
            "led_start": self.led_start,
            "led_end": self.led_end,
            "brightness_scale": self.brightness_scale,
        }

    @classmethod
    def from_dict(cls, data: dict) -> "HALightMapping":
        return cls(
            entity_id=data.get("entity_id", ""),
            led_start=data.get("led_start", 0),
            led_end=data.get("led_end", -1),
            brightness_scale=data.get("brightness_scale", 1.0),
        )


@dataclass
class HALightOutputTarget(OutputTarget):
    """Output target that casts LED colors to Home Assistant lights via service calls."""

    ha_source_id: str = ""  # references HomeAssistantSource
    color_strip_source_id: str = ""  # CSS providing the colors
    light_mappings: List[HALightMapping] = field(default_factory=list)
    update_rate: float = 2.0  # Hz (calls per second, 0.5-5.0)
    transition: float = 0.5  # HA transition seconds (smooth fade between colors)
    min_brightness_threshold: int = 0  # below this brightness → turn off light
    color_tolerance: int = 5  # skip service call if RGB delta < this

    def register_with_manager(self, manager) -> None:
        """Register this HA light target with the processor manager."""
        if self.ha_source_id and self.light_mappings:
            manager.add_ha_light_target(
                target_id=self.id,
                ha_source_id=self.ha_source_id,
                color_strip_source_id=self.color_strip_source_id,
                light_mappings=self.light_mappings,
                update_rate=self.update_rate,
                transition=self.transition,
                min_brightness_threshold=self.min_brightness_threshold,
                color_tolerance=self.color_tolerance,
            )

    def sync_with_manager(
        self,
        manager,
        *,
        settings_changed: bool = False,
        source_changed: bool = False,
        device_changed: bool = False,
        css_changed: bool = False,
        **_kwargs,
    ) -> None:
        """Push changed fields to the processor manager."""
        if settings_changed:
            manager.update_target_settings(
                self.id,
                {
                    "update_rate": self.update_rate,
                    "transition": self.transition,
                    "min_brightness_threshold": self.min_brightness_threshold,
                    "color_tolerance": self.color_tolerance,
                    "light_mappings": self.light_mappings,
                },
            )
        if css_changed:
            manager.update_target_css(self.id, self.color_strip_source_id)

    def update_fields(
        self,
        *,
        name=None,
        ha_source_id=None,
        color_strip_source_id=None,
        light_mappings=None,
        update_rate=None,
        transition=None,
        min_brightness_threshold=None,
        color_tolerance=None,
        description=None,
        tags: Optional[List[str]] = None,
        **_kwargs,
    ) -> None:
        """Apply mutable field updates."""
        super().update_fields(name=name, description=description, tags=tags)
        if ha_source_id is not None:
            self.ha_source_id = _resolve_ref(ha_source_id, self.ha_source_id)
        if color_strip_source_id is not None:
            self.color_strip_source_id = _resolve_ref(
                color_strip_source_id, self.color_strip_source_id
            )
        if light_mappings is not None:
            self.light_mappings = light_mappings
        if update_rate is not None:
            self.update_rate = max(0.5, min(5.0, float(update_rate)))
        if transition is not None:
            self.transition = max(0.0, min(10.0, float(transition)))
        if min_brightness_threshold is not None:
            self.min_brightness_threshold = int(min_brightness_threshold)
        if color_tolerance is not None:
            self.color_tolerance = int(color_tolerance)

    def to_dict(self) -> dict:
        d = super().to_dict()
        d["ha_source_id"] = self.ha_source_id
        d["color_strip_source_id"] = self.color_strip_source_id
        d["light_mappings"] = [m.to_dict() for m in self.light_mappings]
        d["update_rate"] = self.update_rate
        d["transition"] = self.transition
        d["min_brightness_threshold"] = self.min_brightness_threshold
        d["color_tolerance"] = self.color_tolerance
        return d

    @classmethod
    def from_dict(cls, data: dict) -> "HALightOutputTarget":
        mappings = [HALightMapping.from_dict(m) for m in data.get("light_mappings", [])]
        return cls(
            id=data["id"],
            name=data["name"],
            target_type="ha_light",
            ha_source_id=data.get("ha_source_id", ""),
            color_strip_source_id=data.get("color_strip_source_id", ""),
            light_mappings=mappings,
            update_rate=data.get("update_rate", 2.0),
            transition=data.get("transition", 0.5),
            min_brightness_threshold=data.get("min_brightness_threshold", 0),
            color_tolerance=data.get("color_tolerance", 5),
            description=data.get("description"),
            tags=data.get("tags", []),
            created_at=datetime.fromisoformat(
                data.get("created_at", datetime.now(timezone.utc).isoformat())
            ),
            updated_at=datetime.fromisoformat(
                data.get("updated_at", datetime.now(timezone.utc).isoformat())
            ),
        )
