"""Support for climates."""

from __future__ import annotations

from enum import StrEnum
from typing import Any

from aiocomelit import ComelitSerialBridgeObject
from aiocomelit.const import CLIMATE

from homeassistant.components.climate import (
    ClimateEntity,
    ClimateEntityFeature,
    HVACAction,
    HVACMode,
    UnitOfTemperature,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_TENTHS
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity

from .const import DOMAIN
from .coordinator import ComelitSerialBridge


class ClimaComelitMode(StrEnum):
    """Serial Bridge clima modes."""

    AUTO = "A"
    OFF = "O"
    LOWER = "L"
    UPPER = "U"


class ClimaComelitCommand(StrEnum):
    """Serial Bridge clima commands."""

    OFF = "off"
    ON = "on"
    MANUAL = "man"
    SET = "set"
    AUTO = "auto"


API_STATUS: dict[str, dict[str, Any]] = {
    ClimaComelitMode.OFF: {
        "action": "off",
        "hvac_mode": HVACMode.OFF,
        "hvac_action": HVACAction.OFF,
    },
    ClimaComelitMode.LOWER: {
        "action": "lower",
        "hvac_mode": HVACMode.COOL,
        "hvac_action": HVACAction.COOLING,
    },
    ClimaComelitMode.UPPER: {
        "action": "upper",
        "hvac_mode": HVACMode.HEAT,
        "hvac_action": HVACAction.HEATING,
    },
}

MODE_TO_ACTION: dict[HVACMode, ClimaComelitCommand] = {
    HVACMode.OFF: ClimaComelitCommand.OFF,
    HVACMode.AUTO: ClimaComelitCommand.AUTO,
    HVACMode.COOL: ClimaComelitCommand.MANUAL,
    HVACMode.HEAT: ClimaComelitCommand.MANUAL,
}


async def async_setup_entry(
    hass: HomeAssistant,
    config_entry: ConfigEntry,
    async_add_entities: AddEntitiesCallback,
) -> None:
    """Set up Comelit climates."""

    coordinator: ComelitSerialBridge = hass.data[DOMAIN][config_entry.entry_id]

    async_add_entities(
        ComelitClimateEntity(coordinator, device, config_entry.entry_id)
        for device in coordinator.data[CLIMATE].values()
    )


class ComelitClimateEntity(CoordinatorEntity[ComelitSerialBridge], ClimateEntity):
    """Climate device."""

    _attr_hvac_modes = [HVACMode.AUTO, HVACMode.COOL, HVACMode.HEAT, HVACMode.OFF]
    _attr_max_temp = 30
    _attr_min_temp = 5
    _attr_supported_features = (
        ClimateEntityFeature.TARGET_TEMPERATURE
        | ClimateEntityFeature.TURN_OFF
        | ClimateEntityFeature.TURN_ON
    )
    _attr_target_temperature_step = PRECISION_TENTHS
    _attr_temperature_unit = UnitOfTemperature.CELSIUS
    _attr_has_entity_name = True
    _attr_name = None
    _enable_turn_on_off_backwards_compatibility = False

    def __init__(
        self,
        coordinator: ComelitSerialBridge,
        device: ComelitSerialBridgeObject,
        config_entry_entry_id: str,
    ) -> None:
        """Init light entity."""
        self._api = coordinator.api
        self._device = device
        super().__init__(coordinator)
        # Use config_entry.entry_id as base for unique_id
        # because no serial number or mac is available
        self._attr_unique_id = f"{config_entry_entry_id}-{device.index}"
        self._attr_device_info = coordinator.platform_device_info(device, device.type)

    @property
    def _clima(self) -> list[Any]:
        """Return clima device data."""
        # CLIMATE has a 2 item tuple:
        # - first  for Clima
        # - second for Humidifier
        return self.coordinator.data[CLIMATE][self._device.index].val[0]

    @property
    def _api_mode(self) -> str:
        """Return device mode."""
        # Values from API: "O", "L", "U"
        return self._clima[2]

    @property
    def _api_active(self) -> bool:
        "Return device active/idle."
        return self._clima[1]

    @property
    def _api_automatic(self) -> bool:
        """Return device in automatic/manual mode."""
        return self._clima[3] == ClimaComelitMode.AUTO

    @property
    def target_temperature(self) -> float:
        """Set target temperature."""
        return self._clima[4] / 10

    @property
    def current_temperature(self) -> float:
        """Return current temperature."""
        return self._clima[0] / 10

    @property
    def hvac_mode(self) -> HVACMode | None:
        """HVAC current mode."""

        if self._api_mode == ClimaComelitMode.OFF:
            return HVACMode.OFF

        if self._api_automatic:
            return HVACMode.AUTO

        if self._api_mode in API_STATUS:
            return API_STATUS[self._api_mode]["hvac_mode"]

        return None

    @property
    def hvac_action(self) -> HVACAction | None:
        """HVAC current action."""

        if self._api_mode == ClimaComelitMode.OFF:
            return HVACAction.OFF

        if not self._api_active:
            return HVACAction.IDLE

        if self._api_mode in API_STATUS:
            return API_STATUS[self._api_mode]["hvac_action"]

        return None

    async def async_set_temperature(self, **kwargs: Any) -> None:
        """Set new target temperature."""
        if (
            target_temp := kwargs.get(ATTR_TEMPERATURE)
        ) is None or self.hvac_mode == HVACMode.OFF:
            return

        await self.coordinator.api.set_clima_status(
            self._device.index, ClimaComelitCommand.MANUAL
        )
        await self.coordinator.api.set_clima_status(
            self._device.index, ClimaComelitCommand.SET, target_temp
        )

    async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
        """Set hvac mode."""

        if hvac_mode != HVACMode.OFF:
            await self.coordinator.api.set_clima_status(
                self._device.index, ClimaComelitCommand.ON
            )
        await self.coordinator.api.set_clima_status(
            self._device.index, MODE_TO_ACTION[hvac_mode]
        )
