"""Support for TP-Link thermostats."""

from __future__ import annotations

import logging
from typing import Any, cast

from kasa import Device, DeviceType
from kasa.smart.modules.temperaturecontrol import ThermostatState

from homeassistant.components.climate import (
    ATTR_TEMPERATURE,
    ClimateEntity,
    ClimateEntityFeature,
    HVACAction,
    HVACMode,
)
from homeassistant.const import PRECISION_WHOLE
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from . import TPLinkConfigEntry
from .const import UNIT_MAPPING
from .coordinator import TPLinkDataUpdateCoordinator
from .entity import CoordinatedTPLinkEntity, async_refresh_after

# Upstream state to HVACAction
STATE_TO_ACTION = {
    ThermostatState.Idle: HVACAction.IDLE,
    ThermostatState.Heating: HVACAction.HEATING,
    ThermostatState.Off: HVACAction.OFF,
}


_LOGGER = logging.getLogger(__name__)


async def async_setup_entry(
    hass: HomeAssistant,
    config_entry: TPLinkConfigEntry,
    async_add_entities: AddEntitiesCallback,
) -> None:
    """Set up climate entities."""
    data = config_entry.runtime_data
    parent_coordinator = data.parent_coordinator
    device = parent_coordinator.device

    # As there are no standalone thermostats, we just iterate over the children.
    async_add_entities(
        TPLinkClimateEntity(child, parent_coordinator, parent=device)
        for child in device.children
        if child.device_type is DeviceType.Thermostat
    )


class TPLinkClimateEntity(CoordinatedTPLinkEntity, ClimateEntity):
    """Representation of a TPLink thermostat."""

    _attr_name = None
    _attr_supported_features = (
        ClimateEntityFeature.TARGET_TEMPERATURE
        | ClimateEntityFeature.TURN_OFF
        | ClimateEntityFeature.TURN_ON
    )
    _attr_hvac_modes = [HVACMode.HEAT, HVACMode.OFF]
    _attr_precision = PRECISION_WHOLE

    # This disables the warning for async_turn_{on,off}, can be removed later.
    _enable_turn_on_off_backwards_compatibility = False

    def __init__(
        self,
        device: Device,
        coordinator: TPLinkDataUpdateCoordinator,
        *,
        parent: Device,
    ) -> None:
        """Initialize the climate entity."""
        self._state_feature = device.features["state"]
        self._mode_feature = device.features["thermostat_mode"]
        self._temp_feature = device.features["temperature"]
        self._target_feature = device.features["target_temperature"]

        self._attr_min_temp = self._target_feature.minimum_value
        self._attr_max_temp = self._target_feature.maximum_value
        self._attr_temperature_unit = UNIT_MAPPING[cast(str, self._temp_feature.unit)]

        super().__init__(device, coordinator, parent=parent)

    @async_refresh_after
    async def async_set_temperature(self, **kwargs: Any) -> None:
        """Set target temperature."""
        await self._target_feature.set_value(int(kwargs[ATTR_TEMPERATURE]))

    @async_refresh_after
    async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
        """Set hvac mode (heat/off)."""
        if hvac_mode is HVACMode.HEAT:
            await self._state_feature.set_value(True)
        elif hvac_mode is HVACMode.OFF:
            await self._state_feature.set_value(False)
        else:
            raise ServiceValidationError(f"Tried to set unsupported mode: {hvac_mode}")

    @async_refresh_after
    async def async_turn_on(self) -> None:
        """Turn heating on."""
        await self._state_feature.set_value(True)

    @async_refresh_after
    async def async_turn_off(self) -> None:
        """Turn heating off."""
        await self._state_feature.set_value(False)

    @callback
    def _async_update_attrs(self) -> None:
        """Update the entity's attributes."""
        self._attr_current_temperature = self._temp_feature.value
        self._attr_target_temperature = self._target_feature.value

        self._attr_hvac_mode = (
            HVACMode.HEAT if self._state_feature.value else HVACMode.OFF
        )

        if (
            self._mode_feature.value not in STATE_TO_ACTION
            and self._attr_hvac_action is not HVACAction.OFF
        ):
            _LOGGER.warning(
                "Unknown thermostat state, defaulting to OFF: %s",
                self._mode_feature.value,
            )
            self._attr_hvac_action = HVACAction.OFF
            return

        self._attr_hvac_action = STATE_TO_ACTION[self._mode_feature.value]

    def _get_unique_id(self) -> str:
        """Return unique id."""
        return f"{self._device.device_id}_climate"
