"""Provides device automations for Philips Hue events."""
from __future__ import annotations

from typing import TYPE_CHECKING

from aiohue.v2.models.button import ButtonEvent
from aiohue.v2.models.resource import ResourceTypes
import voluptuous as vol

from homeassistant.components import persistent_notification
from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA
from homeassistant.components.homeassistant.triggers import event as event_trigger
from homeassistant.const import (
    CONF_DEVICE_ID,
    CONF_DOMAIN,
    CONF_PLATFORM,
    CONF_TYPE,
    CONF_UNIQUE_ID,
)
from homeassistant.core import CALLBACK_TYPE, callback
from homeassistant.helpers.device_registry import DeviceEntry
from homeassistant.helpers.typing import ConfigType

from ..const import ATTR_HUE_EVENT, CONF_SUBTYPE, DOMAIN

if TYPE_CHECKING:
    from aiohue.v2 import HueBridgeV2

    from homeassistant.components.automation import (
        AutomationActionType,
        AutomationTriggerInfo,
    )

    from ..bridge import HueBridge

TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend(
    {
        vol.Required(CONF_TYPE): str,
        vol.Required(CONF_SUBTYPE): vol.Union(int, str),
        vol.Optional(CONF_UNIQUE_ID): str,
    }
)

DEFAULT_BUTTON_EVENT_TYPES = (
    # all except `DOUBLE_SHORT_RELEASE`
    ButtonEvent.INITIAL_PRESS,
    ButtonEvent.REPEAT,
    ButtonEvent.SHORT_RELEASE,
    ButtonEvent.LONG_RELEASE,
)

DEVICE_SPECIFIC_EVENT_TYPES = {
    # device specific overrides of specific supported button events
    "Hue tap switch": (ButtonEvent.INITIAL_PRESS,),
}


def check_invalid_device_trigger(
    bridge: HueBridge,
    config: ConfigType,
    device_entry: DeviceEntry,
    automation_info: AutomationTriggerInfo | None = None,
):
    """Check automation config for deprecated format."""
    # NOTE: Remove this check after 2022.6
    if isinstance(config["subtype"], int):
        return
    # found deprecated V1 style trigger, notify the user that it should be adjusted
    msg = (
        f"Incompatible device trigger detected for "
        f"[{device_entry.name}](/config/devices/device/{device_entry.id}) "
        "Please manually fix the outdated automation(s) once to fix this issue."
    )
    if automation_info:
        automation_id = automation_info["variables"]["this"]["attributes"]["id"]  # type: ignore[index]
        msg += f"\n\n[Check it out](/config/automation/edit/{automation_id})."
    persistent_notification.async_create(
        bridge.hass,
        msg,
        title="Outdated device trigger found",
        notification_id=f"hue_trigger_{device_entry.id}",
    )


async def async_validate_trigger_config(
    bridge: "HueBridge",
    device_entry: DeviceEntry,
    config: ConfigType,
):
    """Validate config."""
    config = TRIGGER_SCHEMA(config)
    check_invalid_device_trigger(bridge, config, device_entry)
    return config


async def async_attach_trigger(
    bridge: "HueBridge",
    device_entry: DeviceEntry,
    config: ConfigType,
    action: "AutomationActionType",
    automation_info: "AutomationTriggerInfo",
) -> CALLBACK_TYPE:
    """Listen for state changes based on configuration."""
    hass = bridge.hass
    event_config = event_trigger.TRIGGER_SCHEMA(
        {
            event_trigger.CONF_PLATFORM: "event",
            event_trigger.CONF_EVENT_TYPE: ATTR_HUE_EVENT,
            event_trigger.CONF_EVENT_DATA: {
                CONF_DEVICE_ID: config[CONF_DEVICE_ID],
                CONF_TYPE: config[CONF_TYPE],
                CONF_SUBTYPE: config[CONF_SUBTYPE],
            },
        }
    )
    check_invalid_device_trigger(bridge, config, device_entry, automation_info)
    return await event_trigger.async_attach_trigger(
        hass, event_config, action, automation_info, platform_type="device"
    )


async def async_get_triggers(bridge: "HueBridge", device_entry: DeviceEntry):
    """Return device triggers for device on `v2` bridge."""
    api: HueBridgeV2 = bridge.api

    # Get Hue device id from device identifier
    hue_dev_id = get_hue_device_id(device_entry)
    # extract triggers from all button resources of this Hue device
    triggers = []
    model_id = api.devices[hue_dev_id].product_data.product_name
    for resource in api.devices.get_sensors(hue_dev_id):
        if resource.type != ResourceTypes.BUTTON:
            continue
        for event_type in DEVICE_SPECIFIC_EVENT_TYPES.get(
            model_id, DEFAULT_BUTTON_EVENT_TYPES
        ):
            triggers.append(
                {
                    CONF_DEVICE_ID: device_entry.id,
                    CONF_DOMAIN: DOMAIN,
                    CONF_PLATFORM: "device",
                    CONF_TYPE: event_type.value,
                    CONF_SUBTYPE: resource.metadata.control_id,
                    CONF_UNIQUE_ID: resource.id,
                }
            )
    return triggers


@callback
def get_hue_device_id(device_entry: DeviceEntry) -> str | None:
    """Get Hue device id from device entry."""
    return next(
        (
            identifier[1]
            for identifier in device_entry.identifiers
            if identifier[0] == DOMAIN
            and ":" not in identifier[1]  # filter out v1 mac id
        ),
        None,
    )
