"""Support for Balboa Spa Wifi adaptor."""
from __future__ import annotations

from enum import IntEnum
from typing import Any

from pybalboa import SpaClient, SpaControl
from pybalboa.enums import HeatMode, HeatState, TemperatureUnit

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

from .const import DOMAIN
from .entity import BalboaEntity

HEAT_HVAC_MODE_MAP: dict[IntEnum, HVACMode] = {
    HeatMode.READY: HVACMode.HEAT,
    HeatMode.REST: HVACMode.OFF,
    HeatMode.READY_IN_REST: HVACMode.AUTO,
}
HVAC_HEAT_MODE_MAP = {value: key for key, value in HEAT_HVAC_MODE_MAP.items()}
HEAT_STATE_HVAC_ACTION_MAP = {
    HeatState.OFF: HVACAction.OFF,
    HeatState.HEATING: HVACAction.HEATING,
    HeatState.HEAT_WAITING: HVACAction.IDLE,
}
TEMPERATURE_UNIT_MAP = {
    TemperatureUnit.CELSIUS: UnitOfTemperature.CELSIUS,
    TemperatureUnit.FAHRENHEIT: UnitOfTemperature.FAHRENHEIT,
}


async def async_setup_entry(
    hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
    """Set up the spa climate entity."""
    async_add_entities([BalboaClimateEntity(hass.data[DOMAIN][entry.entry_id])])


class BalboaClimateEntity(BalboaEntity, ClimateEntity):
    """Representation of a Balboa spa climate entity."""

    _attr_icon = "mdi:hot-tub"
    _attr_hvac_modes = [HVACMode.HEAT, HVACMode.OFF]
    _attr_supported_features = (
        ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE
    )
    _attr_translation_key = DOMAIN

    def __init__(self, client: SpaClient) -> None:
        """Initialize the climate entity."""
        super().__init__(client, "Climate")
        self._attr_preset_modes = [opt.name.lower() for opt in client.heat_mode.options]

        self._blower: SpaControl | None = None
        if client.blowers and (blower := client.blowers[0]) is not None:
            self._blower = blower
            self._attr_supported_features |= ClimateEntityFeature.FAN_MODE
            self._fan_mode_map = {opt.name.lower(): opt for opt in blower.options}
            self._attr_fan_modes = list(self._fan_mode_map)

    @property
    def hvac_mode(self) -> HVACMode | None:
        """Return the current HVAC mode."""
        return HEAT_HVAC_MODE_MAP.get(self._client.heat_mode.state)

    @property
    def hvac_action(self) -> str:
        """Return the current operation mode."""
        return HEAT_STATE_HVAC_ACTION_MAP[self._client.heat_state]

    @property
    def fan_mode(self) -> str | None:
        """Return the fan setting."""
        if (blower := self._blower) is not None:
            return blower.state.name.lower()
        return None

    @property
    def precision(self) -> float:
        """Return the precision of the system."""
        if self.hass.config.units.temperature_unit == UnitOfTemperature.CELSIUS:
            return PRECISION_HALVES
        return PRECISION_WHOLE

    @property
    def temperature_unit(self) -> str:
        """Return the unit of measurement used by the platform."""
        return TEMPERATURE_UNIT_MAP[self._client.temperature_unit]

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

    @property
    def target_temperature(self) -> float:
        """Return the target temperature we try to reach."""
        return self._client.target_temperature

    @property
    def min_temp(self) -> float:
        """Return the minimum temperature supported by the spa."""
        return self._client.temperature_minimum

    @property
    def max_temp(self) -> float:
        """Return the minimum temperature supported by the spa."""
        return self._client.temperature_maximum

    @property
    def preset_mode(self) -> str:
        """Return current preset mode."""
        return self._client.heat_mode.state.name.lower()

    async def async_set_temperature(self, **kwargs: Any) -> None:
        """Set a new target temperature."""
        await self._client.set_temperature(kwargs[ATTR_TEMPERATURE])

    async def async_set_preset_mode(self, preset_mode: str) -> None:
        """Set new preset mode."""
        await self._client.heat_mode.set_state(HeatMode[preset_mode.upper()])

    async def async_set_fan_mode(self, fan_mode: str) -> None:
        """Set new fan mode."""
        if (blower := self._blower) is not None:
            await blower.set_state(self._fan_mode_map[fan_mode])

    async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
        """Set new target hvac mode."""
        await self._client.heat_mode.set_state(HVAC_HEAT_MODE_MAP[hvac_mode])