"""Fan platform for IntelliClima VMC."""

import math
from typing import Any, override

from pyintelliclima.const import FanMode, FanSpeed
from pyintelliclima.intelliclima_types import IntelliClimaECO

from homeassistant.components.fan import FanEntity, FanEntityFeature
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.util.percentage import (
    percentage_to_ranged_value,
    ranged_value_to_percentage,
)
from homeassistant.util.scaling import int_states_in_range

from .coordinator import IntelliClimaConfigEntry, IntelliClimaCoordinator
from .entity import IntelliClimaECOEntity

# Coordinator is used to centralize the data updates
PARALLEL_UPDATES = 0


async def async_setup_entry(
    hass: HomeAssistant,
    entry: IntelliClimaConfigEntry,
    async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
    """Set up IntelliClima VMC fans."""
    coordinator = entry.runtime_data

    entities: list[IntelliClimaVMCFan] = [
        IntelliClimaVMCFan(
            coordinator=coordinator,
            device=ecocomfort2,
        )
        for ecocomfort2 in coordinator.data.ecocomfort2_devices.values()
    ]

    async_add_entities(entities)


class IntelliClimaVMCFan(IntelliClimaECOEntity, FanEntity):
    """Representation of an IntelliClima VMC fan."""

    _attr_name = None
    _attr_supported_features = (
        FanEntityFeature.PRESET_MODE
        | FanEntityFeature.SET_SPEED
        | FanEntityFeature.TURN_OFF
        | FanEntityFeature.TURN_ON
    )
    _attr_preset_modes = ["auto"]

    def __init__(
        self,
        coordinator: IntelliClimaCoordinator,
        device: IntelliClimaECO,
    ) -> None:
        """Class initializer."""
        super().__init__(coordinator, device)

        self._speed_range = (int(FanSpeed.sleep), int(FanSpeed.high))
        self._attr_unique_id = device.id

    @property
    @override
    def is_on(self) -> bool:
        """Return true if fan is on."""
        return bool(self._device_data.mode_set != FanMode.off)

    @property
    @override
    def percentage(self) -> int | None:
        """Return the current speed percentage."""
        device_data = self._device_data

        if device_data.speed_set == FanSpeed.auto_get:
            return None

        return ranged_value_to_percentage(self._speed_range, int(device_data.speed_set))

    @property
    @override
    def speed_count(self) -> int:
        """Return the number of speeds the fan supports."""
        return int_states_in_range(self._speed_range)

    @property
    @override
    def preset_mode(self) -> str | None:
        """Return the current preset mode."""
        device_data = self._device_data

        if device_data.mode_set == FanMode.off:
            return None
        if (
            device_data.speed_set == FanSpeed.auto_get
            and device_data.mode_set == FanMode.sensor
        ):
            return "auto"

        return None

    @override
    async def async_turn_on(
        self,
        percentage: int | None = None,
        preset_mode: str | None = None,
        **kwargs: Any,
    ) -> None:
        """Turn on the fan.

        Defaults back to 25% if percentage argument is 0
        to prevent loop of turning off/on infinitely.
        """
        percentage = 25 if percentage == 0 else percentage
        await self.async_set_mode_speed(preset_mode=preset_mode, percentage=percentage)

    @override
    async def async_turn_off(self, **kwargs: Any) -> None:
        """Turn off the fan."""
        await self.coordinator.api.ecocomfort.turn_off(self._device_sn)
        await self.coordinator.async_request_refresh()

    @override
    async def async_set_percentage(self, percentage: int) -> None:
        """Set the speed percentage."""
        await self.async_set_mode_speed(percentage=percentage)

    @override
    async def async_set_preset_mode(self, preset_mode: str) -> None:
        """Set preset mode."""
        await self.async_set_mode_speed(preset_mode=preset_mode)

    async def async_set_mode_speed(
        self, preset_mode: str | None = None, percentage: int | None = None
    ) -> None:
        """Set mode and speed.

        If percentage is None, it first defaults to the respective property.
        If that is also None, then percentage defaults to 25 (sleep)
        """
        percentage = self.percentage if percentage is None else percentage
        percentage = 25 if percentage is None else percentage

        if preset_mode == "auto":
            # auto is a special case with special mode and speed setting
            await self.coordinator.api.ecocomfort.set_mode_speed_auto(self._device_sn)
            await self.coordinator.async_request_refresh()
            return
        if percentage == 0:
            # Setting fan speed to zero turns off the fan
            await self.async_turn_off()
            return

        # Determine the fan mode
        if not self.is_on:
            # Default to alternate fan mode if not turned on
            mode = FanMode.alternate
        else:
            # Maintain current mode
            mode = self._device_data.mode_set

        speed = FanSpeed(
            str(
                math.ceil(
                    percentage_to_ranged_value(
                        self._speed_range,
                        percentage,
                    )
                )
            )
        )

        speed = FanSpeed.sleep if speed == FanSpeed.off else speed
        await self.coordinator.api.ecocomfort.set_mode_speed(
            self._device_sn, mode, speed
        )
        await self.coordinator.async_request_refresh()
