"""Support for Devialet speakers."""
from __future__ import annotations

from devialet.const import NORMAL_INPUTS

from homeassistant.components.media_player import (
    MediaPlayerEntity,
    MediaPlayerEntityFeature,
    MediaPlayerState,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_NAME
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity

from .const import DOMAIN, MANUFACTURER, SOUND_MODES
from .coordinator import DevialetCoordinator

SUPPORT_DEVIALET = (
    MediaPlayerEntityFeature.VOLUME_SET
    | MediaPlayerEntityFeature.VOLUME_MUTE
    | MediaPlayerEntityFeature.TURN_OFF
    | MediaPlayerEntityFeature.SELECT_SOURCE
    | MediaPlayerEntityFeature.SELECT_SOUND_MODE
)

DEVIALET_TO_HA_FEATURE_MAP = {
    "play": MediaPlayerEntityFeature.PLAY | MediaPlayerEntityFeature.STOP,
    "pause": MediaPlayerEntityFeature.PAUSE,
    "previous": MediaPlayerEntityFeature.PREVIOUS_TRACK,
    "next": MediaPlayerEntityFeature.NEXT_TRACK,
    "seek": MediaPlayerEntityFeature.SEEK,
}


async def async_setup_entry(
    hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
    """Set up the Devialet entry."""
    client = hass.data[DOMAIN][entry.entry_id]
    coordinator = DevialetCoordinator(hass, client)
    await coordinator.async_config_entry_first_refresh()

    async_add_entities([DevialetMediaPlayerEntity(coordinator, entry)])


class DevialetMediaPlayerEntity(
    CoordinatorEntity[DevialetCoordinator], MediaPlayerEntity
):
    """Devialet media player."""

    _attr_has_entity_name = True
    _attr_name = None

    def __init__(self, coordinator: DevialetCoordinator, entry: ConfigEntry) -> None:
        """Initialize the Devialet device."""
        self.coordinator = coordinator
        super().__init__(coordinator)

        self._attr_unique_id = str(entry.unique_id)
        self._attr_device_info = DeviceInfo(
            identifiers={(DOMAIN, self._attr_unique_id)},
            manufacturer=MANUFACTURER,
            model=self.coordinator.client.model,
            name=entry.data[CONF_NAME],
            sw_version=self.coordinator.client.version,
        )

    @callback
    def _handle_coordinator_update(self) -> None:
        """Handle updated data from the coordinator."""
        if not self.coordinator.client.is_available:
            self.async_write_ha_state()
            return

        self._attr_volume_level = self.coordinator.client.volume_level
        self._attr_is_volume_muted = self.coordinator.client.is_volume_muted
        self._attr_source_list = self.coordinator.client.source_list
        self._attr_sound_mode_list = sorted(SOUND_MODES)
        self._attr_media_artist = self.coordinator.client.media_artist
        self._attr_media_album_name = self.coordinator.client.media_album_name
        self._attr_media_artist = self.coordinator.client.media_artist
        self._attr_media_image_url = self.coordinator.client.media_image_url
        self._attr_media_duration = self.coordinator.client.media_duration
        self._attr_media_position = self.coordinator.client.current_position
        self._attr_media_position_updated_at = (
            self.coordinator.client.position_updated_at
        )
        self._attr_media_title = (
            self.coordinator.client.media_title
            if self.coordinator.client.media_title
            else self.source
        )
        self.async_write_ha_state()

    @property
    def state(self) -> MediaPlayerState | None:
        """Return the state of the device."""
        playing_state = self.coordinator.client.playing_state

        if not playing_state:
            return MediaPlayerState.IDLE
        if playing_state == "playing":
            return MediaPlayerState.PLAYING
        if playing_state == "paused":
            return MediaPlayerState.PAUSED
        return MediaPlayerState.ON

    @property
    def available(self) -> bool:
        """Return if the media player is available."""
        return self.coordinator.client.is_available

    @property
    def supported_features(self) -> MediaPlayerEntityFeature:
        """Flag media player features that are supported."""
        features = SUPPORT_DEVIALET

        if self.coordinator.client.source_state is None:
            return features

        if not self.coordinator.client.available_options:
            return features

        for option in self.coordinator.client.available_options:
            features |= DEVIALET_TO_HA_FEATURE_MAP.get(option, 0)
        return features

    @property
    def source(self) -> str | None:
        """Return the current input source."""
        source = self.coordinator.client.source

        for pretty_name, name in NORMAL_INPUTS.items():
            if source == name:
                return pretty_name
        return None

    @property
    def sound_mode(self) -> str | None:
        """Return the current sound mode."""
        if self.coordinator.client.equalizer is not None:
            sound_mode = self.coordinator.client.equalizer
        elif self.coordinator.client.night_mode:
            sound_mode = "night mode"
        else:
            return None

        for pretty_name, mode in SOUND_MODES.items():
            if sound_mode == mode:
                return pretty_name
        return None

    async def async_volume_up(self) -> None:
        """Volume up media player."""
        await self.coordinator.client.async_volume_up()

    async def async_volume_down(self) -> None:
        """Volume down media player."""
        await self.coordinator.client.async_volume_down()

    async def async_set_volume_level(self, volume: float) -> None:
        """Set volume level, range 0..1."""
        await self.coordinator.client.async_set_volume_level(volume)

    async def async_mute_volume(self, mute: bool) -> None:
        """Mute (true) or unmute (false) media player."""
        await self.coordinator.client.async_mute_volume(mute)

    async def async_media_play(self) -> None:
        """Play media player."""
        await self.coordinator.client.async_media_play()

    async def async_media_pause(self) -> None:
        """Pause media player."""
        await self.coordinator.client.async_media_pause()

    async def async_media_stop(self) -> None:
        """Pause media player."""
        await self.coordinator.client.async_media_stop()

    async def async_media_next_track(self) -> None:
        """Send the next track command."""
        await self.coordinator.client.async_media_next_track()

    async def async_media_previous_track(self) -> None:
        """Send the previous track command."""
        await self.coordinator.client.async_media_previous_track()

    async def async_media_seek(self, position: float) -> None:
        """Send seek command."""
        await self.coordinator.client.async_media_seek(position)

    async def async_select_sound_mode(self, sound_mode: str) -> None:
        """Send sound mode command."""
        for pretty_name, mode in SOUND_MODES.items():
            if sound_mode == pretty_name:
                if mode == "night mode":
                    await self.coordinator.client.async_set_night_mode(True)
                else:
                    await self.coordinator.client.async_set_night_mode(False)
                    await self.coordinator.client.async_set_equalizer(mode)

    async def async_turn_off(self) -> None:
        """Turn off media player."""
        await self.coordinator.client.async_turn_off()

    async def async_select_source(self, source: str) -> None:
        """Select input source."""
        await self.coordinator.client.async_select_source(source)