"""Support for Dreame Vacuum switches."""

from __future__ import annotations

from typing import Any
from collections.abc import Callable
from dataclasses import dataclass

from homeassistant.components.switch import (
    SwitchEntity,
    SwitchEntityDescription,
)
from homeassistant.config_entries import ConfigEntry

from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity import EntityCategory
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.exceptions import HomeAssistantError

from .const import DOMAIN

from .coordinator import DreameVacuumDataUpdateCoordinator
from .entity import DreameVacuumEntity, DreameVacuumEntityDescription
from .dreame import DreameVacuumProperty


@dataclass
class DreameVacuumSwitchEntityDescription(DreameVacuumEntityDescription, SwitchEntityDescription):
    """Describes Dreame Vacuum Switch entity."""

    set_fn: Callable[[object, int]] = None


SWITCHES: tuple[DreameVacuumSwitchEntityDescription, ...] = (
    DreameVacuumSwitchEntityDescription(
        property_key=DreameVacuumProperty.RESUME_CLEANING,
        icon="mdi:play-pause",
        entity_category=EntityCategory.CONFIG,
    ),
    DreameVacuumSwitchEntityDescription(
        property_key=DreameVacuumProperty.CARPET_BOOST,
        icon_fn=lambda value, device: "mdi:upload-off" if value == 0 else "mdi:upload",
        entity_category=EntityCategory.CONFIG,
    ),
    DreameVacuumSwitchEntityDescription(
        property_key=DreameVacuumProperty.OBSTACLE_AVOIDANCE,
        icon_fn=lambda value, device: "mdi:video-3d-off" if value == 0 else "mdi:video-3d",
        entity_category=EntityCategory.CONFIG,
    ),
    DreameVacuumSwitchEntityDescription(
        property_key=DreameVacuumProperty.CUSTOMIZED_CLEANING,
        icon="mdi:home-search",
    ),
    DreameVacuumSwitchEntityDescription(
        property_key=DreameVacuumProperty.CHILD_LOCK,
        icon_fn=lambda value, device: "mdi:lock-off" if value == 0 else "mdi:lock",
        entity_category=EntityCategory.CONFIG,
    ),
    DreameVacuumSwitchEntityDescription(
        property_key=DreameVacuumProperty.TIGHT_MOPPING,
        icon="mdi:heating-coil",
        entity_category=EntityCategory.CONFIG,
    ),
    DreameVacuumSwitchEntityDescription(
        property_key=DreameVacuumProperty.DND,
        name="DND",
        icon_fn=lambda value, device: "mdi:minus-circle-off-outline" if not value else "mdi:minus-circle-outline",
        format_fn=lambda value, device: bool(value),
        entity_category=EntityCategory.CONFIG,
    ),
    DreameVacuumSwitchEntityDescription(
        property_key=DreameVacuumProperty.MULTI_FLOOR_MAP,
        icon_fn=lambda value, device: "mdi:layers-off" if value == 0 else "mdi:layers",
        entity_category=EntityCategory.CONFIG,
        set_fn=lambda device, value: device.set_multi_map(value),
        # exists_fn=lambda description, device: bool(
        #    DreameVacuumEntityDescription().exists_fn(description, device)
        #    and device.status.lidar_navigation
        # ),
    ),
    DreameVacuumSwitchEntityDescription(
        property_key=DreameVacuumProperty.AUTO_DUST_COLLECTING,
        icon_fn=lambda value, device: "mdi:autorenew-off" if value == 0 else "mdi:autorenew",
        entity_category=EntityCategory.CONFIG,
    ),
    DreameVacuumSwitchEntityDescription(
        property_key=DreameVacuumProperty.CARPET_RECOGNITION,
        icon="mdi:rug",
        entity_category=EntityCategory.CONFIG,
        set_fn=lambda device, value: device.set_carpet_recognition(value),
    ),
    DreameVacuumSwitchEntityDescription(
        property_key=DreameVacuumProperty.SELF_CLEAN,
        icon="mdi:water-sync",
        entity_category=EntityCategory.CONFIG,
    ),
    DreameVacuumSwitchEntityDescription(
        property_key=DreameVacuumProperty.WATER_ELECTROLYSIS,
        icon="mdi:lightning-bolt",
        entity_category=EntityCategory.CONFIG,
    ),
    DreameVacuumSwitchEntityDescription(
        property_key=DreameVacuumProperty.AUTO_WATER_REFILLING,
        icon="mdi:water-boiler-auto",
        entity_category=EntityCategory.CONFIG,
    ),
    DreameVacuumSwitchEntityDescription(
        property_key=DreameVacuumProperty.INTELLIGENT_RECOGNITION,
        icon="mdi:wifi-marker",
        entity_category=EntityCategory.CONFIG,
        exists_fn=lambda description, device: device.status.auto_switch_settings_available
        and DreameVacuumEntityDescription().exists_fn(description, device),
        available_fn=lambda device: device.status.multi_map,
    ),
    DreameVacuumSwitchEntityDescription(
        key="auto_drying",
        icon="mdi:hair-dryer",
        entity_category=EntityCategory.CONFIG,
        value_fn=lambda value, device: bool(device.status.auto_drying),
        exists_fn=lambda description, device: device.status.auto_drying is not None,
        set_fn=lambda device, value: device.set_auto_drying(value),
    ),
    DreameVacuumSwitchEntityDescription(
        property_key=DreameVacuumProperty.CARPET_AVOIDANCE,
        icon="mdi:close-box-outline",
        entity_category=EntityCategory.CONFIG,
        value_fn=lambda value, device: device.status.carpet_avoidance,
        format_fn=lambda value, device: 1 if value else 2,
    ),
    DreameVacuumSwitchEntityDescription(
        property_key=DreameVacuumProperty.AUTO_ADD_DETERGENT,
        icon="mdi:chart-bubble",
        value_fn=lambda value, device: device.status.auto_add_detergent,
        entity_category=EntityCategory.CONFIG,
        format_fn=lambda value, device: int(value),
    ),
    DreameVacuumSwitchEntityDescription(
        property_key=DreameVacuumProperty.MAP_SAVING,
        icon="mdi:map-legend",
        entity_category=EntityCategory.CONFIG,
        format_fn=lambda value, device: int(value),
    ),
    DreameVacuumSwitchEntityDescription(
        property_key=DreameVacuumProperty.AUTO_MOUNT_MOP,
        icon="mdi:google-circles-group",
        entity_category=EntityCategory.CONFIG,
        format_fn=lambda value, device: int(value),
    ),
    DreameVacuumSwitchEntityDescription(
        key="cleaning_sequence",
        icon="mdi:order-numeric-ascending",
        value_fn=lambda value, device: device.status.custom_order,
        exists_fn=lambda description, device: device.status.customized_cleaning_available
        and device.status.map_available,
        available_fn=lambda device: bool(
            not device.status.started
            and device.status.has_saved_map
            and device.status.segments
            and next(iter(device.status.segments.values())).order is not None
        ),
        set_fn=lambda device, value: device.set_segment_order(
            next(iter(device.status.segments.values())).segment_id, value
        ),
        format_fn=lambda value, device: int(value),
        entity_category=EntityCategory.CONFIG,
    ),
    DreameVacuumSwitchEntityDescription(
        name="AI Obstacle Detection",
        key="ai_obstacle_detection",
        icon_fn=lambda value, device: "mdi:robot-off" if not value else "mdi:robot",
        value_fn=lambda value, device: device.status.ai_obstacle_detection,
        exists_fn=lambda description, device: device.status.ai_detection_available,
        set_fn=lambda device, value: device.set_ai_obstacle_detection(value),
        entity_category=EntityCategory.CONFIG,
    ),
    DreameVacuumSwitchEntityDescription(
        name="AI Image Upload",
        key="ai_obstacle_image_upload",
        icon="mdi:cloud-upload",
        value_fn=lambda value, device: device.status.obstacle_image_upload,
        exists_fn=lambda description, device: bool(
            device.status.ai_detection_available and device.status.obstacle_image_upload is not None
        ),
        available_fn=lambda device: device.status.ai_obstacle_detection,
        set_fn=lambda device, value: device.set_obstacle_image_upload(value),
        entity_category=EntityCategory.CONFIG,
    ),
    DreameVacuumSwitchEntityDescription(
        name="AI Obstacle Picture",
        key="ai_obstacle_picture",
        icon_fn=lambda value, device: "mdi:camera-off" if not value else "mdi:camera",
        value_fn=lambda value, device: device.status.obstacle_picture,
        exists_fn=lambda description, device: device.status.ai_detection_available
        and device.status.obstacle_picture is not None,
        available_fn=lambda device: device.status.ai_obstacle_detection,
        set_fn=lambda device, value: device.set_obstacle_picture(value),
        entity_category=EntityCategory.CONFIG,
    ),
    DreameVacuumSwitchEntityDescription(
        name="AI Pet Detection",
        key="ai_pet_detection",
        icon_fn=lambda value, device: "mdi:dog-side-off" if not value else "mdi:dog-side",
        value_fn=lambda value, device: device.status.pet_detection,
        exists_fn=lambda description, device: bool(
            device.status.ai_detection_available and device.status.pet_detection is not None
        ),
        available_fn=lambda device: device.status.ai_obstacle_detection,
        set_fn=lambda device, value: device.set_pet_detection(value),
        entity_category=EntityCategory.CONFIG,
    ),
    DreameVacuumSwitchEntityDescription(
        name="AI Human Detection",
        key="ai_human_detection",
        icon_fn=lambda value, device: "mdi:account-off" if not value else "mdi:account",
        value_fn=lambda value, device: device.status.human_detection,
        exists_fn=lambda description, device: bool(
            device.status.ai_detection_available and device.status.human_detection is not None
        ),
        available_fn=lambda device: device.status.ai_obstacle_detection,
        set_fn=lambda device, value: device.set_human_detection(value),
        entity_category=EntityCategory.CONFIG,
    ),
    DreameVacuumSwitchEntityDescription(
        name="AI Furniture Detection",
        key="ai_furniture_detection",
        icon="mdi:table-furniture",
        value_fn=lambda value, device: device.status.furniture_detection,
        exists_fn=lambda description, device: bool(
            device.status.ai_detection_available and device.status.furniture_detection is not None
        ),
        available_fn=lambda device: device.status.ai_obstacle_detection,
        set_fn=lambda device, value: device.set_furniture_detection(value),
        entity_category=EntityCategory.CONFIG,
    ),
    DreameVacuumSwitchEntityDescription(
        name="AI Fluid Detection",
        key="ai_fluid_detection",
        icon_fn=lambda value, device: "mdi:water-off-outline" if not value else "mdi:water-outline",
        value_fn=lambda value, device: device.status.fluid_detection,
        exists_fn=lambda description, device: bool(
            device.status.ai_detection_available and device.status.fluid_detection is not None
        ),
        available_fn=lambda device: device.status.ai_obstacle_detection,
        set_fn=lambda device, value: device.set_fluid_detection(value),
        entity_category=EntityCategory.CONFIG,
    ),
    DreameVacuumSwitchEntityDescription(
        key="fill_light",
        icon_fn=lambda value, device: "mdi:brightness-2" if not value else "mdi:brightness-auto",
        value_fn=lambda value, device: device.status.fill_light,
        exists_fn=lambda description, device: bool(
            device.status.auto_switch_settings_available and device.status.fill_light is not None
        ),
        set_fn=lambda device, value: device.set_fill_light(value),
        entity_category=EntityCategory.CONFIG,
    ),
    DreameVacuumSwitchEntityDescription(
        key="collision_avoidance",
        icon_fn=lambda value, device: "mdi:sign-direction-remove" if not value else "mdi:sign-direction",
        value_fn=lambda value, device: device.status.collision_avoidance,
        exists_fn=lambda description, device: bool(
            device.status.auto_switch_settings_available and device.status.collision_avoidance is not None
        ),
        set_fn=lambda device, value: device.set_collision_avoidance(value),
        entity_category=EntityCategory.CONFIG,
    ),
    DreameVacuumSwitchEntityDescription(
        key="stain_avoidance",
        icon="mdi:liquid-spot",
        value_fn=lambda value, device: bool(device.status.stain_avoidance == 2),
        exists_fn=lambda description, device: device.status.ai_detection_available
        and device.status.auto_switch_settings_available
        and device.status.stain_avoidance is not None,
        set_fn=lambda device, value: device.set_stain_avoidance(2 if value else 1),
        available_fn=lambda device: device.status.fluid_detection,
        entity_category=EntityCategory.CONFIG,
    ),
)


async def async_setup_entry(
    hass: HomeAssistant,
    entry: ConfigEntry,
    async_add_entities: AddEntitiesCallback,
) -> None:
    """Set up Dreame Vacuum switch based on a config entry."""
    coordinator: DreameVacuumDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
    async_add_entities(
        DreameVacuumSwitchEntity(coordinator, description)
        for description in SWITCHES
        if description.exists_fn(description, coordinator.device)
    )


class DreameVacuumSwitchEntity(DreameVacuumEntity, SwitchEntity):
    """Defines a Dreame Vacuum Switch entity."""

    entity_description: DreameVacuumSwitchEntityDescription

    def __init__(
        self,
        coordinator: DreameVacuumDataUpdateCoordinator,
        description: DreameVacuumSwitchEntityDescription,
    ) -> None:
        """Initialize a Dreame Vacuum switch entity."""
        super().__init__(coordinator, description)
        self._attr_is_on = bool(self.native_value)

    @callback
    def _handle_coordinator_update(self) -> None:
        self._attr_is_on = bool(self.native_value)
        super()._handle_coordinator_update()

    async def async_turn_off(self, **kwargs: Any) -> None:
        """Turn off the Dreame Vacuum sync receive switch."""
        await self.async_set_state(0)

    async def async_turn_on(self, **kwargs: Any) -> None:
        """Turn on the Dreame Vacuum sync receive switch."""
        await self.async_set_state(1)

    async def async_set_state(self, state) -> None:
        """Turn on or off the Dreame Vacuum sync receive switch."""
        if not self.available:
            raise HomeAssistantError("Entity unavailable")

        value = int(state)
        if self.entity_description.format_fn is not None:
            value = self.entity_description.format_fn(state, self.device)

        if self.entity_description.property_key is not None:
            await self._try_command(
                "Unable to call: %s",
                self.device.set_property,
                self.entity_description.property_key,
                value,
            )
        elif self.entity_description.set_fn is not None:
            await self._try_command("Unable to call: %s", self.entity_description.set_fn, self.device, value)
