"""
Piped module for downloading and searching songs.
"""

import logging
import shlex
from typing import Any, Dict, List, Optional

import requests
from yt_dlp import YoutubeDL

from spotdl.providers.audio.base import (
    ISRC_REGEX,
    AudioProvider,
    AudioProviderError,
    YTDLLogger,
)
from spotdl.types.result import Result
from spotdl.utils.config import GlobalConfig, get_temp_path
from spotdl.utils.formatter import args_to_ytdlp_options

__all__ = ["Piped"]
logger = logging.getLogger(__name__)

HEADERS = {
    "accept": "*/*",
}


class Piped(AudioProvider):
    """
    YouTube Music audio provider class
    """

    SUPPORTS_ISRC = True
    GET_RESULTS_OPTS: List[Dict[str, Any]] = [
        {"filter": "music_songs"},
        {"filter": "music_videos"},
    ]

    def __init__(  # pylint: disable=super-init-not-called
        self,
        output_format: str = "mp3",
        cookie_file: Optional[str] = None,
        search_query: Optional[str] = None,
        filter_results: bool = True,
        yt_dlp_args: Optional[str] = None,
    ) -> None:
        """
        Pipe audio provider class

        ### Arguments
        - output_directory: The directory to save the downloaded songs to.
        - output_format: The format to save the downloaded songs in.
        - cookie_file: The path to a file containing cookies to be used by YTDL.
        - search_query: The query to use when searching for songs.
        - filter_results: Whether to filter results.
        """

        self.output_format = output_format
        self.cookie_file = cookie_file
        self.search_query = search_query
        self.filter_results = filter_results

        if self.output_format == "m4a":
            ytdl_format = "best[ext=m4a]/best"
        elif self.output_format == "opus":
            ytdl_format = "best[ext=webm]/best"
        else:
            ytdl_format = "best"

        yt_dlp_options = {
            "format": ytdl_format,
            "quiet": True,
            "no_warnings": True,
            "encoding": "UTF-8",
            "logger": YTDLLogger(),
            "cookiefile": self.cookie_file,
            "outtmpl": f"{get_temp_path()}/%(id)s.%(ext)s",
            "retries": 5,
        }

        if yt_dlp_args:
            user_options = args_to_ytdlp_options(shlex.split(yt_dlp_args))
            yt_dlp_options.update(user_options)

        self.audio_handler = YoutubeDL(yt_dlp_options)
        self.session = requests.Session()

    def get_results(self, search_term: str, **kwargs) -> List[Result]:
        """
        Get results from YouTube Music API and simplify them

        ### Arguments
        - search_term: The search term to search for.
        - kwargs: other keyword arguments passed to the `YTMusic.search` method.

        ### Returns
        - A list of simplified results (dicts)
        """

        if kwargs is None:
            kwargs = {}

        params = {"q": search_term, **kwargs}
        if params.get("filter") is None:
            params["filter"] = "music_videos"

        response = self.session.get(
            "https://piped.video/search",
            params=params,
            headers=HEADERS,
            timeout=20,
        )

        if response.status_code != 200:
            raise AudioProviderError(
                f"Failed to get results for {search_term} from Piped: {response.text}"
            )

        search_results = response.json()

        # Simplify results
        results = []
        for result in search_results["items"]:
            if result["type"] != "stream":
                continue

            isrc_result = ISRC_REGEX.search(search_term)

            results.append(
                Result(
                    source="piped",
                    url=f"https://piped.video{result['url']}",
                    verified=kwargs.get("filter") == "music_songs",
                    name=result["title"],
                    duration=result["duration"],
                    author=result["uploaderName"],
                    result_id=result["url"].split("?v=")[1],
                    artists=(
                        (result["uploaderName"],)
                        if kwargs.get("filter") == "music_songs"
                        else None
                    ),
                    isrc_search=isrc_result is not None,
                    search_query=search_term,
                )
            )

        return results

    def get_download_metadata(self, url: str, download: bool = False) -> Dict:
        """
        Get metadata for a download using yt-dlp.

        ### Arguments
        - url: The url to get metadata for.

        ### Returns
        - A dictionary containing the metadata.
        """

        url_id = url.split("?v=")[1]
        piped_response = requests.get(
            f"https://piped.video/streams/{url_id}",
            timeout=10,
            proxies=GlobalConfig.get_parameter("proxies"),
        )

        if piped_response.status_code != 200:
            raise AudioProviderError(
                f"Failed to get metadata for {url} from Piped: {piped_response.text}"
            )

        piped_data = piped_response.json()

        yt_dlp_json = {
            "title": piped_data["title"],
            "id": url_id,
            "view_count": piped_data["views"],
            "extractor": "Generic",
            "formats": [],
        }

        for audio_stream in piped_data["audioStreams"]:
            yt_dlp_json["formats"].append(
                {
                    "url": audio_stream["url"],
                    "ext": "webm" if audio_stream["codec"] == "opus" else "m4a",
                    "abr": audio_stream["quality"].split(" ")[0],
                    "filesize": audio_stream["contentLength"],
                }
            )

        return self.audio_handler.process_video_result(yt_dlp_json, download=download)
