from dataclasses import dataclass
from typing import Any

from ytmusicapi.type_alias import JsonDict, JsonList

from .songs import *

PROGRESS_RENDERER = ["musicPlaybackProgressRenderer"]
DURATION_TEXT = ["durationText", "runs", 1, "text"]


@dataclass
class DescriptionElement:
    text: str

    def __str__(self) -> str:
        return self.text


@dataclass
class Link(DescriptionElement):
    url: str


@dataclass
class Timestamp(DescriptionElement):
    seconds: int


@dataclass
class Description(list[DescriptionElement]):
    def __init__(self, *args: Any, **kwargs: Any):
        super().__init__(args[0])

    @property
    def text(self) -> str:
        return "".join(str(element) for element in self)

    @classmethod
    def from_runs(cls, description_runs: JsonList) -> "Description":
        """parse the description runs into a usable format

        :param description_runs: the original description runs

        :return: List of text (str), timestamp (int) and link values (Link object)
        """
        elements: list[DescriptionElement] = []
        for run in description_runs:
            navigationEndpoint = nav(run, ["navigationEndpoint"], True)
            if navigationEndpoint:
                element = DescriptionElement("")
                if "urlEndpoint" in navigationEndpoint:
                    element = Link(text=run["text"], url=navigationEndpoint["urlEndpoint"]["url"])
                elif "watchEndpoint" in navigationEndpoint:
                    element = Timestamp(
                        text=run["text"],
                        seconds=nav(navigationEndpoint, ["watchEndpoint", "startTimeSeconds"]),
                    )
            else:
                element = DescriptionElement(text=nav(run, ["text"], True))  # type: ignore

            elements.append(element)

        return cls(elements)


def parse_base_header(header: JsonDict) -> JsonDict:
    """parse common left hand side (header) items of an episode or podcast page"""
    strapline = nav(header, ["straplineTextOne"])

    author = {
        "name": nav(strapline, [*RUN_TEXT], True),
        "id": nav(strapline, ["runs", 0, *NAVIGATION_BROWSE_ID], True),
    }

    return {
        "author": author if author["name"] else None,
        "title": nav(header, TITLE_TEXT),
        "thumbnails": nav(header, THUMBNAILS),
    }


def parse_podcast_header(header: JsonDict) -> JsonDict:
    metadata = parse_base_header(header)
    metadata["description"] = nav(header, ["description", *DESCRIPTION_SHELF, *DESCRIPTION], True)
    metadata["saved"] = nav(header, ["buttons", 1, *TOGGLED_BUTTON])

    return metadata


def parse_episode_header(header: JsonDict) -> JsonDict:
    metadata = parse_base_header(header)
    metadata["date"] = nav(header, [*SUBTITLE])
    progress_renderer = nav(header, ["progress", *PROGRESS_RENDERER])
    metadata["duration"] = nav(progress_renderer, DURATION_TEXT, True)
    metadata["progressPercentage"] = nav(progress_renderer, ["playbackProgressPercentage"])
    metadata["saved"] = nav(header, ["buttons", 0, *TOGGLED_BUTTON], True) or False

    metadata["playlistId"] = None
    menu_buttons = nav(header, ["buttons", -1, "menuRenderer", "items"])
    for button in menu_buttons:
        if nav(button, [MNIR, *ICON_TYPE], True) == "BROADCAST":
            metadata["playlistId"] = nav(button, [MNIR, *NAVIGATION_BROWSE_ID])

    return metadata


def parse_episode(data: JsonDict) -> JsonDict:
    """Parses a single episode under "Episodes" on a channel page or on a podcast page"""
    thumbnails = nav(data, THUMBNAILS)
    date = nav(data, SUBTITLE, True)
    duration = nav(data, ["playbackProgress", *PROGRESS_RENDERER, *DURATION_TEXT], True)
    title = nav(data, TITLE_TEXT)
    description = nav(data, DESCRIPTION, True)
    videoId = nav(data, ["onTap", *WATCH_VIDEO_ID], True)
    browseId = nav(data, [*TITLE, *NAVIGATION_BROWSE_ID], True)
    videoType = nav(data, ["onTap", *NAVIGATION_VIDEO_TYPE], True)
    index = nav(data, ["onTap", "watchEndpoint", "index"], True)
    return {
        "index": index,
        "title": title,
        "description": description,
        "duration": duration,
        "videoId": videoId,
        "browseId": browseId,
        "videoType": videoType,
        "date": date,
        "thumbnails": thumbnails,
    }


def parse_episode_flat(data: JsonDict) -> JsonDict:
    return {
        "title": nav(get_flex_column_item(data, 0), TEXT_RUN_TEXT),
        "podcast": parse_id_name(nav(get_flex_column_item(data, 1), TEXT_RUN)),
        "videoId": nav(data, ["playlistItemData", "videoId"]),
        "browseId": nav(get_flex_column_item(data, 0), [*TEXT_RUN, *NAVIGATION_BROWSE_ID]),
        "playlistId": nav(data, [*PLAY_BUTTON, "playNavigationEndpoint", *WATCH_PLAYLIST_ID]),
        "videoType": nav(data, [*PLAY_BUTTON, "playNavigationEndpoint", *NAVIGATION_VIDEO_TYPE]),
        "date": nav(get_flex_column_item(data, 2), TEXT_RUN_TEXT),
        "thumbnails": nav(data, THUMBNAILS),
    }


def parse_podcast(data: JsonDict) -> JsonDict:
    """Parses a single podcast under "Podcasts" on a channel page"""
    return {
        "title": nav(data, TITLE_TEXT),
        "channel": parse_id_name(nav(data, [*SUBTITLE_RUNS, 0], True)),
        "browseId": nav(data, TITLE + NAVIGATION_BROWSE_ID),
        "podcastId": nav(data, THUMBNAIL_OVERLAY, True),
        "thumbnails": nav(data, THUMBNAIL_RENDERER),
    }
