"""Support for Cambridge Audio select entities."""

from collections.abc import Awaitable, Callable
from dataclasses import dataclass, field
from typing import override

from aiostreammagic import StreamMagicClient
from aiostreammagic.models import ControlBusMode, DisplayBrightness

from homeassistant.components.select import SelectEntity, SelectEntityDescription
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from . import CambridgeAudioConfigEntry
from .entity import CambridgeAudioEntity, command

PARALLEL_UPDATES = 0


@dataclass(frozen=True, kw_only=True)
class CambridgeAudioSelectEntityDescription(SelectEntityDescription):
    """Describes Cambridge Audio select entity."""

    options_fn: Callable[[StreamMagicClient], list[str]] = field(default=lambda _: [])
    load_fn: Callable[[StreamMagicClient], bool] = field(default=lambda _: True)
    value_fn: Callable[[StreamMagicClient], str | None]
    set_value_fn: Callable[[StreamMagicClient, str], Awaitable[None]]


async def _audio_output_set_value_fn(client: StreamMagicClient, value: str) -> None:
    """Set the audio output using the display name."""
    audio_output_id = next(
        (output.id for output in client.audio_output.outputs if value == output.name),
        None,
    )
    assert audio_output_id is not None
    await client.set_audio_output(audio_output_id)


def _audio_output_value_fn(client: StreamMagicClient) -> str | None:
    """Convert the current audio output id to name."""
    return next(
        (
            output.name
            for output in client.audio_output.outputs
            if client.state.audio_output == output.id
        ),
        None,
    )


CONTROL_ENTITIES: tuple[CambridgeAudioSelectEntityDescription, ...] = (
    CambridgeAudioSelectEntityDescription(
        key="display_brightness",
        translation_key="display_brightness",
        options=[
            DisplayBrightness.BRIGHT.value,
            DisplayBrightness.DIM.value,
            DisplayBrightness.OFF.value,
        ],
        entity_category=EntityCategory.CONFIG,
        load_fn=lambda client: client.display.brightness != DisplayBrightness.NONE,
        value_fn=lambda client: client.display.brightness,
        set_value_fn=lambda client, value: client.set_display_brightness(
            DisplayBrightness(value)
        ),
    ),
    CambridgeAudioSelectEntityDescription(
        key="audio_output",
        translation_key="audio_output",
        entity_category=EntityCategory.CONFIG,
        options_fn=lambda client: [
            output.name for output in client.audio_output.outputs
        ],
        load_fn=lambda client: len(client.audio_output.outputs) > 0,
        value_fn=_audio_output_value_fn,
        set_value_fn=_audio_output_set_value_fn,
    ),
    CambridgeAudioSelectEntityDescription(
        key="control_bus_mode",
        translation_key="control_bus_mode",
        options=[
            ControlBusMode.AMPLIFIER.value,
            ControlBusMode.RECEIVER.value,
            ControlBusMode.OFF.value,
        ],
        entity_category=EntityCategory.CONFIG,
        value_fn=lambda client: client.state.control_bus,
        set_value_fn=lambda client, value: client.set_control_bus_mode(
            ControlBusMode(value)
        ),
    ),
)


async def async_setup_entry(
    hass: HomeAssistant,
    entry: CambridgeAudioConfigEntry,
    async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
    """Set up Cambridge Audio select entities based on a config entry."""

    client: StreamMagicClient = entry.runtime_data
    entities: list[CambridgeAudioSelect] = [
        CambridgeAudioSelect(client, description)
        for description in CONTROL_ENTITIES
        if description.load_fn(client)
    ]
    async_add_entities(entities)


class CambridgeAudioSelect(CambridgeAudioEntity, SelectEntity):
    """Defines a Cambridge Audio select entity."""

    entity_description: CambridgeAudioSelectEntityDescription

    def __init__(
        self,
        client: StreamMagicClient,
        description: CambridgeAudioSelectEntityDescription,
    ) -> None:
        """Initialize Cambridge Audio select."""
        super().__init__(client)
        self.entity_description = description
        self._attr_unique_id = f"{client.info.unit_id}-{description.key}"
        options_fn = description.options_fn(client)
        if options_fn:
            self._attr_options = options_fn

    @property
    @override
    def current_option(self) -> str | None:
        """Return the state of the select."""
        return self.entity_description.value_fn(self.client)

    @command
    @override
    async def async_select_option(self, option: str) -> None:
        """Change the selected option."""
        await self.entity_description.set_value_fn(self.client, option)
