"""Home Assistant source data model.

A HomeAssistantSource represents a connection to a Home Assistant instance.
It stores the host, access token, and optional entity filters.
Referenced by HomeAssistantColorStripSource and HomeAssistantCondition.
"""

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

from ledgrab.utils import secret_box


def _parse_common(data: dict) -> dict:
    """Extract common fields from a dict, parsing timestamps."""
    created = data.get("created_at", "")
    updated = data.get("updated_at", "")
    return {
        "id": data["id"],
        "name": data["name"],
        "created_at": (
            datetime.fromisoformat(created)
            if isinstance(created, str) and created
            else datetime.now(timezone.utc)
        ),
        "updated_at": (
            datetime.fromisoformat(updated)
            if isinstance(updated, str) and updated
            else datetime.now(timezone.utc)
        ),
        "description": data.get("description"),
        "tags": data.get("tags") or [],
    }


@dataclass
class HomeAssistantSource:
    """Home Assistant connection configuration."""

    id: str
    name: str
    created_at: datetime
    updated_at: datetime
    host: str = ""  # e.g. "192.168.1.100:8123"
    token: str = ""  # Long-Lived Access Token
    use_ssl: bool = False
    entity_filters: List[str] = field(
        default_factory=list
    )  # optional allowlist (e.g. ["sensor.*"])
    description: Optional[str] = None
    tags: List[str] = field(default_factory=list)
    icon: str = ""
    icon_color: str = ""

    @property
    def ws_url(self) -> str:
        """Build WebSocket URL from host and SSL setting."""
        scheme = "wss" if self.use_ssl else "ws"
        return f"{scheme}://{self.host}/api/websocket"

    @property
    def http_url(self) -> str:
        """Build HTTP URL from host and SSL setting."""
        scheme = "https" if self.use_ssl else "http"
        return f"{scheme}://{self.host}"

    @property
    def plaintext_token(self) -> str:
        """Return the plaintext access token (decrypts the stored envelope)."""
        return secret_box.decrypt(self.token) if self.token else ""

    def to_dict(self) -> dict:
        # Always persist the token in encrypted envelope form. If the field
        # already contains an envelope, encrypt() is a no-op.
        stored_token = secret_box.encrypt(self.token) if self.token else ""
        d = {
            "id": self.id,
            "name": self.name,
            "host": self.host,
            "token": stored_token,
            "use_ssl": self.use_ssl,
            "entity_filters": list(self.entity_filters),
            "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

    @staticmethod
    def from_dict(data: dict) -> "HomeAssistantSource":
        common = _parse_common(data)
        raw_token = data.get("token", "")
        # Decrypt at load time so consumers see plaintext via .token.
        # Plaintext (legacy) input passes through unchanged — migration
        # in HomeAssistantStore re-saves it in encrypted form.
        token = secret_box.decrypt(raw_token) if raw_token else ""
        return HomeAssistantSource(
            **common,
            host=data.get("host", ""),
            token=token,
            use_ssl=data.get("use_ssl", False),
            entity_filters=data.get("entity_filters") or [],
            icon=data.get("icon", ""),
            icon_color=data.get("icon_color", ""),
        )
