"""Output target base data model."""

from dataclasses import dataclass, field
from datetime import datetime
from typing import ClassVar, Dict, List, Optional, Type


@dataclass
class OutputTarget:
    """Base class for output targets.

    Subclasses register themselves into ``_registry`` via the
    ``type_key="..."`` class-header argument; :meth:`from_dict`
    uses that registry to dispatch deserialization without a
    per-type if/elif chain. The :attr:`target_type` instance
    field stays as the persisted discriminator.
    """

    # Discriminator-keyed registry of concrete subclasses. Populated
    # automatically via ``__init_subclass__``. Requires the subclass
    # modules to be imported at least once before ``from_dict`` runs;
    # ``output_target_store`` imports all three at module load, which
    # is sufficient for the live app and tests.
    _registry: ClassVar[Dict[str, Type["OutputTarget"]]] = {}
    _type_key: ClassVar[str] = ""

    id: str
    name: str
    target_type: str  # "led", "ha_light", "z2m_light"
    created_at: datetime
    updated_at: datetime
    description: Optional[str] = None
    tags: List[str] = field(default_factory=list)
    # Custom card icon (frontend display only). When empty, the LED target
    # card inherits the icon from its referenced device; HA-light targets
    # have no inheritance source and just show the empty plate placeholder.
    icon: str = ""
    icon_color: str = ""

    def __init_subclass__(cls, *, type_key: str = "", **kwargs) -> None:
        super().__init_subclass__(**kwargs)
        if type_key:
            cls._type_key = type_key
            OutputTarget._registry[type_key] = cls

    def register_with_manager(self, manager) -> None:
        """Register this target with the processor manager. Subclasses override."""
        pass

    def sync_with_manager(
        self, manager, *, settings_changed: bool, source_changed: bool, device_changed: bool
    ) -> None:
        """Push changed fields to a running processor. Subclasses override."""
        pass

    def update_fields(
        self,
        *,
        name=None,
        device_id=None,
        description=None,
        tags: Optional[List[str]] = None,
        icon: Optional[str] = None,
        icon_color: Optional[str] = None,
        **_kwargs,
    ) -> None:
        """Apply mutable field updates. Base handles common fields; subclasses handle type-specific ones."""
        if name is not None:
            self.name = name
        if description is not None:
            self.description = description
        if tags is not None:
            self.tags = tags
        if icon is not None:
            self.icon = icon
        if icon_color is not None:
            self.icon_color = icon_color

    def to_dict(self) -> dict:
        """Convert to dictionary."""
        d = {
            "id": self.id,
            "name": self.name,
            "target_type": self.target_type,
            "description": self.description,
            "tags": self.tags,
            "created_at": self.created_at.isoformat(),
            "updated_at": self.updated_at.isoformat(),
        }
        if self.icon:
            d["icon"] = self.icon
        if self.icon_color:
            d["icon_color"] = self.icon_color
        return d

    @classmethod
    def from_dict(cls, data: dict) -> "OutputTarget":
        """Create from dictionary, dispatching to the correct subclass via the registry."""
        target_type = data.get("target_type", "led")
        # When called on a concrete subclass (e.g. ``WledOutputTarget.from_dict``),
        # respect that — subclasses override this method to actually build themselves.
        if cls is not OutputTarget:
            raise NotImplementedError(
                f"{cls.__name__}.from_dict must be implemented on the subclass"
            )
        impl = OutputTarget._registry.get(target_type)
        if impl is None:
            raise ValueError(f"Unknown target type: {target_type}")
        return impl.from_dict(data)
