"""Home Assistant runtime manager — ref-counted pool of HA WebSocket connections.

Follows the WeatherManager pattern: multiple consumers (CSS streams, automation
conditions) sharing the same WebSocket connection per HA instance.
"""

import asyncio
from typing import Any, Dict, List, Optional

from wled_controller.core.home_assistant.ha_runtime import HAEntityState, HARuntime
from wled_controller.storage.home_assistant_store import HomeAssistantStore
from wled_controller.utils import get_logger

logger = get_logger(__name__)


class HomeAssistantManager:
    """Ref-counted pool of Home Assistant runtimes.

    Each HA source gets at most one runtime (WebSocket connection).
    Multiple consumers share the same runtime via acquire/release.
    """

    def __init__(self, store: HomeAssistantStore) -> None:
        self._store = store
        # source_id -> (runtime, ref_count)
        self._runtimes: Dict[str, tuple] = {}
        self._lock = asyncio.Lock()

    async def acquire(self, source_id: str) -> HARuntime:
        """Get or create a runtime for the given HA source. Increments ref count."""
        async with self._lock:
            if source_id in self._runtimes:
                runtime, count = self._runtimes[source_id]
                self._runtimes[source_id] = (runtime, count + 1)
                return runtime

            source = self._store.get(source_id)
            runtime = HARuntime(source)
            await runtime.start()
            self._runtimes[source_id] = (runtime, 1)
            return runtime

    async def release(self, source_id: str) -> None:
        """Decrement ref count; stop runtime when it reaches zero."""
        async with self._lock:
            if source_id not in self._runtimes:
                return
            runtime, count = self._runtimes[source_id]
            if count <= 1:
                await runtime.stop()
                del self._runtimes[source_id]
            else:
                self._runtimes[source_id] = (runtime, count - 1)

    def get_state(self, source_id: str, entity_id: str) -> Optional[HAEntityState]:
        """Get cached entity state from a running runtime (synchronous)."""
        entry = self._runtimes.get(source_id)
        if entry is None:
            return None
        runtime, _count = entry
        return runtime.get_state(entity_id)

    def get_runtime(self, source_id: str) -> Optional[HARuntime]:
        """Get a running runtime without changing ref count (for read-only access)."""
        entry = self._runtimes.get(source_id)
        if entry is None:
            return None
        runtime, _count = entry
        return runtime

    async def ensure_runtime(self, source_id: str) -> HARuntime:
        """Get or create a runtime (for API endpoints that need a connection)."""
        async with self._lock:
            if source_id in self._runtimes:
                runtime, count = self._runtimes[source_id]
                return runtime

        source = self._store.get(source_id)
        runtime = HARuntime(source)
        await runtime.start()
        async with self._lock:
            if source_id not in self._runtimes:
                self._runtimes[source_id] = (runtime, 0)
            else:
                await runtime.stop()
                runtime, _count = self._runtimes[source_id]
        return runtime

    async def call_service(
        self, source_id: str, domain: str, service: str, service_data: dict, target: dict
    ) -> bool:
        """Call a HA service via the runtime for the given source. Returns success."""
        entry = self._runtimes.get(source_id)
        if entry is None:
            return False
        runtime, _count = entry
        return await runtime.call_service(domain, service, service_data, target)

    async def update_source(self, source_id: str) -> None:
        """Hot-update runtime config when the source is edited."""
        entry = self._runtimes.get(source_id)
        if entry is None:
            return
        runtime, _count = entry
        try:
            source = self._store.get(source_id)
            runtime.update_config(source)
        except Exception as e:
            logger.warning(f"Failed to update HA runtime {source_id}: {e}")

    def get_connection_status(self) -> List[Dict[str, Any]]:
        """Get status of all active HA connections (for dashboard indicators)."""
        result = []
        for source_id, (runtime, ref_count) in self._runtimes.items():
            try:
                source = self._store.get(source_id)
                name = source.name
            except Exception:
                name = source_id
            result.append(
                {
                    "source_id": source_id,
                    "name": name,
                    "connected": runtime.is_connected,
                    "ref_count": ref_count,
                    "entity_count": len(runtime.get_all_states()),
                }
            )
        return result

    async def shutdown(self) -> None:
        """Stop all runtimes."""
        async with self._lock:
            for source_id, (runtime, _count) in list(self._runtimes.items()):
                await runtime.stop()
            self._runtimes.clear()
        logger.info("Home Assistant manager shut down")
