"""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.bindable import BindableFloat
from wled_controller.storage.output_target import OutputTarget
from wled_controller.storage.utils import resolve_ref


@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: BindableFloat = field(default_factory=lambda: BindableFloat(1.0))

    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.to_dict(),
        }

    @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=BindableFloat.from_raw(data.get("brightness_scale"), default=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
    brightness: BindableFloat = field(default_factory=lambda: BindableFloat(1.0))
    light_mappings: List[HALightMapping] = field(default_factory=list)
    update_rate: BindableFloat = field(default_factory=lambda: BindableFloat(2.0))
    transition: BindableFloat = field(default_factory=lambda: BindableFloat(0.5))
    min_brightness_threshold: BindableFloat = field(default_factory=lambda: BindableFloat(0.0))
    color_tolerance: BindableFloat = field(default_factory=lambda: BindableFloat(5.0))

    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,
                brightness=self.brightness,
                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,
                {
                    "brightness": self.brightness,
                    "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,
        brightness=None,
        # legacy compat
        brightness_value_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 brightness is not None:
            self.brightness = self.brightness.apply_update(brightness)
        elif brightness_value_source_id is not None:
            self.brightness = BindableFloat(
                value=self.brightness.value,
                source_id=resolve_ref(brightness_value_source_id, self.brightness.source_id),
            )
        if light_mappings is not None:
            self.light_mappings = light_mappings
        if update_rate is not None:
            self.update_rate = self.update_rate.apply_update(update_rate)
        if transition is not None:
            self.transition = self.transition.apply_update(transition)
        if min_brightness_threshold is not None:
            self.min_brightness_threshold = self.min_brightness_threshold.apply_update(
                min_brightness_threshold
            )
        if color_tolerance is not None:
            self.color_tolerance = self.color_tolerance.apply_update(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["brightness"] = self.brightness.to_dict()
        d["light_mappings"] = [m.to_dict() for m in self.light_mappings]
        d["update_rate"] = self.update_rate.to_dict()
        d["transition"] = self.transition.to_dict()
        d["min_brightness_threshold"] = self.min_brightness_threshold.to_dict()
        d["color_tolerance"] = self.color_tolerance.to_dict()
        return d

    @classmethod
    def from_dict(cls, data: dict) -> "HALightOutputTarget":
        mappings = [HALightMapping.from_dict(m) for m in data.get("light_mappings", [])]
        # Legacy migration: brightness_value_source_id → brightness.source_id
        brightness = BindableFloat.from_legacy(
            data.get("brightness"),
            legacy_source_id=data.get("brightness_value_source_id", ""),
            default=1.0,
        )
        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", ""),
            brightness=brightness,
            light_mappings=mappings,
            update_rate=BindableFloat.from_raw(data.get("update_rate"), default=2.0),
            transition=BindableFloat.from_raw(data.get("transition"), default=0.5),
            min_brightness_threshold=BindableFloat.from_raw(
                data.get("min_brightness_threshold"), default=0.0
            ),
            color_tolerance=BindableFloat.from_raw(data.get("color_tolerance"), default=5.0),
            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())
            ),
        )
