"""Support for media players through the SmartThings cloud API."""

from typing import Any, override

from pysmartthings import Attribute, Capability, Category, Command, SmartThings

from homeassistant.components.media_player import (
    MediaPlayerDeviceClass,
    MediaPlayerEntity,
    MediaPlayerEntityFeature,
    MediaPlayerState,
    RepeatMode,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from . import FullDevice, SmartThingsConfigEntry
from .const import MAIN
from .entity import SmartThingsEntity

MEDIA_SOURCE_ID_TO_HA_KEY: dict[str, str] = {
    "AM": "am",
    "BT": "bluetooth",
    "CD": "cd",
    "D.IN": "digital_input",
    "HDMI": "hdmi",
    "HDMI1": "hdmi1",
    "HDMI2": "hdmi2",
    "HDMI3": "hdmi3",
    "HDMI4": "hdmi4",
    "HDMI5": "hdmi5",
    "HDMI6": "hdmi6",
    "USB": "usb",
    "WIFI": "wifi",
    "digitalTv": "digital_tv",
    "dtv": "digital_tv",
    "melon": "melon",
    "youtube": "youtube",
}

MEDIA_PLAYER_CAPABILITIES = (
    Capability.AUDIO_MUTE,
    Capability.AUDIO_VOLUME,
)

CONTROLLABLE_SOURCES = ["bluetooth", "wifi"]

DEVICE_CLASS_MAP: dict[Category | str, MediaPlayerDeviceClass] = {
    Category.NETWORK_AUDIO: MediaPlayerDeviceClass.SPEAKER,
    Category.SPEAKER: MediaPlayerDeviceClass.SPEAKER,
    Category.TELEVISION: MediaPlayerDeviceClass.TV,
    Category.RECEIVER: MediaPlayerDeviceClass.RECEIVER,
    Category.PROJECTOR: MediaPlayerDeviceClass.PROJECTOR,
}

VALUE_TO_STATE = {
    "buffering": MediaPlayerState.BUFFERING,
    "paused": MediaPlayerState.PAUSED,
    "playing": MediaPlayerState.PLAYING,
    "stopped": MediaPlayerState.IDLE,
    "fast forwarding": MediaPlayerState.BUFFERING,
    "rewinding": MediaPlayerState.BUFFERING,
}

REPEAT_MODE_TO_HA = {
    "all": RepeatMode.ALL,
    "one": RepeatMode.ONE,
    "off": RepeatMode.OFF,
}

HA_REPEAT_MODE_TO_SMARTTHINGS = {v: k for k, v in REPEAT_MODE_TO_HA.items()}


async def async_setup_entry(
    hass: HomeAssistant,
    entry: SmartThingsConfigEntry,
    async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
    """Add media players for a config entry."""
    entry_data = entry.runtime_data

    async_add_entities(
        SmartThingsMediaPlayer(entry_data.client, device)
        for device in entry_data.devices.values()
        if all(
            capability in device.status[MAIN]
            for capability in MEDIA_PLAYER_CAPABILITIES
        )
    )


class SmartThingsMediaPlayer(SmartThingsEntity, MediaPlayerEntity):
    """Define a SmartThings media player."""

    _attr_name = None
    _attr_translation_key = "media_player"

    def __init__(self, client: SmartThings, device: FullDevice) -> None:
        """Initialize the media_player class."""
        super().__init__(
            client,
            device,
            {
                Capability.AUDIO_MUTE,
                Capability.AUDIO_TRACK_DATA,
                Capability.AUDIO_VOLUME,
                Capability.MEDIA_INPUT_SOURCE,
                Capability.MEDIA_PLAYBACK,
                Capability.MEDIA_PLAYBACK_REPEAT,
                Capability.MEDIA_PLAYBACK_SHUFFLE,
                Capability.SAMSUNG_VD_AUDIO_INPUT_SOURCE,
                Capability.SAMSUNG_VD_MEDIA_INPUT_SOURCE,
                Capability.SWITCH,
            },
        )
        self._attr_supported_features = self._determine_features()
        self._attr_device_class = DEVICE_CLASS_MAP.get(
            device.device.components[MAIN].user_category
            or device.device.components[MAIN].manufacturer_category,
        )
        self._source_to_smartthings_id: dict[str, str] = {}

    @override
    def _update_attr(self) -> None:
        """Update the attributes."""
        self._build_source_map()

    def _build_source_map(self) -> None:
        """Build the source mapping from HA key to SmartThings ID."""
        raw_sources = self._get_raw_source_list()
        if not raw_sources:
            self._source_to_smartthings_id = {}
            return
        self._source_to_smartthings_id = {
            MEDIA_SOURCE_ID_TO_HA_KEY.get(source_id, source_id): source_id
            for source_id in raw_sources
        }

    def _get_raw_source_list(self) -> list[str] | None:
        """Get the raw source list from the device."""
        if self.supports_capability(Capability.SAMSUNG_VD_MEDIA_INPUT_SOURCE):
            sources_map = self.get_attribute_value(
                Capability.SAMSUNG_VD_MEDIA_INPUT_SOURCE,
                Attribute.SUPPORTED_INPUT_SOURCES_MAP,
            )
            if not sources_map:
                return None
            return [source["id"] for source in sources_map]
        if self.supports_capability(Capability.MEDIA_INPUT_SOURCE):
            return self.get_attribute_value(
                Capability.MEDIA_INPUT_SOURCE, Attribute.SUPPORTED_INPUT_SOURCES
            )
        if self.supports_capability(Capability.SAMSUNG_VD_AUDIO_INPUT_SOURCE):
            return self.get_attribute_value(
                Capability.SAMSUNG_VD_AUDIO_INPUT_SOURCE,
                Attribute.SUPPORTED_INPUT_SOURCES,
            )
        return None

    def _determine_features(self) -> MediaPlayerEntityFeature:
        flags = (
            MediaPlayerEntityFeature.VOLUME_SET
            | MediaPlayerEntityFeature.VOLUME_STEP
            | MediaPlayerEntityFeature.VOLUME_MUTE
        )
        if self.supports_capability(Capability.MEDIA_PLAYBACK):
            playback_commands = self.get_attribute_value(
                Capability.MEDIA_PLAYBACK, Attribute.SUPPORTED_PLAYBACK_COMMANDS
            )
            if "play" in playback_commands:
                flags |= MediaPlayerEntityFeature.PLAY
            if "pause" in playback_commands:
                flags |= MediaPlayerEntityFeature.PAUSE
            if "stop" in playback_commands:
                flags |= MediaPlayerEntityFeature.STOP
            if "rewind" in playback_commands:
                flags |= MediaPlayerEntityFeature.PREVIOUS_TRACK
            if "fastForward" in playback_commands:
                flags |= MediaPlayerEntityFeature.NEXT_TRACK
        if self.supports_capability(Capability.SWITCH):
            flags |= (
                MediaPlayerEntityFeature.TURN_ON | MediaPlayerEntityFeature.TURN_OFF
            )
        if self.supports_capability(
            Capability.SAMSUNG_VD_MEDIA_INPUT_SOURCE
        ) or self.supports_capability(Capability.MEDIA_INPUT_SOURCE):
            flags |= MediaPlayerEntityFeature.SELECT_SOURCE
        if self.supports_capability(Capability.MEDIA_PLAYBACK_SHUFFLE):
            flags |= MediaPlayerEntityFeature.SHUFFLE_SET
        if self.supports_capability(Capability.MEDIA_PLAYBACK_REPEAT):
            flags |= MediaPlayerEntityFeature.REPEAT_SET
        return flags

    @override
    async def async_turn_off(self, **kwargs: Any) -> None:
        """Turn the media player off."""
        await self.execute_device_command(
            Capability.SWITCH,
            Command.OFF,
        )

    @override
    async def async_turn_on(self, **kwargs: Any) -> None:
        """Turn the media player on."""
        await self.execute_device_command(
            Capability.SWITCH,
            Command.ON,
        )

    @override
    async def async_mute_volume(self, mute: bool) -> None:
        """Mute volume."""
        await self.execute_device_command(
            Capability.AUDIO_MUTE,
            Command.SET_MUTE,
            argument="muted" if mute else "unmuted",
        )

    @override
    async def async_set_volume_level(self, volume: float) -> None:
        """Set volume level."""
        await self.execute_device_command(
            Capability.AUDIO_VOLUME,
            Command.SET_VOLUME,
            argument=int(volume * 100),
        )

    @override
    async def async_volume_up(self) -> None:
        """Increase volume."""
        await self.execute_device_command(
            Capability.AUDIO_VOLUME,
            Command.VOLUME_UP,
        )

    @override
    async def async_volume_down(self) -> None:
        """Decrease volume."""
        await self.execute_device_command(
            Capability.AUDIO_VOLUME,
            Command.VOLUME_DOWN,
        )

    @override
    async def async_media_play(self) -> None:
        """Play media."""
        await self.execute_device_command(
            Capability.MEDIA_PLAYBACK,
            Command.PLAY,
        )

    @override
    async def async_media_pause(self) -> None:
        """Pause media."""
        await self.execute_device_command(
            Capability.MEDIA_PLAYBACK,
            Command.PAUSE,
        )

    @override
    async def async_media_stop(self) -> None:
        """Stop media."""
        await self.execute_device_command(
            Capability.MEDIA_PLAYBACK,
            Command.STOP,
        )

    @override
    async def async_media_previous_track(self) -> None:
        """Previous track."""
        await self.execute_device_command(
            Capability.MEDIA_PLAYBACK,
            Command.REWIND,
        )

    @override
    async def async_media_next_track(self) -> None:
        """Next track."""
        await self.execute_device_command(
            Capability.MEDIA_PLAYBACK,
            Command.FAST_FORWARD,
        )

    @override
    async def async_select_source(self, source: str) -> None:
        """Select source."""
        smartthings_source = self._source_to_smartthings_id.get(source, source)
        if self.supports_capability(Capability.SAMSUNG_VD_MEDIA_INPUT_SOURCE):
            await self.execute_device_command(
                Capability.SAMSUNG_VD_MEDIA_INPUT_SOURCE,
                Command.SET_INPUT_SOURCE,
                argument=smartthings_source,
            )
        else:
            await self.execute_device_command(
                Capability.MEDIA_INPUT_SOURCE,
                Command.SET_INPUT_SOURCE,
                argument=smartthings_source,
            )

    @override
    async def async_set_shuffle(self, shuffle: bool) -> None:
        """Set shuffle mode."""
        await self.execute_device_command(
            Capability.MEDIA_PLAYBACK_SHUFFLE,
            Command.SET_PLAYBACK_SHUFFLE,
            argument="enabled" if shuffle else "disabled",
        )

    @override
    async def async_set_repeat(self, repeat: RepeatMode) -> None:
        """Set repeat mode."""
        await self.execute_device_command(
            Capability.MEDIA_PLAYBACK_REPEAT,
            Command.SET_PLAYBACK_REPEAT_MODE,
            argument=HA_REPEAT_MODE_TO_SMARTTHINGS[repeat],
        )

    @property
    @override
    def media_title(self) -> str | None:
        """Title of current playing media."""
        if (
            not self.supports_capability(Capability.AUDIO_TRACK_DATA)
            or (
                track_data := self.get_attribute_value(
                    Capability.AUDIO_TRACK_DATA, Attribute.AUDIO_TRACK_DATA
                )
            )
            is None
        ):
            return None
        return track_data.get("title", None)

    @property
    @override
    def media_artist(self) -> str | None:
        """Artist of current playing media."""
        if (
            not self.supports_capability(Capability.AUDIO_TRACK_DATA)
            or (
                track_data := self.get_attribute_value(
                    Capability.AUDIO_TRACK_DATA, Attribute.AUDIO_TRACK_DATA
                )
            )
            is None
        ):
            return None
        return track_data.get("artist")

    @property
    @override
    def state(self) -> MediaPlayerState | None:
        """State of the media player."""
        if self.supports_capability(Capability.SWITCH):
            if not self.supports_capability(Capability.MEDIA_PLAYBACK):
                if (
                    self.get_attribute_value(Capability.SWITCH, Attribute.SWITCH)
                    == "on"
                ):
                    return MediaPlayerState.ON
                return MediaPlayerState.OFF
            if self.get_attribute_value(Capability.SWITCH, Attribute.SWITCH) == "on":
                if (
                    self.source is not None
                    and self.source in CONTROLLABLE_SOURCES
                    and self.get_attribute_value(
                        Capability.MEDIA_PLAYBACK, Attribute.PLAYBACK_STATUS
                    )
                    in VALUE_TO_STATE
                ):
                    return VALUE_TO_STATE[
                        self.get_attribute_value(
                            Capability.MEDIA_PLAYBACK, Attribute.PLAYBACK_STATUS
                        )
                    ]
                return MediaPlayerState.ON
            return MediaPlayerState.OFF
        return VALUE_TO_STATE[
            self.get_attribute_value(
                Capability.MEDIA_PLAYBACK, Attribute.PLAYBACK_STATUS
            )
        ]

    @property
    @override
    def is_volume_muted(self) -> bool:
        """Returns if the volume is muted."""
        return (
            self.get_attribute_value(Capability.AUDIO_MUTE, Attribute.MUTE) == "muted"
        )

    @property
    @override
    def volume_level(self) -> float:
        """Volume level."""
        return self.get_attribute_value(Capability.AUDIO_VOLUME, Attribute.VOLUME) / 100

    @property
    @override
    def source(self) -> str | None:
        """Input source."""
        if self.supports_capability(Capability.SAMSUNG_VD_MEDIA_INPUT_SOURCE):
            raw = self.get_attribute_value(
                Capability.SAMSUNG_VD_MEDIA_INPUT_SOURCE, Attribute.INPUT_SOURCE
            )
        elif self.supports_capability(Capability.MEDIA_INPUT_SOURCE):
            raw = self.get_attribute_value(
                Capability.MEDIA_INPUT_SOURCE, Attribute.INPUT_SOURCE
            )
        elif self.supports_capability(Capability.SAMSUNG_VD_AUDIO_INPUT_SOURCE):
            raw = self.get_attribute_value(
                Capability.SAMSUNG_VD_AUDIO_INPUT_SOURCE, Attribute.INPUT_SOURCE
            )
        else:
            raw = None
        if raw is None:
            return None
        return MEDIA_SOURCE_ID_TO_HA_KEY.get(raw, raw)

    @property
    @override
    def source_list(self) -> list[str] | None:
        """List of input sources."""
        if not self._source_to_smartthings_id:
            return None
        return list(self._source_to_smartthings_id)

    @property
    @override
    def shuffle(self) -> bool | None:
        """Returns if shuffle mode is set."""
        if self.supports_capability(Capability.MEDIA_PLAYBACK_SHUFFLE):
            return (
                self.get_attribute_value(
                    Capability.MEDIA_PLAYBACK_SHUFFLE, Attribute.PLAYBACK_SHUFFLE
                )
                == "enabled"
            )
        return None

    @property
    @override
    def repeat(self) -> RepeatMode | None:
        """Returns if repeat mode is set."""
        if self.supports_capability(Capability.MEDIA_PLAYBACK_REPEAT):
            return REPEAT_MODE_TO_HA[
                self.get_attribute_value(
                    Capability.MEDIA_PLAYBACK_REPEAT, Attribute.PLAYBACK_REPEAT_MODE
                )
            ]
        return None
