"""Platform for Miele vacuum integration."""

from __future__ import annotations

from dataclasses import dataclass
from enum import IntEnum
import logging
from typing import Any, Final

from aiohttp import ClientResponseError
from pymiele import MieleEnum

from homeassistant.components.vacuum import (
    StateVacuumEntity,
    StateVacuumEntityDescription,
    VacuumActivity,
    VacuumEntityFeature,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from .const import DOMAIN, PROCESS_ACTION, PROGRAM_ID, MieleActions, MieleAppliance
from .coordinator import MieleConfigEntry
from .entity import MieleEntity

PARALLEL_UPDATES = 1

_LOGGER = logging.getLogger(__name__)

# The following const classes define program speeds and programs for the vacuum cleaner.
# Miele have used the same and overlapping names for fan_speeds and programs even
# if the contexts are different. This is an attempt to make it clearer in the integration.


class FanSpeed(IntEnum):
    """Define fan speeds."""

    normal = 0
    turbo = 1
    silent = 2


class FanProgram(IntEnum):
    """Define fan programs."""

    auto = 1
    spot = 2
    turbo = 3
    silent = 4


PROGRAM_MAP = {
    "normal": FanProgram.auto,
    "turbo": FanProgram.turbo,
    "silent": FanProgram.silent,
}

PROGRAM_TO_SPEED: dict[int, str] = {
    FanProgram.auto: "normal",
    FanProgram.turbo: "turbo",
    FanProgram.silent: "silent",
    FanProgram.spot: "normal",
}


class MieleVacuumStateCode(MieleEnum, missing_to_none=True):
    """Define vacuum state codes."""

    idle = 0
    cleaning = 5889
    returning = 5890
    paused = 5891
    going_to_target_area = 5892
    wheel_lifted = 5893
    dirty_sensors = 5894
    dust_box_missing = 5895
    blocked_drive_wheels = 5896
    blocked_brushes = 5897
    check_dust_box_and_filter = 5898
    internal_fault_reboot = 5899
    blocked_front_wheel = 5900
    docked = 5903, 5904
    remote_controlled = 5910


SUPPORTED_FEATURES = (
    VacuumEntityFeature.STATE
    | VacuumEntityFeature.FAN_SPEED
    | VacuumEntityFeature.START
    | VacuumEntityFeature.STOP
    | VacuumEntityFeature.PAUSE
    | VacuumEntityFeature.CLEAN_SPOT
)


@dataclass(frozen=True, kw_only=True)
class MieleVacuumDescription(StateVacuumEntityDescription):
    """Class describing Miele vacuum entities."""

    on_value: int


@dataclass
class MieleVacuumDefinition:
    """Class for defining vacuum entities."""

    types: tuple[MieleAppliance, ...]
    description: MieleVacuumDescription


VACUUM_TYPES: Final[tuple[MieleVacuumDefinition, ...]] = (
    MieleVacuumDefinition(
        types=(MieleAppliance.ROBOT_VACUUM_CLEANER,),
        description=MieleVacuumDescription(
            key="vacuum",
            on_value=14,
            name=None,
            translation_key="vacuum",
        ),
    ),
)


async def async_setup_entry(
    hass: HomeAssistant,
    config_entry: MieleConfigEntry,
    async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
    """Set up the vacuum platform."""
    coordinator = config_entry.runtime_data.coordinator

    async_add_entities(
        MieleVacuum(coordinator, device_id, definition.description)
        for device_id, device in coordinator.data.devices.items()
        for definition in VACUUM_TYPES
        if device.device_type in definition.types
    )


VACUUM_PHASE_TO_ACTIVITY = {
    MieleVacuumStateCode.idle.value: VacuumActivity.IDLE,
    MieleVacuumStateCode.docked.value: VacuumActivity.DOCKED,
    MieleVacuumStateCode.cleaning.value: VacuumActivity.CLEANING,
    MieleVacuumStateCode.going_to_target_area.value: VacuumActivity.CLEANING,
    MieleVacuumStateCode.returning.value: VacuumActivity.RETURNING,
    MieleVacuumStateCode.wheel_lifted.value: VacuumActivity.ERROR,
    MieleVacuumStateCode.dirty_sensors.value: VacuumActivity.ERROR,
    MieleVacuumStateCode.dust_box_missing.value: VacuumActivity.ERROR,
    MieleVacuumStateCode.blocked_drive_wheels.value: VacuumActivity.ERROR,
    MieleVacuumStateCode.blocked_brushes.value: VacuumActivity.ERROR,
    MieleVacuumStateCode.check_dust_box_and_filter.value: VacuumActivity.ERROR,
    MieleVacuumStateCode.internal_fault_reboot.value: VacuumActivity.ERROR,
    MieleVacuumStateCode.blocked_front_wheel.value: VacuumActivity.ERROR,
    MieleVacuumStateCode.paused.value: VacuumActivity.PAUSED,
    MieleVacuumStateCode.remote_controlled.value: VacuumActivity.PAUSED,
}


class MieleVacuum(MieleEntity, StateVacuumEntity):
    """Representation of a Vacuum entity."""

    entity_description: MieleVacuumDescription
    _attr_supported_features = SUPPORTED_FEATURES
    _attr_fan_speed_list = [fan_speed.name for fan_speed in FanSpeed]
    _attr_name = None

    @property
    def activity(self) -> VacuumActivity | None:
        """Return activity."""
        return VACUUM_PHASE_TO_ACTIVITY.get(
            MieleVacuumStateCode(self.device.state_program_phase).value
        )

    @property
    def fan_speed(self) -> str | None:
        """Return the fan speed."""
        return PROGRAM_TO_SPEED.get(self.device.state_program_id)

    @property
    def available(self) -> bool:
        """Return the availability of the entity."""

        return (
            self.action.power_off_enabled or self.action.power_on_enabled
        ) and super().available

    async def send(self, device_id: str, action: dict[str, Any]) -> None:
        """Send action to the device."""
        try:
            await self.api.send_action(device_id, action)
        except ClientResponseError as err:
            _LOGGER.debug("Error setting vacuum state for %s: %s", self.entity_id, err)
            raise HomeAssistantError(
                translation_domain=DOMAIN,
                translation_key="set_state_error",
                translation_placeholders={
                    "entity": self.entity_id,
                },
            ) from err

    async def async_clean_spot(self, **kwargs: Any) -> None:
        """Clean spot."""
        await self.send(self._device_id, {PROGRAM_ID: FanProgram.spot})

    async def async_start(self, **kwargs: Any) -> None:
        """Start cleaning."""
        await self.send(self._device_id, {PROCESS_ACTION: MieleActions.START})

    async def async_stop(self, **kwargs: Any) -> None:
        """Stop cleaning."""
        await self.send(self._device_id, {PROCESS_ACTION: MieleActions.STOP})

    async def async_pause(self, **kwargs: Any) -> None:
        """Pause cleaning."""
        await self.send(self._device_id, {PROCESS_ACTION: MieleActions.PAUSE})

    async def async_set_fan_speed(self, fan_speed: str, **kwargs: Any) -> None:
        """Set fan speed."""
        await self.send(self._device_id, {PROGRAM_ID: PROGRAM_MAP[fan_speed]})
