"""Matter ModeSelect Cluster Support."""

from __future__ import annotations

from dataclasses import dataclass
from typing import TYPE_CHECKING

from chip.clusters import Objects as clusters
from chip.clusters.Types import Nullable
from matter_server.common.helpers.util import create_attribute_path_from_attribute

from homeassistant.components.select import SelectEntity, SelectEntityDescription
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory, Platform
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from .entity import MatterEntity, MatterEntityDescription
from .helpers import get_matter
from .models import MatterDiscoverySchema

type SelectCluster = (
    clusters.ModeSelect
    | clusters.OvenMode
    | clusters.LaundryWasherMode
    | clusters.RefrigeratorAndTemperatureControlledCabinetMode
    | clusters.RvcRunMode
    | clusters.RvcCleanMode
    | clusters.DishwasherMode
    | clusters.EnergyEvseMode
    | clusters.DeviceEnergyManagementMode
)


async def async_setup_entry(
    hass: HomeAssistant,
    config_entry: ConfigEntry,
    async_add_entities: AddEntitiesCallback,
) -> None:
    """Set up Matter ModeSelect from Config Entry."""
    matter = get_matter(hass)
    matter.register_platform_handler(Platform.SELECT, async_add_entities)


@dataclass(frozen=True)
class MatterSelectEntityDescription(SelectEntityDescription, MatterEntityDescription):
    """Describe Matter select entities."""


class MatterSelectEntity(MatterEntity, SelectEntity):
    """Representation of a select entity from Matter Attribute read/write."""

    entity_description: MatterSelectEntityDescription

    async def async_select_option(self, option: str) -> None:
        """Change the selected mode."""
        value_convert = self.entity_description.ha_to_native_value
        if TYPE_CHECKING:
            assert value_convert is not None
        await self.matter_client.write_attribute(
            node_id=self._endpoint.node.node_id,
            attribute_path=create_attribute_path_from_attribute(
                self._endpoint.endpoint_id, self._entity_info.primary_attribute
            ),
            value=value_convert(option),
        )

    @callback
    def _update_from_device(self) -> None:
        """Update from device."""
        value: Nullable | int | None
        value = self.get_matter_attribute_value(self._entity_info.primary_attribute)
        value_convert = self.entity_description.measurement_to_ha
        if TYPE_CHECKING:
            assert value_convert is not None
        self._attr_current_option = value_convert(value)


class MatterModeSelectEntity(MatterSelectEntity):
    """Representation of a select entity from Matter (Mode) Cluster attribute(s)."""

    async def async_select_option(self, option: str) -> None:
        """Change the selected mode."""
        cluster: SelectCluster = self._endpoint.get_cluster(
            self._entity_info.primary_attribute.cluster_id
        )
        # select the mode ID from the label string
        for mode in cluster.supportedModes:
            if mode.label != option:
                continue
            await self.matter_client.send_device_command(
                node_id=self._endpoint.node.node_id,
                endpoint_id=self._endpoint.endpoint_id,
                command=cluster.Commands.ChangeToMode(newMode=mode.mode),
            )
            break

    @callback
    def _update_from_device(self) -> None:
        """Update from device."""
        # NOTE: cluster can be ModeSelect or a variant of that,
        # such as DishwasherMode. They all have the same characteristics.
        cluster: SelectCluster = self._endpoint.get_cluster(
            self._entity_info.primary_attribute.cluster_id
        )
        modes = {mode.mode: mode.label for mode in cluster.supportedModes}
        self._attr_options = list(modes.values())
        self._attr_current_option = modes[cluster.currentMode]
        # handle optional Description attribute as descriptive name for the mode
        if desc := getattr(cluster, "description", None):
            self._attr_name = desc


# Discovery schema(s) to map Matter Attributes to HA entities
DISCOVERY_SCHEMAS = [
    MatterDiscoverySchema(
        platform=Platform.SELECT,
        entity_description=MatterSelectEntityDescription(
            key="MatterModeSelect",
            entity_category=EntityCategory.CONFIG,
            translation_key="mode",
        ),
        entity_class=MatterModeSelectEntity,
        required_attributes=(
            clusters.ModeSelect.Attributes.CurrentMode,
            clusters.ModeSelect.Attributes.SupportedModes,
        ),
    ),
    MatterDiscoverySchema(
        platform=Platform.SELECT,
        entity_description=MatterSelectEntityDescription(
            key="MatterOvenMode",
            translation_key="mode",
        ),
        entity_class=MatterModeSelectEntity,
        required_attributes=(
            clusters.OvenMode.Attributes.CurrentMode,
            clusters.OvenMode.Attributes.SupportedModes,
        ),
    ),
    MatterDiscoverySchema(
        platform=Platform.SELECT,
        entity_description=MatterSelectEntityDescription(
            key="MatterLaundryWasherMode",
            translation_key="mode",
        ),
        entity_class=MatterModeSelectEntity,
        required_attributes=(
            clusters.LaundryWasherMode.Attributes.CurrentMode,
            clusters.LaundryWasherMode.Attributes.SupportedModes,
        ),
    ),
    MatterDiscoverySchema(
        platform=Platform.SELECT,
        entity_description=MatterSelectEntityDescription(
            key="MatterRefrigeratorAndTemperatureControlledCabinetMode",
            translation_key="mode",
        ),
        entity_class=MatterModeSelectEntity,
        required_attributes=(
            clusters.RefrigeratorAndTemperatureControlledCabinetMode.Attributes.CurrentMode,
            clusters.RefrigeratorAndTemperatureControlledCabinetMode.Attributes.SupportedModes,
        ),
    ),
    MatterDiscoverySchema(
        platform=Platform.SELECT,
        entity_description=MatterSelectEntityDescription(
            key="MatterRvcRunMode",
            translation_key="mode",
        ),
        entity_class=MatterModeSelectEntity,
        required_attributes=(
            clusters.RvcRunMode.Attributes.CurrentMode,
            clusters.RvcRunMode.Attributes.SupportedModes,
        ),
    ),
    MatterDiscoverySchema(
        platform=Platform.SELECT,
        entity_description=MatterSelectEntityDescription(
            key="MatterRvcCleanMode",
            translation_key="mode",
        ),
        entity_class=MatterModeSelectEntity,
        required_attributes=(
            clusters.RvcCleanMode.Attributes.CurrentMode,
            clusters.RvcCleanMode.Attributes.SupportedModes,
        ),
    ),
    MatterDiscoverySchema(
        platform=Platform.SELECT,
        entity_description=MatterSelectEntityDescription(
            key="MatterDishwasherMode",
            translation_key="mode",
        ),
        entity_class=MatterModeSelectEntity,
        required_attributes=(
            clusters.DishwasherMode.Attributes.CurrentMode,
            clusters.DishwasherMode.Attributes.SupportedModes,
        ),
    ),
    MatterDiscoverySchema(
        platform=Platform.SELECT,
        entity_description=MatterSelectEntityDescription(
            key="MatterEnergyEvseMode",
            translation_key="mode",
        ),
        entity_class=MatterModeSelectEntity,
        required_attributes=(
            clusters.EnergyEvseMode.Attributes.CurrentMode,
            clusters.EnergyEvseMode.Attributes.SupportedModes,
        ),
    ),
    MatterDiscoverySchema(
        platform=Platform.SELECT,
        entity_description=MatterSelectEntityDescription(
            key="MatterDeviceEnergyManagementMode",
            translation_key="mode",
        ),
        entity_class=MatterModeSelectEntity,
        required_attributes=(
            clusters.DeviceEnergyManagementMode.Attributes.CurrentMode,
            clusters.DeviceEnergyManagementMode.Attributes.SupportedModes,
        ),
    ),
    MatterDiscoverySchema(
        platform=Platform.SELECT,
        entity_description=MatterSelectEntityDescription(
            key="MatterStartUpOnOff",
            entity_category=EntityCategory.CONFIG,
            translation_key="startup_on_off",
            options=["On", "Off", "Toggle", "Previous"],
            measurement_to_ha=lambda x: {  # pylint: disable=unnecessary-lambda
                0: "Off",
                1: "On",
                2: "Toggle",
                None: "Previous",
            }.get(x),
            ha_to_native_value=lambda x: {
                "Off": 0,
                "On": 1,
                "Toggle": 2,
                "Previous": None,
            }[x],
        ),
        entity_class=MatterSelectEntity,
        required_attributes=(clusters.OnOff.Attributes.StartUpOnOff,),
    ),
]
