"""Support for Homekit switches."""

from dataclasses import dataclass
from typing import Any, override

from aiohomekit.model.characteristics import (
    Characteristic,
    CharacteristicsTypes,
    InUseValues,
    IsConfiguredValues,
)
from aiohomekit.model.services import Service, ServicesTypes

from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory, Platform
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.typing import ConfigType

from . import KNOWN_DEVICES
from .connection import HKDevice
from .entity import CharacteristicEntity, HomeKitEntity

OUTLET_IN_USE = "outlet_in_use"

ATTR_IN_USE = "in_use"
ATTR_IS_CONFIGURED = "is_configured"
ATTR_REMAINING_DURATION = "remaining_duration"


@dataclass(frozen=True)
class DeclarativeSwitchEntityDescription(SwitchEntityDescription):
    """Describes Homekit button."""

    true_value: bool = True
    false_value: bool = False


SWITCH_ENTITIES: dict[str, DeclarativeSwitchEntityDescription] = {
    CharacteristicsTypes.VENDOR_AQARA_PAIRING_MODE: DeclarativeSwitchEntityDescription(
        key=CharacteristicsTypes.VENDOR_AQARA_PAIRING_MODE,
        name="Pairing Mode",
        translation_key="pairing_mode",
        entity_category=EntityCategory.CONFIG,
    ),
    CharacteristicsTypes.VENDOR_AQARA_E1_PAIRING_MODE: (
        DeclarativeSwitchEntityDescription(
            key=CharacteristicsTypes.VENDOR_AQARA_E1_PAIRING_MODE,
            name="Pairing Mode",
            translation_key="pairing_mode",
            entity_category=EntityCategory.CONFIG,
        )
    ),
    CharacteristicsTypes.LOCK_PHYSICAL_CONTROLS: DeclarativeSwitchEntityDescription(
        key=CharacteristicsTypes.LOCK_PHYSICAL_CONTROLS,
        name="Lock Physical Controls",
        translation_key="lock_physical_controls",
        entity_category=EntityCategory.CONFIG,
    ),
    CharacteristicsTypes.MUTE: DeclarativeSwitchEntityDescription(
        key=CharacteristicsTypes.MUTE,
        name="Mute",
        translation_key="mute",
        entity_category=EntityCategory.CONFIG,
    ),
    CharacteristicsTypes.VENDOR_AIRVERSA_SLEEP_MODE: DeclarativeSwitchEntityDescription(
        key=CharacteristicsTypes.VENDOR_AIRVERSA_SLEEP_MODE,
        name="Sleep Mode",
        translation_key="sleep_mode",
        entity_category=EntityCategory.CONFIG,
    ),
    CharacteristicsTypes.AIRPLAY_ENABLE: DeclarativeSwitchEntityDescription(
        key=CharacteristicsTypes.AIRPLAY_ENABLE,
        name="AirPlay Enable",
        translation_key="airplay_enable",
        entity_category=EntityCategory.CONFIG,
    ),
}


class HomeKitSwitch(HomeKitEntity, SwitchEntity):
    """Representation of a Homekit switch."""

    @override
    def get_characteristic_types(self) -> list[str]:
        """Define the homekit characteristics the entity cares about."""
        return [CharacteristicsTypes.ON, CharacteristicsTypes.OUTLET_IN_USE]

    @property
    @override
    def is_on(self) -> bool:
        """Return true if device is on."""
        return self.service.value(CharacteristicsTypes.ON)

    @override
    async def async_turn_on(self, **kwargs: Any) -> None:
        """Turn the specified switch on."""
        await self.async_put_characteristics({CharacteristicsTypes.ON: True})

    @override
    async def async_turn_off(self, **kwargs: Any) -> None:
        """Turn the specified switch off."""
        await self.async_put_characteristics({CharacteristicsTypes.ON: False})

    @property
    @override
    def extra_state_attributes(self) -> dict[str, Any] | None:
        """Return the optional state attributes."""
        outlet_in_use = self.service.value(CharacteristicsTypes.OUTLET_IN_USE)
        if outlet_in_use is not None:
            return {OUTLET_IN_USE: outlet_in_use}
        return None


class HomeKitFaucet(HomeKitEntity, SwitchEntity):
    """Representation of a Homekit faucet."""

    @override
    def get_characteristic_types(self) -> list[str]:
        """Define the homekit characteristics the entity cares about."""
        return [CharacteristicsTypes.ACTIVE]

    @property
    @override
    def is_on(self) -> bool:
        """Return true if device is on."""
        return self.service.value(CharacteristicsTypes.ACTIVE)

    @override
    async def async_turn_on(self, **kwargs: Any) -> None:
        """Turn the specified faucet on."""
        await self.async_put_characteristics({CharacteristicsTypes.ACTIVE: True})

    @override
    async def async_turn_off(self, **kwargs: Any) -> None:
        """Turn the specified faucet off."""
        await self.async_put_characteristics({CharacteristicsTypes.ACTIVE: False})


class HomeKitValve(HomeKitEntity, SwitchEntity):
    """Represents a valve in an irrigation system."""

    _attr_translation_key = "valve"

    @override
    def get_characteristic_types(self) -> list[str]:
        """Define the homekit characteristics the entity cares about."""
        return [
            CharacteristicsTypes.ACTIVE,
            CharacteristicsTypes.IN_USE,
            CharacteristicsTypes.IS_CONFIGURED,
            CharacteristicsTypes.REMAINING_DURATION,
        ]

    @override
    async def async_turn_on(self, **kwargs: Any) -> None:
        """Turn the specified valve on."""
        await self.async_put_characteristics({CharacteristicsTypes.ACTIVE: True})

    @override
    async def async_turn_off(self, **kwargs: Any) -> None:
        """Turn the specified valve off."""
        await self.async_put_characteristics({CharacteristicsTypes.ACTIVE: False})

    @property
    @override
    def is_on(self) -> bool:
        """Return true if device is on."""
        return self.service.value(CharacteristicsTypes.ACTIVE)

    @property
    @override
    def extra_state_attributes(self) -> dict[str, Any]:
        """Return the optional state attributes."""
        attrs = {}

        in_use = self.service.value(CharacteristicsTypes.IN_USE)
        if in_use is not None:
            attrs[ATTR_IN_USE] = in_use == InUseValues.IN_USE

        is_configured = self.service.value(CharacteristicsTypes.IS_CONFIGURED)
        if is_configured is not None:
            attrs[ATTR_IS_CONFIGURED] = is_configured == IsConfiguredValues.CONFIGURED

        remaining = self.service.value(CharacteristicsTypes.REMAINING_DURATION)
        if remaining is not None:
            attrs[ATTR_REMAINING_DURATION] = remaining

        return attrs


class DeclarativeCharacteristicSwitch(CharacteristicEntity, SwitchEntity):
    """Representation of a Homekit switch backed by a single characteristic."""

    def __init__(
        self,
        conn: HKDevice,
        info: ConfigType,
        char: Characteristic,
        description: DeclarativeSwitchEntityDescription,
    ) -> None:
        """Initialise a HomeKit switch."""
        self.entity_description: DeclarativeSwitchEntityDescription = description
        super().__init__(conn, info, char)

    @property
    @override
    def name(self) -> str:
        """Return the name of the device if any."""
        if name := self.accessory.name:
            return f"{name} {self.entity_description.name}"
        return f"{self.entity_description.name}"

    @override
    def get_characteristic_types(self) -> list[str]:
        """Define the homekit characteristics the entity cares about."""
        return [self._char.type]

    @property
    @override
    def is_on(self) -> bool:
        """Return true if device is on."""
        return self._char.value == self.entity_description.true_value

    @override
    async def async_turn_on(self, **kwargs: Any) -> None:
        """Turn the specified switch on."""
        await self.async_put_characteristics(
            {self._char.type: self.entity_description.true_value}
        )

    @override
    async def async_turn_off(self, **kwargs: Any) -> None:
        """Turn the specified switch off."""
        await self.async_put_characteristics(
            {self._char.type: self.entity_description.false_value}
        )


ENTITY_TYPES: dict[str, type[HomeKitSwitch | HomeKitFaucet | HomeKitValve]] = {
    ServicesTypes.SWITCH: HomeKitSwitch,
    ServicesTypes.OUTLET: HomeKitSwitch,
    ServicesTypes.FAUCET: HomeKitFaucet,
    ServicesTypes.VALVE: HomeKitValve,
}


async def async_setup_entry(
    hass: HomeAssistant,
    config_entry: ConfigEntry,
    async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
    """Set up Homekit switches."""
    hkid: str = config_entry.data["AccessoryPairingID"]
    conn: HKDevice = hass.data[KNOWN_DEVICES][hkid]

    @callback
    def async_add_service(service: Service) -> bool:
        if not (entity_class := ENTITY_TYPES.get(service.type)):
            return False
        info = {"aid": service.accessory.aid, "iid": service.iid}
        entity: HomeKitSwitch | HomeKitFaucet | HomeKitValve = entity_class(conn, info)
        conn.async_migrate_unique_id(
            entity.old_unique_id, entity.unique_id, Platform.SWITCH
        )
        async_add_entities([entity])
        return True

    conn.add_listener(async_add_service)

    @callback
    def async_add_characteristic(char: Characteristic) -> bool:
        if not (description := SWITCH_ENTITIES.get(char.type)):
            return False

        info = {"aid": char.service.accessory.aid, "iid": char.service.iid}
        entity = DeclarativeCharacteristicSwitch(conn, info, char, description)
        conn.async_migrate_unique_id(
            entity.old_unique_id, entity.unique_id, Platform.SWITCH
        )
        async_add_entities([entity])
        return True

    conn.add_char_factory(async_add_characteristic)
