"""Midea Fan entries."""

import logging
from typing import Any, cast

from homeassistant.components.fan import FanEntity, FanEntityFeature
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
    CONF_DEVICE_ID,
    CONF_SWITCHES,
    Platform,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from midealan.device import DeviceType
from midealan.devices.ac import DeviceAttributes as ACAttributes
from midealan.devices.ac import MideaACDevice
from midealan.devices.b6 import MideaB6Device
from midealan.devices.ce import DeviceAttributes as CEAttributes
from midealan.devices.ce import MideaCEDevice
from midealan.devices.fa import MideaFADevice
from midealan.devices.x40 import DeviceAttributes as X40Attributes
from midealan.devices.x40 import MideaX40Device

from .const import DEVICES, DOMAIN
from .midea_devices import (
    FRESH_AIR_EXHAUST,
    FRESH_AIR_EXHAUST_MODE,
    FRESH_AIR_EXHAUST_POWER,
    FRESH_AIR_EXHAUST_SPEED,
    MIDEA_DEVICES,
)
from .midea_entity import MideaEntity

_LOGGER = logging.getLogger(__name__)


async def async_setup_entry(
    hass: HomeAssistant,
    config_entry: ConfigEntry,
    async_add_entities: AddEntitiesCallback,
) -> None:
    """Set up fan entries."""
    device_id = config_entry.data.get(CONF_DEVICE_ID)
    device = hass.data[DOMAIN][DEVICES].get(device_id)
    extra_switches = config_entry.options.get(CONF_SWITCHES, [])
    devs: list[
        MideaFAFan | MideaB6Fan | MideaACFreshAirFan | MideaCEFan | MideaX40Fan
    ] = []
    for entity_key, config in cast(
        "dict",
        MIDEA_DEVICES[device.device_type]["entities"],
    ).items():
        required_attribute = config.get("required_attribute")
        if (
            required_attribute is not None
            and required_attribute not in device.attributes
        ):
            continue
        if config["type"] == Platform.FAN and (
            config.get("default") or entity_key in extra_switches
        ):
            if device.device_type == DeviceType.FA:
                devs.append(MideaFAFan(device, entity_key))
            elif device.device_type == DeviceType.B6:
                devs.append(MideaB6Fan(device, entity_key))
            elif device.device_type == DeviceType.AC:
                devs.append(MideaACFreshAirFan(device, entity_key))
            elif device.device_type == DeviceType.CE:
                devs.append(MideaCEFan(device, entity_key))
            elif device.device_type == DeviceType.X40:
                devs.append(MideaX40Fan(device, entity_key))
    async_add_entities(devs)


# HA version >= 2024,8 support TURN_ON | TURN_OFF,for future changes, ref PR #285
try:
    FAN_FEATURE_TURN_ON_OFF = FanEntityFeature["TURN_ON"] | FanEntityFeature["TURN_OFF"]
except KeyError:
    FAN_FEATURE_TURN_ON_OFF = FanEntityFeature(0)


class MideaFan(MideaEntity, FanEntity):
    """Midea Fan Entries Base Class."""

    _enable_turn_on_off_backwards_compatibility = False  # 2024.8~2025.1

    @property
    def preset_modes(self) -> list[str] | None:
        """Midea Fan preset modes."""
        return (
            self._device.preset_modes if hasattr(self._device, "preset_modes") else None
        )

    @property
    def is_on(self) -> bool:
        """Midea Fan is on."""
        return cast("bool", self._device.get_attribute("power"))

    @property
    def oscillating(self) -> bool:
        """Midea Fan oscillating."""
        return cast("bool", self._device.get_attribute("oscillate"))

    @property
    def preset_mode(self) -> str | None:
        """Midea Fan preset mode."""
        return cast("str", self._device.get_attribute("mode"))

    @property
    def fan_speed(self) -> int | None:
        """Midea Fan fan speed."""
        return cast("int", self._device.get_attribute("fan_speed"))

    def turn_off(self, **kwargs: Any) -> None:  # ruff:ignore[any-type, unused-method-argument]
        """Midea Fan turn off."""
        self._device.set_attribute(attr="power", value=False)

    def oscillate(self, oscillating: bool) -> None:
        """Midea Fan oscillate."""
        self._device.set_attribute(attr="oscillate", value=oscillating)

    def set_preset_mode(self, preset_mode: str) -> None:
        """Midea Fan set preset mode."""
        # Pass the preset value through unchanged. `preset_modes` already
        # returns the device's own strings and HA only ever hands one of them
        # back, so the value is guaranteed valid. Do NOT `.capitalize()` it:
        # that corrupts multi-word / mixed-case names such as the CE fan's
        # "ECO mode" -> "Eco mode", which the device matches case-sensitively
        # and silently drops, making the ECO preset unselectable.
        self._device.set_attribute(attr="mode", value=preset_mode)

    @property
    def percentage(self) -> int | None:
        """Midea Fan percentage."""
        if not self.fan_speed:
            return None
        return int(round(self.fan_speed * self.percentage_step))  # ruff:ignore[unnecessary-cast-to-int]

    def set_percentage(self, percentage: int) -> None:
        """Midea Fan set percentage."""
        fan_speed = round(percentage / self.percentage_step)
        self._device.set_attribute(attr="fan_speed", value=fan_speed)

    async def async_set_percentage(self, percentage: int) -> None:
        """Midea Fan async set percentage."""
        if percentage == 0:
            await self.async_turn_off()
        else:
            await self.hass.async_add_executor_job(self.set_percentage, percentage)

    def update_state(self, status: Any) -> None:  # ruff:ignore[any-type, unused-method-argument]
        """Midea Fan update state."""
        if not self.hass:
            _LOGGER.warning(
                "Fan update_state skipped for %s [%s]: HASS is None",
                self.name,
                type(self),
            )
            return
        self.schedule_update_if_running()


class MideaFAFan(MideaFan):
    """Midea FA Fan Entries."""

    _device: MideaFADevice

    def __init__(self, device: MideaFADevice, entity_key: str) -> None:
        """Midea FA Fan entity init."""
        super().__init__(device, entity_key)
        self._attr_supported_features = (
            FanEntityFeature.SET_SPEED
            | FanEntityFeature.OSCILLATE
            | FanEntityFeature.PRESET_MODE
            | FAN_FEATURE_TURN_ON_OFF
        )
        self._attr_speed_count = self._device.speed_count

    def turn_on(
        self,
        percentage: int | None = None,
        preset_mode: str | None = None,
        **kwargs: Any,  # ruff:ignore[any-type, unused-method-argument]
    ) -> None:
        """Midea FA Fan turn on."""
        fan_speed = int(percentage / self.percentage_step + 0.5) if percentage else None
        self._device.turn_on(fan_speed=fan_speed, mode=preset_mode)


class MideaB6Fan(MideaFan):
    """Midea B6 Fan Entries."""

    _device: MideaB6Device

    def __init__(self, device: MideaB6Device, entity_key: str) -> None:
        """Midea B6 Fan entity init."""
        super().__init__(device, entity_key)
        self._attr_supported_features = (
            FanEntityFeature.SET_SPEED
            | FanEntityFeature.PRESET_MODE
            | FAN_FEATURE_TURN_ON_OFF
        )
        self._attr_speed_count = self._device.speed_count

    def turn_on(
        self,
        percentage: int | None = None,
        preset_mode: str | None = None,
        **kwargs: Any,  # ruff:ignore[any-type, unused-method-argument]
    ) -> None:
        """Midea B6 Fan turn on."""
        fan_speed = int(percentage / self.percentage_step + 0.5) if percentage else None
        self._device.turn_on(fan_speed=fan_speed, mode=preset_mode)


class MideaACFreshAirFan(MideaFan):
    """Midea AC Fresh Air Fan Entries."""

    _device: MideaACDevice

    def __init__(self, device: MideaACDevice, entity_key: str) -> None:
        """Midea AC Fresh Air Fan entity init."""
        super().__init__(device, entity_key)
        self._is_exhaust = entity_key == FRESH_AIR_EXHAUST
        self._attr_supported_features = (
            FanEntityFeature.SET_SPEED
            | FanEntityFeature.PRESET_MODE
            | FAN_FEATURE_TURN_ON_OFF
        )
        self._attr_speed_count = 100

    @property
    def preset_modes(self) -> list[str] | None:
        """Midea AC Fan preset modes."""
        if self._is_exhaust:
            return cast(
                "list[str]",
                getattr(self._device, "fresh_air_exhaust_fan_speeds", []),
            )
        return cast("list", self._device.fresh_air_fan_speeds)

    @property
    def is_on(self) -> bool:
        """Midea AC Fan is on."""
        attribute = (
            FRESH_AIR_EXHAUST_POWER
            if self._is_exhaust
            else ACAttributes.fresh_air_power
        )
        return cast("bool", self._device.get_attribute(attribute))

    @property
    def fan_speed(self) -> int:
        """Midea AC Fan fan speed."""
        attribute = (
            FRESH_AIR_EXHAUST_SPEED
            if self._is_exhaust
            else ACAttributes.fresh_air_fan_speed
        )
        return cast("int", self._device.get_attribute(attribute))

    def turn_on(
        self,
        percentage: int | None = None,
        preset_mode: str | None = None,
        **kwargs: Any,  # ruff:ignore[any-type, unused-method-argument]
    ) -> None:
        """Midea AC Fan tun on."""
        if preset_mode is not None:
            self.set_preset_mode(preset_mode)
            return
        if percentage is not None:
            self.set_percentage(percentage)
            return
        attribute = (
            FRESH_AIR_EXHAUST_POWER
            if self._is_exhaust
            else ACAttributes.fresh_air_power
        )
        self._device.set_attribute(attr=attribute, value=True)

    def turn_off(self, **kwargs: Any) -> None:  # ruff:ignore[any-type, unused-method-argument]
        """Midea AC Fan turn off."""
        attribute = (
            FRESH_AIR_EXHAUST_POWER
            if self._is_exhaust
            else ACAttributes.fresh_air_power
        )
        self._device.set_attribute(attr=attribute, value=False)

    def set_percentage(self, percentage: int) -> None:
        """Midea AC Fan set percentage."""
        fan_speed = int(percentage / self.percentage_step + 0.5)
        attribute = (
            FRESH_AIR_EXHAUST_SPEED
            if self._is_exhaust
            else ACAttributes.fresh_air_fan_speed
        )
        self._device.set_attribute(
            attr=attribute,
            value=fan_speed,
        )

    def set_preset_mode(self, preset_mode: str) -> None:
        """Midea AC Fan set preset mode."""
        attribute = (
            FRESH_AIR_EXHAUST_MODE if self._is_exhaust else ACAttributes.fresh_air_mode
        )
        self._device.set_attribute(attr=attribute, value=preset_mode)

    @property
    def preset_mode(self) -> str | None:
        """Midea AC Fan preset mode."""
        attribute = (
            FRESH_AIR_EXHAUST_MODE if self._is_exhaust else ACAttributes.fresh_air_mode
        )
        return cast("str", self._device.get_attribute(attr=attribute))


class MideaCEFan(MideaFan):
    """Midea CE Fan Entries."""

    _device: MideaCEDevice

    def __init__(self, device: MideaCEDevice, entity_key: str) -> None:
        """Midea CE Fan entity init."""
        super().__init__(device, entity_key)
        self._attr_supported_features = (
            FanEntityFeature.SET_SPEED
            | FanEntityFeature.PRESET_MODE
            | FAN_FEATURE_TURN_ON_OFF
        )
        self._attr_speed_count = self._device.speed_count

    def turn_on(
        self,
        percentage: int | None = None,  # ruff:ignore[unused-method-argument]
        preset_mode: str | None = None,  # ruff:ignore[unused-method-argument]
        **kwargs: Any,  # ruff:ignore[any-type, unused-method-argument]
    ) -> None:
        """Midea CE Fan turn on."""
        self._device.set_attribute(attr=CEAttributes.power, value=True)

    async def async_set_percentage(self, percentage: int) -> None:
        """Midea CE Fan async set percentage."""
        await self.hass.async_add_executor_job(self.set_percentage, percentage)


class MideaX40Fan(MideaFan):
    """Midea X40 Fan Entries."""

    _device: MideaX40Device

    def __init__(self, device: MideaX40Device, entity_key: str) -> None:
        """Midea X40 Fan entity init."""
        super().__init__(device, entity_key)
        self._attr_supported_features = (
            FanEntityFeature.SET_SPEED
            | FanEntityFeature.OSCILLATE
            | FAN_FEATURE_TURN_ON_OFF
        )
        self._attr_speed_count = 2

    @property
    def is_on(self) -> bool:
        """Midea X40 Fan is on."""
        return cast("int", self._device.get_attribute(attr=X40Attributes.fan_speed)) > 0

    def turn_on(
        self,
        percentage: int | None = None,  # ruff:ignore[unused-method-argument]
        preset_mode: str | None = None,  # ruff:ignore[unused-method-argument]
        **kwargs: Any,  # ruff:ignore[any-type, unused-method-argument]
    ) -> None:
        """Midea X40 Fan turn on."""
        self._device.set_attribute(attr=X40Attributes.fan_speed, value=1)

    def turn_off(self, **kwargs: Any) -> None:  # ruff:ignore[any-type, unused-method-argument]
        """Midea X40 Fan turn off."""
        self._device.set_attribute(attr=X40Attributes.fan_speed, value=0)
