"""The Homee climate platform."""

from typing import Any

from pyHomee.const import AttributeType, NodeProfile
from pyHomee.model import HomeeNode

from homeassistant.components.climate import (
    ATTR_TEMPERATURE,
    PRESET_BOOST,
    PRESET_ECO,
    PRESET_NONE,
    ClimateEntity,
    ClimateEntityFeature,
    HVACAction,
    HVACMode,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from . import HomeeConfigEntry
from .const import CLIMATE_PROFILES, DOMAIN, HOMEE_UNIT_TO_HA_UNIT, PRESET_MANUAL
from .entity import HomeeNodeEntity
from .helpers import setup_homee_platform

PARALLEL_UPDATES = 0

ROOM_THERMOSTATS = {
    NodeProfile.ROOM_THERMOSTAT,
    NodeProfile.ROOM_THERMOSTAT_WITH_HUMIDITY_SENSOR,
    NodeProfile.WIFI_ROOM_THERMOSTAT,
}


async def add_climate_entities(
    config_entry: HomeeConfigEntry,
    async_add_entities: AddConfigEntryEntitiesCallback,
    nodes: list[HomeeNode],
) -> None:
    """Add homee climate entities."""
    async_add_entities(
        HomeeClimate(node, config_entry)
        for node in nodes
        if node.profile in CLIMATE_PROFILES
    )


async def async_setup_entry(
    hass: HomeAssistant,
    config_entry: HomeeConfigEntry,
    async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
    """Add the Homee platform for the climate component."""

    await setup_homee_platform(add_climate_entities, async_add_entities, config_entry)


class HomeeClimate(HomeeNodeEntity, ClimateEntity):
    """Representation of a Homee climate entity."""

    _attr_name = None
    _attr_translation_key = DOMAIN

    def __init__(self, node: HomeeNode, entry: HomeeConfigEntry) -> None:
        """Initialize a Homee climate entity."""
        super().__init__(node, entry)

        (
            self._attr_supported_features,
            self._attr_hvac_modes,
            self._attr_preset_modes,
        ) = get_climate_features(self._node)

        self._target_temp = self._node.get_attribute_by_type(
            AttributeType.TARGET_TEMPERATURE
        )
        assert self._target_temp is not None
        self._attr_temperature_unit = str(HOMEE_UNIT_TO_HA_UNIT[self._target_temp.unit])
        self._attr_target_temperature_step = self._target_temp.step_value
        self._attr_unique_id = f"{self._attr_unique_id}-{self._target_temp.id}"

        self._heating_mode = self._node.get_attribute_by_type(
            AttributeType.HEATING_MODE
        )
        self._temperature = self._node.get_attribute_by_type(AttributeType.TEMPERATURE)
        self._valve_position = self._node.get_attribute_by_type(
            AttributeType.CURRENT_VALVE_POSITION
        )

    @property
    def hvac_mode(self) -> HVACMode:
        """Return the hvac operation mode."""
        if ClimateEntityFeature.TURN_OFF in self.supported_features and (
            self._heating_mode is not None
        ):
            if self._heating_mode.current_value == self._heating_mode.minimum:
                return HVACMode.OFF

        return HVACMode.HEAT

    @property
    def hvac_action(self) -> HVACAction:
        """Return the hvac action."""
        if (
            self._heating_mode is not None
            and self._heating_mode.current_value == self._heating_mode.minimum
        ):
            return HVACAction.OFF

        if (
            self._valve_position is not None and self._valve_position.current_value == 0
        ) or (
            self._temperature is not None
            and self._temperature.current_value >= self.target_temperature
        ):
            return HVACAction.IDLE

        return HVACAction.HEATING

    @property
    def preset_mode(self) -> str:
        """Return the present preset mode."""
        if (
            ClimateEntityFeature.PRESET_MODE in self.supported_features
            and self._heating_mode is not None
            and self._heating_mode.current_value > self._heating_mode.minimum
        ):
            assert self._attr_preset_modes is not None
            return self._attr_preset_modes[
                int(self._heating_mode.current_value - self._heating_mode.minimum) - 1
            ]

        return PRESET_NONE

    @property
    def current_temperature(self) -> float | None:
        """Return the current temperature."""
        if self._temperature is not None:
            return self._temperature.current_value
        return None

    @property
    def target_temperature(self) -> float:
        """Return the temperature we try to reach."""
        assert self._target_temp is not None
        return self._target_temp.current_value

    @property
    def min_temp(self) -> float:
        """Return the lowest settable target temperature."""
        assert self._target_temp is not None
        return self._target_temp.minimum

    @property
    def max_temp(self) -> float:
        """Return the lowest settable target temperature."""
        assert self._target_temp is not None
        return self._target_temp.maximum

    async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
        """Set new target hvac mode."""
        # Currently only HEAT and OFF are supported.
        assert self._heating_mode is not None
        await self.async_set_homee_value(
            self._heating_mode,
            (hvac_mode == HVACMode.HEAT) + self._heating_mode.minimum,
        )

    async def async_set_preset_mode(self, preset_mode: str) -> None:
        """Set new target preset mode."""
        assert self._heating_mode is not None and self._attr_preset_modes is not None
        await self.async_set_homee_value(
            self._heating_mode,
            self._attr_preset_modes.index(preset_mode) + self._heating_mode.minimum + 1,
        )

    async def async_set_temperature(self, **kwargs: Any) -> None:
        """Set new target temperature."""
        assert self._target_temp is not None
        if ATTR_TEMPERATURE in kwargs:
            await self.async_set_homee_value(
                self._target_temp, kwargs[ATTR_TEMPERATURE]
            )

    async def async_turn_on(self) -> None:
        """Turn the entity on."""
        assert self._heating_mode is not None
        await self.async_set_homee_value(
            self._heating_mode, 1 + self._heating_mode.minimum
        )

    async def async_turn_off(self) -> None:
        """Turn the entity on."""
        assert self._heating_mode is not None
        await self.async_set_homee_value(
            self._heating_mode, 0 + self._heating_mode.minimum
        )


def get_climate_features(
    node: HomeeNode,
) -> tuple[ClimateEntityFeature, list[HVACMode], list[str] | None]:
    """Determine supported climate features of a node based on the available attributes."""
    features = ClimateEntityFeature.TARGET_TEMPERATURE
    hvac_modes = [HVACMode.HEAT]
    preset_modes: list[str] = []

    if (
        attribute := node.get_attribute_by_type(AttributeType.HEATING_MODE)
    ) is not None:
        features |= ClimateEntityFeature.TURN_ON | ClimateEntityFeature.TURN_OFF
        hvac_modes.append(HVACMode.OFF)

        if attribute.maximum > 1:
            # Node supports more modes than off and heating.
            features |= ClimateEntityFeature.PRESET_MODE
            if attribute.maximum < 5:
                preset_modes.extend([PRESET_ECO, PRESET_BOOST, PRESET_MANUAL])
            else:
                preset_modes.extend([PRESET_ECO])

    if len(preset_modes) > 0:
        preset_modes.insert(0, PRESET_NONE)
    return (features, hvac_modes, preset_modes if len(preset_modes) > 0 else None)
