from hueify.cache.lookup import EntityLookupCache
from hueify.exceptions import ResourceNotFoundException
from hueify.grouped_lights.views import GroupedLightInfo, GroupInfo
from hueify.http import HttpClient
from hueify.scenes.cache import SceneCache
from hueify.scenes.schemas import SceneInfo
from hueify.scenes.service import Scene
from hueify.shared.decorators import timed
from hueify.shared.fuzzy import find_all_matches_sorted
from hueify.shared.resource import Resource
from hueify.shared.resource.views import ActionResult


class GroupedLights(Resource[GroupedLightInfo]):
    """Represents a Hue grouped-light resource (room or zone).

    Inherits all brightness and colour-temperature control from
    :class:`~hueify.shared.resource.Resource` and adds scene management.

    Obtain an instance from a namespace rather than constructing directly:

    ```python
    room = hue.rooms.from_name("Living Room")
    await room.activate_scene("Relax")
    ```
    """

    def __init__(
        self,
        light_info: GroupedLightInfo,
        client: HttpClient,
        group_info: GroupInfo | None = None,
        scene_cache: SceneCache | None = None,
        cache: EntityLookupCache[GroupedLightInfo] | None = None,
    ) -> None:
        super().__init__(light_info=light_info, client=client, cache=cache)
        self._group_info = group_info
        self._scene_cache = scene_cache

    @property
    def name(self) -> str:
        """Display name of the group (room or zone name when available)."""
        if self._group_info is not None:
            return self._group_info.name
        return self._light_info.name

    def _get_resource_endpoint(self) -> str:
        return "/grouped_light"

    @property
    def scene_names(self) -> list[str]:
        """Names of all scenes assigned to this group."""
        return [s.name for s in self._list_scenes()]

    def list_scenes(self) -> list[SceneInfo]:
        """Return full :class:`~hueify.scenes.SceneInfo` objects for this group."""
        return self._list_scenes()

    def get_active_scene(self) -> SceneInfo | None:
        """Return the currently active scene, or ``None`` if no scene is active."""
        return next((s for s in self._list_scenes() if s.is_active), None)

    @timed()
    async def activate_scene(self, scene_name: str) -> ActionResult:
        """Activate a scene by name.

        Matching is case-insensitive. If the exact name is not found,
        a fuzzy suggestion is included in the raised exception.

        Args:
            scene_name: Name of the scene to activate.

        Raises:
            :class:`~hueify.exceptions.ResourceNotFoundException`: When the
                scene is not found for this group.
        """
        scenes = self._list_scenes()
        scene_info = self._resolve_scene(scene_name, scenes)
        scene = Scene(scene_info=scene_info, client=self._client)
        return await scene.activate()

    def _list_scenes(self) -> list[SceneInfo]:
        if self._scene_cache is None or self._group_info is None:
            raise ValueError(
                f"No scene cache or group info available for '{self.name}'"
            )
        return [
            s for s in self._scene_cache.get_all() if s.group_id == self._group_info.id
        ]

    def _resolve_scene(self, scene_name: str, scenes: list[SceneInfo]) -> SceneInfo:
        for scene in scenes:
            if scene.name.lower() == scene_name.lower():
                return scene

        matching_scenes = find_all_matches_sorted(
            query=scene_name,
            items=scenes,
            text_extractor=lambda s: s.name,
        )
        raise ResourceNotFoundException(
            resource_type="scene",
            lookup_name=scene_name,
            suggested_names=[s.name for s in matching_scenes],
        )
