"""Matter valve platform."""

from __future__ import annotations

from dataclasses import dataclass

from chip.clusters import Objects as clusters
from matter_server.client.models import device_types

from homeassistant.components.valve import (
    ValveDeviceClass,
    ValveEntity,
    ValveEntityDescription,
    ValveEntityFeature,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

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

ValveConfigurationAndControl = clusters.ValveConfigurationAndControl
ValveStateEnum = ValveConfigurationAndControl.Enums.ValveStateEnum


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


@dataclass(frozen=True, kw_only=True)
class MatterValveEntityDescription(ValveEntityDescription, MatterEntityDescription):
    """Describe Matter Valve entities."""


class MatterValve(MatterEntity, ValveEntity):
    """Representation of a Matter Valve."""

    _feature_map: int | None = None
    entity_description: MatterValveEntityDescription
    _platform_translation_key = "valve"

    async def async_open_valve(self) -> None:
        """Open the valve."""
        await self.send_device_command(ValveConfigurationAndControl.Commands.Open())

    async def async_close_valve(self) -> None:
        """Close the valve."""
        await self.send_device_command(ValveConfigurationAndControl.Commands.Close())

    async def async_set_valve_position(self, position: int) -> None:
        """Move the valve to a specific position."""
        if position > 0:
            await self.send_device_command(
                ValveConfigurationAndControl.Commands.Open(targetLevel=position)
            )
            return
        await self.send_device_command(ValveConfigurationAndControl.Commands.Close())

    @callback
    def _update_from_device(self) -> None:
        """Update from device."""
        self._calculate_features()
        current_state: int
        current_state = self.get_matter_attribute_value(
            ValveConfigurationAndControl.Attributes.CurrentState
        )
        target_state: int
        target_state = self.get_matter_attribute_value(
            ValveConfigurationAndControl.Attributes.TargetState
        )
        if (
            current_state == ValveStateEnum.kTransitioning
            and target_state == ValveStateEnum.kOpen
        ):
            self._attr_is_opening = True
            self._attr_is_closing = False
        elif (
            current_state == ValveStateEnum.kTransitioning
            and target_state == ValveStateEnum.kClosed
        ):
            self._attr_is_opening = False
            self._attr_is_closing = True
        elif current_state == ValveStateEnum.kClosed:
            self._attr_is_opening = False
            self._attr_is_closing = False
            self._attr_is_closed = True
        else:
            self._attr_is_opening = False
            self._attr_is_closing = False
            self._attr_is_closed = False
        # handle optional position
        if self.supported_features & ValveEntityFeature.SET_POSITION:
            self._attr_current_valve_position = self.get_matter_attribute_value(
                ValveConfigurationAndControl.Attributes.CurrentLevel
            )

    @callback
    def _calculate_features(
        self,
    ) -> None:
        """Calculate features for HA Valve platform from Matter FeatureMap."""
        feature_map = int(
            self.get_matter_attribute_value(
                ValveConfigurationAndControl.Attributes.FeatureMap
            )
        )
        # NOTE: the featuremap can dynamically change, so we need to update the
        # supported features if the featuremap changes.
        # work out supported features and presets from matter featuremap
        if self._feature_map == feature_map:
            return
        self._feature_map = feature_map
        self._attr_supported_features = ValveEntityFeature(0)
        if feature_map & ValveConfigurationAndControl.Bitmaps.Feature.kLevel:
            self._attr_supported_features |= ValveEntityFeature.SET_POSITION
            self._attr_reports_position = True
        else:
            self._attr_reports_position = False

        self._attr_supported_features |= (
            ValveEntityFeature.CLOSE | ValveEntityFeature.OPEN
        )


# Discovery schema(s) to map Matter Attributes to HA entities
DISCOVERY_SCHEMAS = [
    MatterDiscoverySchema(
        platform=Platform.VALVE,
        entity_description=MatterValveEntityDescription(
            key="MatterValve",
            device_class=ValveDeviceClass.WATER,
            name=None,
        ),
        entity_class=MatterValve,
        required_attributes=(
            ValveConfigurationAndControl.Attributes.CurrentState,
            ValveConfigurationAndControl.Attributes.TargetState,
        ),
        optional_attributes=(ValveConfigurationAndControl.Attributes.CurrentLevel,),
        device_type=(device_types.WaterValve,),
    ),
]
