"""Smart Thermostat Radiator Device."""

import logging
from typing import Any

from ..const import SwitchbotModel
from ..const.climate import ClimateAction, ClimateMode
from ..const.climate import SmartThermostatRadiatorMode as STRMode
from .device import (
    SwitchbotEncryptedDevice,
    SwitchbotOperationError,
    SwitchbotSequenceDevice,
    update_after_operation,
)

_LOGGER = logging.getLogger(__name__)

DEVICE_GET_BASIC_SETTINGS_KEY = "5702"

_modes = STRMode.get_valid_modes()
SMART_THERMOSTAT_TO_HA_HVAC_MODE = {
    "off": ClimateMode.OFF,
    **dict.fromkeys(_modes, ClimateMode.HEAT),
}

COMMAND_SET_MODE = {
    mode.lname: f"570F7800{index:02X}" for index, mode in enumerate(STRMode)
}

# fast heating default use max temperature
COMMAND_SET_TEMP = {
    STRMode.MANUAL.lname: "570F7801{temp:04X}",
    STRMode.ECO.lname: "570F7802{temp:02X}",
    STRMode.COMFORT.lname: "570F7803{temp:02X}",
    STRMode.SCHEDULE.lname: "570F7806{temp:04X}",
}

MODE_TEMP_RANGE = {
    STRMode.ECO.lname: (10.0, 20.0),
    STRMode.COMFORT.lname: (10.0, 25.0),
}

DEFAULT_TEMP_RANGE = (5.0, 35.0)
HYSTERESIS_IDLE_THRESHOLD = 0.5


class SwitchbotSmartThermostatRadiator(
    SwitchbotSequenceDevice, SwitchbotEncryptedDevice
):
    """Representation of a Switchbot Smart Thermostat Radiator."""

    _model = SwitchbotModel.SMART_THERMOSTAT_RADIATOR
    _turn_off_command = "570100"
    _turn_on_command = "570101"

    @property
    def min_temperature(self) -> float:
        """Return the minimum target temperature."""
        return MODE_TEMP_RANGE.get(self.preset_mode, DEFAULT_TEMP_RANGE)[0]

    @property
    def max_temperature(self) -> float:
        """Return the maximum target temperature."""
        return MODE_TEMP_RANGE.get(self.preset_mode, DEFAULT_TEMP_RANGE)[1]

    @property
    def preset_modes(self) -> list[str]:
        """Return the supported preset modes."""
        return STRMode.get_modes()

    @property
    def preset_mode(self) -> str | None:
        """Return the current preset mode."""
        return self.get_current_mode()

    @property
    def hvac_modes(self) -> set[ClimateMode]:
        """Return the supported hvac modes."""
        return {ClimateMode.HEAT, ClimateMode.OFF}

    @property
    def hvac_mode(self) -> ClimateMode | None:
        """Return the current hvac mode."""
        return SMART_THERMOSTAT_TO_HA_HVAC_MODE.get(self.preset_mode, ClimateMode.OFF)

    @property
    def hvac_action(self) -> ClimateAction | None:
        """Return current action."""
        return self.get_action()

    @property
    def current_temperature(self) -> float | None:
        """Return the current temperature."""
        return self.get_current_temperature()

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

    @update_after_operation
    async def set_hvac_mode(self, hvac_mode: ClimateMode) -> None:
        """Set the hvac mode."""
        if hvac_mode == ClimateMode.OFF:
            return await self.turn_off()
        return await self.set_preset_mode("comfort")

    @update_after_operation
    async def set_preset_mode(self, preset_mode: str) -> bool:
        """Send command to set thermostat preset_mode."""
        return await self._send_command(COMMAND_SET_MODE[preset_mode])

    @update_after_operation
    async def set_target_temperature(self, temperature: float) -> bool:
        """Send command to set target temperature."""
        if self.preset_mode == STRMode.OFF.lname:
            raise SwitchbotOperationError("Cannot set temperature when mode is OFF.")
        if self.preset_mode == STRMode.BOOST.lname:
            raise SwitchbotOperationError("Boost mode defaults to max temperature.")

        temp_value = int(temperature * 10)
        cmd = COMMAND_SET_TEMP[self.preset_mode].format(temp=temp_value)

        _LOGGER.debug(
            "Setting temperature %.1f°C in mode %s → cmd=%s",
            temperature,
            self.preset_mode,
            cmd,
        )
        return await self._send_command(cmd)

    async def get_basic_info(self) -> dict[str, Any] | None:
        """Get device basic settings."""
        if not (_data := await self._get_basic_info()):
            return None
        _LOGGER.debug("data: %s", _data)

        battery = _data[1]
        firmware = _data[2] / 10.0
        hardware = _data[3]
        last_mode = STRMode.get_mode_name((_data[4] >> 3) & 0x07)
        mode = STRMode.get_mode_name(_data[4] & 0x07)
        temp_raw_value = _data[5] << 8 | _data[6]
        temp_sign = 1 if temp_raw_value >> 15 else -1
        temperature = temp_sign * (temp_raw_value & 0x7FFF) / 10.0
        manual_target_temp = (_data[7] << 8 | _data[8]) / 10.0
        comfort_target_temp = _data[9] / 10.0
        economic_target_temp = _data[10] / 10.0
        fast_heat_time = _data[11]
        child_lock = bool(_data[12] & 0x03)
        target_temp = (_data[13] << 8 | _data[14]) / 10.0
        door_open = bool(_data[14] & 0x01)

        result = {
            "battery": battery,
            "firmware": firmware,
            "hardware": hardware,
            "last_mode": last_mode,
            "mode": mode,
            "temperature": temperature,
            "manual_target_temp": manual_target_temp,
            "comfort_target_temp": comfort_target_temp,
            "economic_target_temp": economic_target_temp,
            "fast_heat_time": fast_heat_time,
            "child_lock": child_lock,
            "target_temp": target_temp,
            "door_open": door_open,
        }

        _LOGGER.debug("Smart Thermostat Radiator basic info: %s", result)
        return result

    def is_on(self) -> bool | None:
        """Return true if the thermostat is on."""
        return self._get_adv_value("isOn")

    def get_current_mode(self) -> str | None:
        """Return the current mode of the thermostat."""
        return self._get_adv_value("mode")

    def door_open(self) -> bool | None:
        """Return true if the door is open."""
        return self._get_adv_value("door_open")

    def get_current_temperature(self) -> float | None:
        """Return the current temperature."""
        return self._get_adv_value("temperature")

    def get_target_temperature(self) -> float | None:
        """Return the target temperature."""
        return self._get_adv_value("target_temperature")

    def get_action(self) -> ClimateAction:
        """Return current action from cache."""
        if not self.is_on():
            return ClimateAction.OFF

        current_temp = self.get_current_temperature()
        target_temp = self.get_target_temperature()

        if (
            current_temp is not None
            and target_temp is not None
            and current_temp >= (target_temp + HYSTERESIS_IDLE_THRESHOLD)
        ):
            return ClimateAction.IDLE

        return ClimateAction.HEATING
