"""Support for Homematic thermostats."""

from __future__ import annotations

from typing import Any

from homeassistant.components.climate import (
    PRESET_BOOST,
    PRESET_COMFORT,
    PRESET_ECO,
    PRESET_NONE,
    ClimateEntity,
    ClimateEntityFeature,
    HVACMode,
)
from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType

from .const import ATTR_DISCOVER_DEVICES, HM_ATTRIBUTE_SUPPORT
from .entity import HMDevice

HM_TEMP_MAP = ["ACTUAL_TEMPERATURE", "TEMPERATURE"]

HM_HUMI_MAP = ["ACTUAL_HUMIDITY", "HUMIDITY"]

HM_PRESET_MAP = {
    "BOOST_MODE": PRESET_BOOST,
    "COMFORT_MODE": PRESET_COMFORT,
    "LOWERING_MODE": PRESET_ECO,
}

HM_CONTROL_MODE = "CONTROL_MODE"
HMIP_CONTROL_MODE = "SET_POINT_MODE"


def setup_platform(
    hass: HomeAssistant,
    config: ConfigType,
    add_entities: AddEntitiesCallback,
    discovery_info: DiscoveryInfoType | None = None,
) -> None:
    """Set up the Homematic thermostat platform."""
    if discovery_info is None:
        return

    devices = []
    for conf in discovery_info[ATTR_DISCOVER_DEVICES]:
        new_device = HMThermostat(conf)
        devices.append(new_device)

    add_entities(devices, True)


class HMThermostat(HMDevice, ClimateEntity):
    """Representation of a Homematic thermostat."""

    _attr_supported_features = (
        ClimateEntityFeature.TARGET_TEMPERATURE
        | ClimateEntityFeature.PRESET_MODE
        | ClimateEntityFeature.TURN_OFF
        | ClimateEntityFeature.TURN_ON
    )
    _attr_temperature_unit = UnitOfTemperature.CELSIUS
    _attr_min_temp = 4.5
    _attr_max_temp = 30.5
    _attr_target_temperature_step = 0.5

    _state: str

    @property
    def hvac_mode(self) -> HVACMode:
        """Return hvac operation ie. heat, cool mode.

        Need to be one of HVAC_MODE_*.
        """
        if self.target_temperature <= self._hmdevice.OFF_VALUE + 0.5:
            return HVACMode.OFF
        if "MANU_MODE" in self._hmdevice.ACTIONNODE:
            if self._hm_control_mode == self._hmdevice.MANU_MODE:
                return HVACMode.HEAT
            return HVACMode.AUTO

        # Simple devices
        if self._data.get("BOOST_MODE"):
            return HVACMode.AUTO
        return HVACMode.HEAT

    @property
    def hvac_modes(self) -> list[HVACMode]:
        """Return the list of available hvac operation modes.

        Need to be a subset of HVAC_MODES.
        """
        if "AUTO_MODE" in self._hmdevice.ACTIONNODE:
            return [HVACMode.AUTO, HVACMode.HEAT, HVACMode.OFF]
        return [HVACMode.HEAT, HVACMode.OFF]

    @property
    def preset_mode(self) -> str:
        """Return the current preset mode, e.g., home, away, temp."""
        if self._data.get("BOOST_MODE", False):
            return "boost"

        if not self._hm_control_mode:
            return PRESET_NONE

        mode = HM_ATTRIBUTE_SUPPORT[HM_CONTROL_MODE][1][self._hm_control_mode]
        mode = mode.lower()

        # Filter HVAC states
        if mode not in (HVACMode.AUTO, HVACMode.HEAT):
            return PRESET_NONE
        return mode

    @property
    def preset_modes(self) -> list[str]:
        """Return a list of available preset modes."""
        return [
            HM_PRESET_MAP[mode]
            for mode in self._hmdevice.ACTIONNODE
            if mode in HM_PRESET_MAP
        ]

    @property
    def current_humidity(self) -> float | None:
        """Return the current humidity."""
        for node in HM_HUMI_MAP:
            if node in self._data:
                return self._data[node]
        return None

    @property
    def current_temperature(self) -> float | None:
        """Return the current temperature."""
        for node in HM_TEMP_MAP:
            if node in self._data:
                return self._data[node]
        return None

    @property
    def target_temperature(self) -> float | None:
        """Return the target temperature."""
        return self._data.get(self._state)

    def set_temperature(self, **kwargs: Any) -> None:
        """Set new target temperature."""
        if (temperature := kwargs.get(ATTR_TEMPERATURE)) is None:
            return

        self._hmdevice.writeNodeData(self._state, float(temperature))

    def set_hvac_mode(self, hvac_mode: HVACMode) -> None:
        """Set new target hvac mode."""
        if hvac_mode == HVACMode.AUTO:
            self._hmdevice.MODE = self._hmdevice.AUTO_MODE
        elif hvac_mode == HVACMode.HEAT:
            self._hmdevice.MODE = self._hmdevice.MANU_MODE
        elif hvac_mode == HVACMode.OFF:
            self._hmdevice.turnoff()

    def set_preset_mode(self, preset_mode: str) -> None:
        """Set new preset mode."""
        if preset_mode == PRESET_BOOST:
            self._hmdevice.MODE = self._hmdevice.BOOST_MODE
        elif preset_mode == PRESET_COMFORT:
            self._hmdevice.MODE = self._hmdevice.COMFORT_MODE
        elif preset_mode == PRESET_ECO:
            self._hmdevice.MODE = self._hmdevice.LOWERING_MODE

    @property
    def _hm_control_mode(self):
        """Return Control mode."""
        if HMIP_CONTROL_MODE in self._data:
            return self._data[HMIP_CONTROL_MODE]

        # Homematic
        return self._data.get("CONTROL_MODE")

    def _init_data_struct(self) -> None:
        """Generate a data dict (self._data) from the Homematic metadata."""
        self._state = next(iter(self._hmdevice.WRITENODE.keys()))
        self._data[self._state] = None

        if (
            HM_CONTROL_MODE in self._hmdevice.ATTRIBUTENODE
            or HMIP_CONTROL_MODE in self._hmdevice.ATTRIBUTENODE
        ):
            self._data[HM_CONTROL_MODE] = None

        for node in self._hmdevice.SENSORNODE:
            self._data[node] = None
