import re

from ytmusicapi.type_alias import JsonDict, JsonList, ParseFuncDictType

from .albums import parse_album_playlistid_if_exists
from .artists import parse_artists_runs
from .podcasts import parse_episode, parse_podcast
from .songs import *


def parse_mixed_content(
    rows: JsonList,
) -> JsonList:
    items = []
    for row in rows:
        if DESCRIPTION_SHELF[0] in row:
            results = nav(row, DESCRIPTION_SHELF)
            title = nav(results, ["header", *RUN_TEXT])
            contents = nav(results, DESCRIPTION)
        else:
            results = next(iter(row.values()))
            if "contents" not in results:
                continue
            # some carousel headers only carry a strapline (e.g. "MORE FROM") instead of a title
            title = nav(results, [*CAROUSEL_TITLE, "text"], True) or nav(
                results, [*CAROUSEL_STRAPLINE, "text"], True
            )
            contents = []
            for result in results["contents"]:
                data = nav(result, [MTRIR], True)
                content = None
                if data:
                    page_type = nav(data, TITLE + NAVIGATION_BROWSE + PAGE_TYPE, True)
                    if page_type is None:  # song or watch_playlist
                        if nav(data, NAVIGATION_WATCH_PLAYLIST_ID, True) is not None:
                            content = parse_watch_playlist(data)
                        else:
                            content = parse_song(data)
                    elif page_type in ["MUSIC_PAGE_TYPE_ALBUM", "MUSIC_PAGE_TYPE_AUDIOBOOK"]:
                        content = parse_album(data)
                    elif page_type in ["MUSIC_PAGE_TYPE_ARTIST", "MUSIC_PAGE_TYPE_USER_CHANNEL"]:
                        content = parse_related_artist(data)
                    elif page_type == "MUSIC_PAGE_TYPE_PLAYLIST":
                        content = parse_playlist(data)
                    elif page_type == "MUSIC_PAGE_TYPE_PODCAST_SHOW_DETAIL_PAGE":
                        content = parse_podcast(data)
                elif data := nav(result, [MRLIR], True):
                    content = parse_song_flat(data)
                elif data := nav(result, [MMRIR], True):
                    content = parse_episode(data)
                else:
                    continue

                contents.append(content)

        items.append({"title": title, "contents": contents})
    return items


def parse_content_list(results: JsonList, parse_func: ParseFuncDictType, key: str = MTRIR) -> JsonList:
    contents = []
    for result in results:
        # carousels can mix renderer types (e.g. songs/videos alongside playlists),
        # so skip anything that isn't the renderer parse_func expects
        if key not in result:
            continue

        contents.append(parse_func(result[key]))

    return contents


def _parse_album_single_subtitle(result: JsonDict, album_or_single: JsonDict) -> JsonDict:
    if type_or_year := nav(result, SUBTITLE, True):
        if type_or_year.isnumeric():
            album_or_single["year"] = type_or_year
        else:
            album_or_single["type"] = type_or_year
            if (year := nav(result, SUBTITLE2, True)) and year.isnumeric():
                album_or_single["year"] = year
    return album_or_single


def parse_album(result: JsonDict) -> JsonDict:
    album = {
        "title": nav(result, TITLE_TEXT),
        "artists": [
            parse_id_name(x)
            for x in (nav(result, ["subtitle", "runs"], True) or [])
            if "navigationEndpoint" in x
        ],
        "browseId": nav(result, TITLE + NAVIGATION_BROWSE_ID),
        "audioPlaylistId": parse_album_playlistid_if_exists(nav(result, THUMBNAIL_OVERLAY_NAVIGATION, True)),
        "thumbnails": nav(result, THUMBNAIL_RENDERER),
        "isExplicit": nav(result, SUBTITLE_BADGE_LABEL, True) is not None,
    }

    return _parse_album_single_subtitle(result, album)


def parse_single(result: JsonDict) -> JsonDict:
    single = {
        "title": nav(result, TITLE_TEXT),
        "browseId": nav(result, TITLE + NAVIGATION_BROWSE_ID),
        "thumbnails": nav(result, THUMBNAIL_RENDERER),
    }

    return _parse_album_single_subtitle(result, single)


def parse_song(result: JsonDict) -> JsonDict:
    song = {
        "title": nav(result, TITLE_TEXT),
        "videoId": nav(result, NAVIGATION_VIDEO_ID),
        "playlistId": nav(result, NAVIGATION_PLAYLIST_ID, True),
        "thumbnails": nav(result, THUMBNAIL_RENDERER),
    }
    song.update(parse_song_runs(nav(result, SUBTITLE_RUNS), skip_type_spec=True))
    return song


def parse_song_flat(data: JsonDict, with_playlist_id: bool = False) -> JsonDict:
    columns = [get_flex_column_item(data, i) for i in range(len(data["flexColumns"]))]
    song = {
        "title": nav(columns[0], TEXT_RUN_TEXT),
        "videoId": nav(columns[0], TEXT_RUN + NAVIGATION_VIDEO_ID, True),
        "videoType": nav(data, [*PLAY_BUTTON, "playNavigationEndpoint", *NAVIGATION_VIDEO_TYPE], True),
        "thumbnails": nav(data, THUMBNAILS),
        "isExplicit": nav(data, BADGE_LABEL, True) is not None,
    }

    if with_playlist_id:
        song["playlistId"] = nav(data, [*PLAY_BUTTON, "playNavigationEndpoint", *WATCH_PLAYLIST_ID])

    runs = nav(columns[1], TEXT_RUNS)
    song.update(parse_song_runs(runs, skip_type_spec=True))

    if len(columns) > 2 and columns[2] is not None and "navigationEndpoint" in nav(columns[2], TEXT_RUN):
        song["album"] = {
            "name": nav(columns[2], TEXT_RUN_TEXT),
            "id": nav(columns[2], TEXT_RUN + NAVIGATION_BROWSE_ID),
        }

    return song


def parse_video(result: JsonDict) -> JsonDict:
    runs = nav(result, SUBTITLE_RUNS)
    artists_len = get_dot_separator_index(runs)
    videoId = nav(result, NAVIGATION_VIDEO_ID, True)
    if not videoId:
        videoId = next(
            video_id
            for entry in nav(result, MENU_ITEMS)
            if (video_id := nav(entry, MENU_SERVICE + QUEUE_VIDEO_ID, True))
        )
    return {
        "title": nav(result, TITLE_TEXT),
        "videoId": videoId,
        "artists": parse_artists_runs(runs[:artists_len]),
        "playlistId": nav(result, NAVIGATION_PLAYLIST_ID, True),
        "thumbnails": nav(result, THUMBNAIL_RENDERER, True),
        "views": runs[-1]["text"].split(" ")[0],
    }


def parse_playlist(data: JsonDict) -> JsonDict:
    playlist_id = nav(data, TITLE + NAVIGATION_BROWSE_ID)[2:]
    menu_items = nav(data, ["menu", "menuRenderer", "items"], True) or []
    playlist = {
        "title": nav(
            data,
            TITLE_TEXT,
            none_if_absent=True,  # rare but possible for playlist title to be missing
        ),
        "playlistId": playlist_id,
        "thumbnails": nav(data, THUMBNAIL_RENDERER, True),
        "owned": any(
            nav(
                item,
                [
                    "menuNavigationItemRenderer",
                    "navigationEndpoint",
                    "playlistEditorEndpoint",
                    "playlistId",
                ],
                True,
            )
            == playlist_id
            for item in menu_items
        ),
    }
    subtitle = data["subtitle"]
    if "runs" in subtitle:
        playlist["description"] = "".join([run["text"] for run in subtitle["runs"]])
        if len(subtitle["runs"]) == 3 and re.search(r"\d+ ", nav(data, SUBTITLE2)):
            playlist["count"] = nav(data, SUBTITLE2).split(" ")[0]
            playlist["author"] = parse_artists_runs(subtitle["runs"][:1])

    return playlist


def parse_related_artist(data: JsonDict) -> JsonDict:
    subscribers = nav(data, SUBTITLE, True)
    if subscribers:
        subscribers = subscribers.split(" ")[0]
    return {
        "title": nav(data, TITLE_TEXT),
        "browseId": nav(data, TITLE + NAVIGATION_BROWSE_ID),
        "subscribers": subscribers,
        "thumbnails": nav(data, THUMBNAIL_RENDERER),
    }


def parse_watch_playlist(data: JsonDict) -> JsonDict:
    return {
        "title": nav(data, TITLE_TEXT),
        "playlistId": nav(data, NAVIGATION_WATCH_PLAYLIST_ID),
        "thumbnails": nav(data, THUMBNAIL_RENDERER),
    }
