"""Support for WaterHeater entities of the Evohome integration."""

from __future__ import annotations

import logging
from typing import Any

import evohomeasync2 as evo
from evohomeasync2.const import SZ_STATE_STATUS, SZ_TEMPERATURE_STATUS
from evohomeasync2.schemas.const import DhwState as EvoDhwState, ZoneMode as EvoZoneMode

from homeassistant.components.water_heater import (
    WaterHeaterEntity,
    WaterHeaterEntityFeature,
)
from homeassistant.const import (
    PRECISION_TENTHS,
    PRECISION_WHOLE,
    STATE_OFF,
    STATE_ON,
    UnitOfTemperature,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.util import dt as dt_util

from .const import EVOHOME_DATA
from .coordinator import EvoDataUpdateCoordinator
from .entity import EvoChild

_LOGGER = logging.getLogger(__name__)

STATE_AUTO = "auto"

HA_STATE_TO_EVO = {STATE_AUTO: "", STATE_ON: EvoDhwState.ON, STATE_OFF: EvoDhwState.OFF}
EVO_STATE_TO_HA = {v: k for k, v in HA_STATE_TO_EVO.items() if k != ""}


async def async_setup_platform(
    hass: HomeAssistant,
    config: ConfigType,
    async_add_entities: AddEntitiesCallback,
    discovery_info: DiscoveryInfoType | None = None,
) -> None:
    """Create a DHW controller."""
    if discovery_info is None:
        return

    coordinator = hass.data[EVOHOME_DATA].coordinator
    tcs = hass.data[EVOHOME_DATA].tcs

    assert tcs.hotwater is not None  # mypy check

    _LOGGER.debug(
        "Adding: DhwController (%s), id=%s",
        tcs.hotwater.type,
        tcs.hotwater.id,
    )

    entity = EvoDHW(coordinator, tcs.hotwater)

    async_add_entities([entity])

    await entity.update_attrs()


class EvoDHW(EvoChild, WaterHeaterEntity):
    """Base for any evohome-compatible DHW controller."""

    _attr_name = "DHW controller"
    _attr_icon = "mdi:thermometer-lines"
    _attr_operation_list = list(HA_STATE_TO_EVO)
    _attr_supported_features = (
        WaterHeaterEntityFeature.AWAY_MODE
        | WaterHeaterEntityFeature.ON_OFF
        | WaterHeaterEntityFeature.OPERATION_MODE
    )
    _attr_temperature_unit = UnitOfTemperature.CELSIUS

    _evo_device: evo.HotWater
    _evo_id_attr = "dhw_id"
    _evo_state_attr_names = (SZ_STATE_STATUS, SZ_TEMPERATURE_STATUS)

    def __init__(
        self, coordinator: EvoDataUpdateCoordinator, evo_device: evo.HotWater
    ) -> None:
        """Initialize an evohome-compatible DHW controller."""

        super().__init__(coordinator, evo_device)
        self._evo_id = evo_device.id

        self._attr_unique_id = evo_device.id
        self._attr_name = evo_device.name  # is static

        self._attr_precision = (
            PRECISION_TENTHS if coordinator.client_v1 else PRECISION_WHOLE
        )

    @property
    def current_operation(self) -> str | None:
        """Return the current operating mode (Auto, On, or Off)."""
        if self._evo_device.mode == EvoZoneMode.FOLLOW_SCHEDULE:
            return STATE_AUTO
        return EVO_STATE_TO_HA[self._evo_device.state]

    @property
    def is_away_mode_on(self) -> bool | None:
        """Return True if away mode is on."""
        is_off = EVO_STATE_TO_HA[self._evo_device.state] == STATE_OFF
        is_permanent = self._evo_device.mode == EvoZoneMode.PERMANENT_OVERRIDE
        return is_off and is_permanent

    async def async_set_operation_mode(self, operation_mode: str) -> None:
        """Set new operation mode for a DHW controller.

        Except for Auto, the mode is only until the next SetPoint.
        """
        if operation_mode == STATE_AUTO:
            await self.coordinator.call_client_api(self._evo_device.reset())
        else:
            await self._update_schedule()
            until = self.setpoints.get("next_sp_from")
            until = dt_util.as_utc(until) if until else None

            if operation_mode == STATE_ON:
                await self.coordinator.call_client_api(self._evo_device.on(until=until))
            else:  # STATE_OFF
                await self.coordinator.call_client_api(
                    self._evo_device.off(until=until)
                )

    async def async_turn_away_mode_on(self) -> None:
        """Turn away mode on."""
        await self.coordinator.call_client_api(self._evo_device.off())

    async def async_turn_away_mode_off(self) -> None:
        """Turn away mode off."""
        await self.coordinator.call_client_api(self._evo_device.reset())

    async def async_turn_on(self, **kwargs: Any) -> None:
        """Turn on."""
        await self.coordinator.call_client_api(self._evo_device.on())

    async def async_turn_off(self, **kwargs: Any) -> None:
        """Turn off."""
        await self.coordinator.call_client_api(self._evo_device.off())
