"""Support to interface with the Plex API."""
import logging

from homeassistant.components.media_player import BrowseMedia
from homeassistant.components.media_player.const import (
    MEDIA_CLASS_ALBUM,
    MEDIA_CLASS_ARTIST,
    MEDIA_CLASS_DIRECTORY,
    MEDIA_CLASS_EPISODE,
    MEDIA_CLASS_MOVIE,
    MEDIA_CLASS_PLAYLIST,
    MEDIA_CLASS_SEASON,
    MEDIA_CLASS_TRACK,
    MEDIA_CLASS_TV_SHOW,
    MEDIA_CLASS_VIDEO,
)
from homeassistant.components.media_player.errors import BrowseError

from .const import DOMAIN


class UnknownMediaType(BrowseError):
    """Unknown media type."""


EXPANDABLES = ["album", "artist", "playlist", "season", "show"]
PLAYLISTS_BROWSE_PAYLOAD = {
    "title": "Playlists",
    "media_class": MEDIA_CLASS_DIRECTORY,
    "media_content_id": "all",
    "media_content_type": "playlists",
    "can_play": False,
    "can_expand": True,
}
SPECIAL_METHODS = {
    "On Deck": "onDeck",
    "Recently Added": "recentlyAdded",
}

ITEM_TYPE_MEDIA_CLASS = {
    "album": MEDIA_CLASS_ALBUM,
    "artist": MEDIA_CLASS_ARTIST,
    "episode": MEDIA_CLASS_EPISODE,
    "movie": MEDIA_CLASS_MOVIE,
    "playlist": MEDIA_CLASS_PLAYLIST,
    "season": MEDIA_CLASS_SEASON,
    "show": MEDIA_CLASS_TV_SHOW,
    "track": MEDIA_CLASS_TRACK,
    "video": MEDIA_CLASS_VIDEO,
}

_LOGGER = logging.getLogger(__name__)


def browse_media(  # noqa: C901
    entity, is_internal, media_content_type=None, media_content_id=None
):
    """Implement the websocket media browsing helper."""

    def item_payload(item):
        """Create response payload for a single media item."""
        try:
            media_class = ITEM_TYPE_MEDIA_CLASS[item.type]
        except KeyError as err:
            _LOGGER.debug("Unknown type received: %s", item.type)
            raise UnknownMediaType from err
        payload = {
            "title": item.title,
            "media_class": media_class,
            "media_content_id": str(item.ratingKey),
            "media_content_type": item.type,
            "can_play": True,
            "can_expand": item.type in EXPANDABLES,
        }
        if hasattr(item, "thumbUrl"):
            entity.plex_server.thumbnail_cache.setdefault(
                str(item.ratingKey), item.thumbUrl
            )

            if is_internal:
                thumbnail = item.thumbUrl
            else:
                thumbnail = entity.get_browse_image_url(item.type, item.ratingKey)

            payload["thumbnail"] = thumbnail

        return BrowseMedia(**payload)

    def library_payload(library_id):
        """Create response payload to describe contents of a specific library."""
        library = entity.plex_server.library.sectionByID(library_id)
        library_info = library_section_payload(library)
        library_info.children = []
        library_info.children.append(special_library_payload(library_info, "On Deck"))
        library_info.children.append(
            special_library_payload(library_info, "Recently Added")
        )
        for item in library.all():
            try:
                library_info.children.append(item_payload(item))
            except UnknownMediaType:
                continue
        return library_info

    def playlists_payload():
        """Create response payload for all available playlists."""
        playlists_info = {**PLAYLISTS_BROWSE_PAYLOAD, "children": []}
        for playlist in entity.plex_server.playlists():
            try:
                playlists_info["children"].append(item_payload(playlist))
            except UnknownMediaType:
                continue
        response = BrowseMedia(**playlists_info)
        response.children_media_class = MEDIA_CLASS_PLAYLIST
        return response

    def build_item_response(payload):
        """Create response payload for the provided media query."""
        media = entity.plex_server.lookup_media(**payload)

        if media is None:
            return None

        try:
            media_info = item_payload(media)
        except UnknownMediaType:
            return None
        if media_info.can_expand:
            media_info.children = []
            for item in media:
                try:
                    media_info.children.append(item_payload(item))
                except UnknownMediaType:
                    continue
        return media_info

    if media_content_id and ":" in media_content_id:
        media_content_id, special_folder = media_content_id.split(":")
    else:
        special_folder = None

    if (
        media_content_type
        and media_content_type == "server"
        and media_content_id != entity.plex_server.machine_identifier
    ):
        raise BrowseError(
            f"Plex server with ID '{media_content_id}' is not associated with {entity.entity_id}"
        )

    if special_folder:
        if media_content_type == "server":
            library_or_section = entity.plex_server.library
            children_media_class = MEDIA_CLASS_DIRECTORY
            title = entity.plex_server.friendly_name
        elif media_content_type == "library":
            library_or_section = entity.plex_server.library.sectionByID(
                int(media_content_id)
            )
            title = library_or_section.title
            try:
                children_media_class = ITEM_TYPE_MEDIA_CLASS[library_or_section.TYPE]
            except KeyError as err:
                raise BrowseError(
                    f"Unknown type received: {library_or_section.TYPE}"
                ) from err
        else:
            raise BrowseError(
                f"Media not found: {media_content_type} / {media_content_id}"
            )

        payload = {
            "title": title,
            "media_class": MEDIA_CLASS_DIRECTORY,
            "media_content_id": f"{media_content_id}:{special_folder}",
            "media_content_type": media_content_type,
            "can_play": False,
            "can_expand": True,
            "children": [],
            "children_media_class": children_media_class,
        }

        method = SPECIAL_METHODS[special_folder]
        items = getattr(library_or_section, method)()
        for item in items:
            try:
                payload["children"].append(item_payload(item))
            except UnknownMediaType:
                continue

        return BrowseMedia(**payload)

    try:
        if media_content_type in ["server", None]:
            return server_payload(entity.plex_server)

        if media_content_type == "library":
            return library_payload(int(media_content_id))

    except UnknownMediaType as err:
        raise BrowseError(
            f"Media not found: {media_content_type} / {media_content_id}"
        ) from err

    if media_content_type == "playlists":
        return playlists_payload()

    payload = {
        "media_type": DOMAIN,
        "plex_key": int(media_content_id),
    }
    response = build_item_response(payload)
    if response is None:
        raise BrowseError(f"Media not found: {media_content_type} / {media_content_id}")
    return response


def library_section_payload(section):
    """Create response payload for a single library section."""
    try:
        children_media_class = ITEM_TYPE_MEDIA_CLASS[section.TYPE]
    except KeyError as err:
        _LOGGER.debug("Unknown type received: %s", section.TYPE)
        raise UnknownMediaType from err
    return BrowseMedia(
        title=section.title,
        media_class=MEDIA_CLASS_DIRECTORY,
        media_content_id=str(section.key),
        media_content_type="library",
        can_play=False,
        can_expand=True,
        children_media_class=children_media_class,
    )


def special_library_payload(parent_payload, special_type):
    """Create response payload for special library folders."""
    title = f"{special_type} ({parent_payload.title})"
    return BrowseMedia(
        title=title,
        media_class=parent_payload.media_class,
        media_content_id=f"{parent_payload.media_content_id}:{special_type}",
        media_content_type=parent_payload.media_content_type,
        can_play=False,
        can_expand=True,
        children_media_class=parent_payload.children_media_class,
    )


def server_payload(plex_server):
    """Create response payload to describe libraries of the Plex server."""
    server_info = BrowseMedia(
        title=plex_server.friendly_name,
        media_class=MEDIA_CLASS_DIRECTORY,
        media_content_id=plex_server.machine_identifier,
        media_content_type="server",
        can_play=False,
        can_expand=True,
        children_media_class=MEDIA_CLASS_DIRECTORY,
    )
    server_info.children = []
    server_info.children.append(special_library_payload(server_info, "On Deck"))
    server_info.children.append(special_library_payload(server_info, "Recently Added"))
    for library in plex_server.library.sections():
        if library.type == "photo":
            continue
        server_info.children.append(library_section_payload(library))
    server_info.children.append(BrowseMedia(**PLAYLISTS_BROWSE_PAYLOAD))
    return server_info
