import re
import json

from fuzzywuzzy import fuzz

from .extras import Logger
from .animdl_types import (
    AnimdlAnimeUrlAndTitle,
    AnimdlAnimeEpisode,
    AnimdlEpisodeStream,
)


# Currently this links don't work so we filter it out
broken_link_pattern = r"https://tools.fast4speed.rsvp/\w*"


def path_parser(path: str) -> str:
    """Parses a string and removes path unsafe characters

    Args:
        path (str): a path literal

    Returns:
        str: a parsed string that can be used as a valid path
    """
    return (
        path.replace(":", "")
        .replace("/", "")
        .replace("\\", "")
        .replace('"', "")
        .replace("'", "")
        .replace("<", "")
        .replace(">", "")
        .replace("|", "")
        .replace("?", "")
        .replace(".", "")
        .replace("*", "")
    )


def string_contains_only_spaces(input_string: str) -> bool:
    """Checks if the string is a string of spaces

    Args:
        input_string (str): any string

    Returns:
        bool: a boolean in indicating whether it does contain only spaces or not
    """
    return all(char.isspace() for char in input_string)


def anime_title_percentage_match(
    possible_user_requested_anime_title: str, title: str
) -> int:
    """Returns the percentage match between the possible title and user title

    Args:
        possible_user_requested_anime_title (str): an Animdl search result title
        title (str): the anime title the user wants

    Returns:
        int: the percentage match
    """

    percentage_ratio = fuzz.ratio(title, possible_user_requested_anime_title)
    Logger.info(
        f"Animdl Api Fuzzy: Percentage match of {possible_user_requested_anime_title} against {title}: {percentage_ratio}%"
    )
    return percentage_ratio


def filter_broken_streams(
    streams: list[AnimdlEpisodeStream],
) -> list[AnimdlEpisodeStream]:
    """filters the streams that the project has evaluated doesnt work

    Args:
        streams (list[AnimdlEpisodeStream]): the streams to filter

    Returns:
        list[AnimdlEpisodeStream]: the valid streams
    """

    stream_filter = lambda stream: (
        True if not re.match(broken_link_pattern, stream["stream_url"]) else False
    )
    return list(filter(stream_filter, streams))


def filter_streams_by_quality(
    anime_episode_streams: list[AnimdlEpisodeStream], quality: str | int, strict=False
) -> AnimdlEpisodeStream:
    """filters streams by quality

    Args:
        anime_episode_streams (list[AnimdlEpisodeStream]): the streams to filter
        quality (str | int): the quality you want to get
        strict (bool, optional): whether to always return an episode if quality isn,t found. Defaults to False.

    Returns:
        AnimdlEpisodeStream: the stream of specified quality
    """

    # get the appropriate stream or default to best
    get_quality_func = lambda stream_: (
        stream_.get("quality") if stream_.get("quality") else 0
    )
    match quality:
        case "best":
            return max(anime_episode_streams, key=get_quality_func)
        case "worst":
            return min(anime_episode_streams, key=get_quality_func)
        case _:
            for episode_stream in anime_episode_streams:
                if str(episode_stream.get("quality")) == str(quality):
                    return episode_stream
            else:
                # if not strict:
                Logger.info(f"Animdl Api: Not strict so defaulting to best")
                return max(anime_episode_streams, key=get_quality_func)
                # else:
                #     Logger.warning(
                #         f"Animdl Api: No stream matching the given quality was found"
                #     )
                #     return AnimdlEpisodeStream({})


def parse_stream_urls_data(raw_stream_urls_data: str) -> list[AnimdlAnimeEpisode]:
    """parses the streams data gotten from animdl grab

    Args:
        raw_stream_urls_data (str): the animdl grab data to parse

    Returns:
        list[AnimdlAnimeEpisode]: the parsed streams for all episode
    """

    try:
        return [
            AnimdlAnimeEpisode(json.loads(episode.strip()))
            for episode in raw_stream_urls_data.strip().split("\n")
        ]
    except Exception as e:
        Logger.error(f"Animdl Api Parser {e}")
        return []


def search_output_parser(raw_data: str) -> list[AnimdlAnimeUrlAndTitle]:
    """Parses the recieved raw search animdl data and makes it more easy to use

    Args:
        raw_data (str): valid animdl data

    Returns:
        AnimdlAnimeUrlAndTitle: parsed animdl data containing an animdl anime url and anime title
    """

    # get each line of dat and ignore those that contain unwanted data
    data = raw_data.split("\n")[3:]

    parsed_data = []
    pass_next = False

    # loop through all lines and return an appropriate AnimdlAimeUrlAndTitle
    for i, data_item in enumerate(data[:]):
        # continue if current was used in creating previous animdlanimeurlandtitle
        if pass_next:
            pass_next = False
            continue

        # there is no data or its just spaces so ignore and continue
        if not data_item or string_contains_only_spaces(data_item):
            continue

        # split title? from url?
        item = data_item.split(" / ")

        numbering_pattern = r"^\d*\.\s*"

        # attempt to parse
        try:
            # remove numbering from search results
            anime_title = re.sub(numbering_pattern, "", item[0]).lower()

            # special case for onepiece since allanime labels it as 1p instead of onepiece
            one_piece_regex = re.compile(r"1p", re.IGNORECASE)
            if one_piece_regex.match(anime_title):
                anime_title = "one piece"

            # checks if the data is already structure like anime title, animdl url if not makes it that way
            if item[1] == "" or string_contains_only_spaces(item[1]):
                pass_next = True
                parsed_data.append(AnimdlAnimeUrlAndTitle(anime_title, data[(i + 1)]))
            else:
                parsed_data.append(AnimdlAnimeUrlAndTitle(anime_title, item[1]))
        except:
            pass
    return parsed_data  # anime title,url
