"""Matter valve platform."""

from dataclasses import dataclass
from typing import override

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.const import Platform
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

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

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


async def async_setup_entry(
    hass: HomeAssistant,
    config_entry: MatterConfigEntry,
    async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
    """Set up Matter valve platform from Config Entry."""
    matter = config_entry.runtime_data.adapter
    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"

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

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

    @override
    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
    @override
    def _update_from_device(self) -> None:
        """Update from device."""
        self._calculate_features()
        self._attr_is_opening = False
        self._attr_is_closing = False

        current_state: int | None
        current_state = self.get_matter_attribute_value(
            ValveConfigurationAndControl.Attributes.CurrentState
        )
        target_state: int | None
        target_state = self.get_matter_attribute_value(
            ValveConfigurationAndControl.Attributes.TargetState
        )

        if current_state is None:
            self._attr_is_closed = None
        elif current_state == ValveStateEnum.kTransitioning and (
            target_state == ValveStateEnum.kOpen
        ):
            self._attr_is_opening = True
            self._attr_is_closed = None
        elif current_state == ValveStateEnum.kTransitioning and (
            target_state == ValveStateEnum.kClosed
        ):
            self._attr_is_closing = True
            self._attr_is_closed = None
        elif current_state == ValveStateEnum.kClosed:
            self._attr_is_closed = True
        elif current_state == ValveStateEnum.kOpen:
            self._attr_is_closed = False
        else:
            self._attr_is_closed = None

        # 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,
        ),
        allow_none_value=True,
        optional_attributes=(ValveConfigurationAndControl.Attributes.CurrentLevel,),
        device_type=(device_types.WaterValve,),
    ),
]
