"""Support for switch platform for Hue resources (V2 only)."""

from collections.abc import Callable
from typing import Any, override

from aiohue.v2 import HueBridgeV2
from aiohue.v2.controllers.config import BehaviorInstance, BehaviorInstanceController
from aiohue.v2.controllers.events import EventType
from aiohue.v2.controllers.sensors import (
    LightLevel,
    LightLevelController,
    Motion,
    MotionController,
)
from aiohue.v2.models.behavior_instance import PresenceMimickingState
from aiohue.v2.models.behavior_script import BehaviorScriptCategory

from homeassistant.components.switch import (
    SwitchDeviceClass,
    SwitchEntity,
    SwitchEntityDescription,
)
from homeassistant.const import EntityCategory, Platform
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from .bridge import HueConfigEntry
from .const import DOMAIN
from .v2.entity import HueBaseEntity


async def async_setup_entry(
    hass: HomeAssistant,
    config_entry: HueConfigEntry,
    async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
    """Set up Hue switch platform from Hue resources."""
    bridge = config_entry.runtime_data
    api: HueBridgeV2 = bridge.api

    if bridge.api_version == 1:
        # should not happen, but just in case
        raise NotImplementedError("Switch support is only available for V2 bridges")

    @callback
    def register_items(
        controller: BehaviorInstanceController
        | LightLevelController
        | MotionController,
        switch_class: type[
            HueBehaviorInstanceEnabledEntity
            | HueLightSensorEnabledEntity
            | HueMotionSensorEnabledEntity
        ],
        resource_filter: Callable[[Any], bool] | None = None,
    ):
        @callback
        def async_add_entity(
            event_type: EventType, resource: BehaviorInstance | LightLevel | Motion
        ) -> None:
            """Add entity from Hue resource."""
            if resource_filter is not None and not resource_filter(resource):
                return
            async_add_entities([switch_class(bridge, controller, resource)])

        # add all current items in controller
        for item in controller:
            async_add_entity(EventType.RESOURCE_ADDED, item)

        # register listener for new items only
        config_entry.async_on_unload(
            controller.subscribe(
                async_add_entity, event_filter=EventType.RESOURCE_ADDED
            )
        )

    @callback
    def is_user_automation(resource: BehaviorInstance) -> bool:
        """Return if the behavior instance is an automation from the Hue app.

        Anything else is device configuration, which the bridge keeps running
        even after it accepts switching it off. Categories we do not recognise
        are skipped too, better no switch than one that does nothing.
        """
        script = api.config.behavior_script.get(resource.script_id)
        return (
            script is not None
            and script.metadata.category is BehaviorScriptCategory.AUTOMATION
        )

    # clean up entities previously created for internal behavior instances
    entity_registry = er.async_get(hass)
    for resource in api.config.behavior_instance:
        if is_user_automation(resource):
            continue
        if entity_id := entity_registry.async_get_entity_id(
            Platform.SWITCH, DOMAIN, resource.id
        ):
            entity_registry.async_remove(entity_id)

    # setup for each switch-type hue resource
    register_items(api.sensors.motion, HueMotionSensorEnabledEntity)
    register_items(api.sensors.light_level, HueLightSensorEnabledEntity)
    register_items(
        api.config.behavior_instance,
        HueBehaviorInstanceEnabledEntity,
        is_user_automation,
    )


class HueResourceEnabledEntity(HueBaseEntity, SwitchEntity):
    """Represent a Switch entity from a Hue resource that toggles."""

    controller: BehaviorInstanceController | LightLevelController | MotionController
    resource: BehaviorInstance | LightLevel | Motion

    entity_description = SwitchEntityDescription(
        key="sensing_service_enabled",
        device_class=SwitchDeviceClass.SWITCH,
        entity_category=EntityCategory.CONFIG,
        has_entity_name=True,
    )

    @property
    @override
    def is_on(self) -> bool:
        """Return true if the switch is on."""
        return self.resource.enabled

    @override
    async def async_turn_on(self, **kwargs: Any) -> None:
        """Turn the entity on."""
        await self.bridge.async_request_call(
            self.controller.set_enabled, self.resource.id, enabled=True
        )

    @override
    async def async_turn_off(self, **kwargs: Any) -> None:
        """Turn the entity on."""
        await self.bridge.async_request_call(
            self.controller.set_enabled, self.resource.id, enabled=False
        )


class HueBehaviorInstanceEnabledEntity(HueResourceEnabledEntity):
    """Representation of a Switch entity to enable/disable a Hue Behavior Instance.

    Automations that the Hue app runs with a play button, such as mimic
    presence, are started and stopped. All others are enabled and disabled.
    """

    controller: BehaviorInstanceController
    resource: BehaviorInstance

    entity_description = SwitchEntityDescription(
        key="behavior_instance",
        device_class=SwitchDeviceClass.SWITCH,
        entity_category=EntityCategory.CONFIG,
    )

    @property
    @override
    def name(self) -> str:
        """Return name for this entity."""
        return f"Automation: {self.resource.metadata.name}"

    @property
    @override
    def is_on(self) -> bool:
        """Return true if the switch is on."""
        if (run_state := self.resource.presence_mimicking_state) is not None:
            return run_state is PresenceMimickingState.STARTED
        return self.resource.enabled

    @override
    async def async_turn_on(self, **kwargs: Any) -> None:
        """Turn the entity on."""
        if self.resource.presence_mimicking_state is None:
            await super().async_turn_on(**kwargs)
            return
        await self.bridge.async_request_call(self.controller.start, self.resource.id)

    @override
    async def async_turn_off(self, **kwargs: Any) -> None:
        """Turn the entity off."""
        if self.resource.presence_mimicking_state is None:
            await super().async_turn_off(**kwargs)
            return
        await self.bridge.async_request_call(self.controller.stop, self.resource.id)


class HueMotionSensorEnabledEntity(HueResourceEnabledEntity):
    """Representation of a Switch entity to enable/disable a Hue motion sensor."""

    entity_description = SwitchEntityDescription(
        key="motion_sensor_enabled",
        device_class=SwitchDeviceClass.SWITCH,
        entity_category=EntityCategory.CONFIG,
        has_entity_name=True,
        translation_key="motion_sensor_enabled",
    )


class HueLightSensorEnabledEntity(HueResourceEnabledEntity):
    """Representation of a Switch entity to enable/disable a Hue light sensor."""

    entity_description = SwitchEntityDescription(
        key="light_sensor_enabled",
        device_class=SwitchDeviceClass.SWITCH,
        entity_category=EntityCategory.CONFIG,
        has_entity_name=True,
        translation_key="light_sensor_enabled",
    )
