"""Support for Litter-Robot selects."""

from __future__ import annotations

from collections.abc import Callable, Coroutine
from dataclasses import dataclass
from typing import Any, Generic, TypeVar

from pylitterbot import FeederRobot, LitterRobot, LitterRobot4, Robot
from pylitterbot.robot.litterrobot4 import BrightnessLevel

from homeassistant.components.select import SelectEntity, SelectEntityDescription
from homeassistant.const import EntityCategory, UnitOfTime
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from .coordinator import LitterRobotConfigEntry, LitterRobotDataUpdateCoordinator
from .entity import LitterRobotEntity, _WhiskerEntityT

_CastTypeT = TypeVar("_CastTypeT", int, float, str)


@dataclass(frozen=True, kw_only=True)
class RobotSelectEntityDescription(
    SelectEntityDescription, Generic[_WhiskerEntityT, _CastTypeT]
):
    """A class that describes robot select entities."""

    entity_category: EntityCategory = EntityCategory.CONFIG
    current_fn: Callable[[_WhiskerEntityT], _CastTypeT | None]
    options_fn: Callable[[_WhiskerEntityT], list[_CastTypeT]]
    select_fn: Callable[[_WhiskerEntityT, str], Coroutine[Any, Any, bool]]


ROBOT_SELECT_MAP: dict[type[Robot], RobotSelectEntityDescription] = {
    LitterRobot: RobotSelectEntityDescription[LitterRobot, int](  # type: ignore[type-abstract]  # only used for isinstance check
        key="cycle_delay",
        translation_key="cycle_delay",
        unit_of_measurement=UnitOfTime.MINUTES,
        current_fn=lambda robot: robot.clean_cycle_wait_time_minutes,
        options_fn=lambda robot: robot.VALID_WAIT_TIMES,
        select_fn=lambda robot, opt: robot.set_wait_time(int(opt)),
    ),
    LitterRobot4: RobotSelectEntityDescription[LitterRobot4, str](
        key="panel_brightness",
        translation_key="brightness_level",
        current_fn=(
            lambda robot: bri.name.lower()
            if (bri := robot.panel_brightness) is not None
            else None
        ),
        options_fn=lambda _: [level.name.lower() for level in BrightnessLevel],
        select_fn=(
            lambda robot, opt: robot.set_panel_brightness(BrightnessLevel[opt.upper()])
        ),
    ),
    FeederRobot: RobotSelectEntityDescription[FeederRobot, float](
        key="meal_insert_size",
        translation_key="meal_insert_size",
        unit_of_measurement="cups",
        current_fn=lambda robot: robot.meal_insert_size,
        options_fn=lambda robot: robot.VALID_MEAL_INSERT_SIZES,
        select_fn=lambda robot, opt: robot.set_meal_insert_size(float(opt)),
    ),
}


async def async_setup_entry(
    hass: HomeAssistant,
    entry: LitterRobotConfigEntry,
    async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
    """Set up Litter-Robot selects using config entry."""
    coordinator = entry.runtime_data
    async_add_entities(
        LitterRobotSelectEntity(
            robot=robot, coordinator=coordinator, description=description
        )
        for robot in coordinator.account.robots
        for robot_type, description in ROBOT_SELECT_MAP.items()
        if isinstance(robot, robot_type)
    )


class LitterRobotSelectEntity(
    LitterRobotEntity[_WhiskerEntityT],
    SelectEntity,
    Generic[_WhiskerEntityT, _CastTypeT],
):
    """Litter-Robot Select."""

    entity_description: RobotSelectEntityDescription[_WhiskerEntityT, _CastTypeT]

    def __init__(
        self,
        robot: _WhiskerEntityT,
        coordinator: LitterRobotDataUpdateCoordinator,
        description: RobotSelectEntityDescription[_WhiskerEntityT, _CastTypeT],
    ) -> None:
        """Initialize a Litter-Robot select entity."""
        super().__init__(robot, coordinator, description)
        options = self.entity_description.options_fn(self.robot)
        self._attr_options = list(map(str, options))

    @property
    def current_option(self) -> str | None:
        """Return the selected entity option to represent the entity state."""
        return str(self.entity_description.current_fn(self.robot))

    async def async_select_option(self, option: str) -> None:
        """Change the selected option."""
        await self.entity_description.select_fn(self.robot, option)