"""Intents for the light integration."""
from __future__ import annotations

import asyncio
import logging
from typing import Any

import voluptuous as vol

from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_ON
from homeassistant.core import HomeAssistant
from homeassistant.helpers import area_registry as ar, config_validation as cv, intent
import homeassistant.util.color as color_util

from . import (
    ATTR_BRIGHTNESS_PCT,
    ATTR_RGB_COLOR,
    ATTR_SUPPORTED_COLOR_MODES,
    DOMAIN,
    brightness_supported,
    color_supported,
)

_LOGGER = logging.getLogger(__name__)

INTENT_SET = "HassLightSet"


async def async_setup_intents(hass: HomeAssistant) -> None:
    """Set up the light intents."""
    intent.async_register(hass, SetIntentHandler())


class SetIntentHandler(intent.IntentHandler):
    """Handle set color intents."""

    intent_type = INTENT_SET
    slot_schema = {
        vol.Any("name", "area"): cv.string,
        vol.Optional("domain"): vol.All(cv.ensure_list, [cv.string]),
        vol.Optional("device_class"): vol.All(cv.ensure_list, [cv.string]),
        vol.Optional("color"): color_util.color_name_to_rgb,
        vol.Optional("brightness"): vol.All(vol.Coerce(int), vol.Range(0, 100)),
    }

    async def async_handle(self, intent_obj: intent.Intent) -> intent.IntentResponse:
        """Handle the hass intent."""
        hass = intent_obj.hass
        service_data: dict[str, Any] = {}
        slots = self.async_validate_slots(intent_obj.slots)

        name: str | None = slots.get("name", {}).get("value")
        if name == "all":
            # Don't match on name if targeting all entities
            name = None

        # Look up area first to fail early
        area_name = slots.get("area", {}).get("value")
        area: ar.AreaEntry | None = None
        if area_name is not None:
            areas = ar.async_get(hass)
            area = areas.async_get_area(area_name) or areas.async_get_area_by_name(
                area_name
            )
            if area is None:
                raise intent.IntentHandleError(f"No area named {area_name}")

        # Optional domain/device class filters.
        # Convert to sets for speed.
        domains: set[str] | None = None
        device_classes: set[str] | None = None

        if "domain" in slots:
            domains = set(slots["domain"]["value"])

        if "device_class" in slots:
            device_classes = set(slots["device_class"]["value"])

        states = list(
            intent.async_match_states(
                hass,
                name=name,
                area=area,
                domains=domains,
                device_classes=device_classes,
            )
        )

        if not states:
            raise intent.IntentHandleError("No entities matched")

        if "color" in slots:
            service_data[ATTR_RGB_COLOR] = slots["color"]["value"]

        if "brightness" in slots:
            service_data[ATTR_BRIGHTNESS_PCT] = slots["brightness"]["value"]

        response = intent_obj.create_response()
        needs_brightness = ATTR_BRIGHTNESS_PCT in service_data
        needs_color = ATTR_RGB_COLOR in service_data

        success_results: list[intent.IntentResponseTarget] = []
        failed_results: list[intent.IntentResponseTarget] = []
        service_coros = []

        if area is not None:
            success_results.append(
                intent.IntentResponseTarget(
                    type=intent.IntentResponseTargetType.AREA,
                    name=area.name,
                    id=area.id,
                )
            )

        for state in states:
            target = intent.IntentResponseTarget(
                type=intent.IntentResponseTargetType.ENTITY,
                name=state.name,
                id=state.entity_id,
            )

            # Test brightness/color
            supported_color_modes = state.attributes.get(ATTR_SUPPORTED_COLOR_MODES)
            if (needs_color and not color_supported(supported_color_modes)) or (
                needs_brightness and not brightness_supported(supported_color_modes)
            ):
                failed_results.append(target)
                continue

            service_coros.append(
                hass.services.async_call(
                    DOMAIN,
                    SERVICE_TURN_ON,
                    {**service_data, ATTR_ENTITY_ID: state.entity_id},
                    context=intent_obj.context,
                )
            )
            success_results.append(target)

        # Handle service calls in parallel.
        await asyncio.gather(*service_coros)

        response.async_set_results(
            success_results=success_results, failed_results=failed_results
        )

        return response
