from asyncio import gather
from dataclasses import dataclass
from html import unescape
from typing import Union

from bs4 import BeautifulSoup
from httpx import AsyncClient

from ..hoster.doodstream import DoodstreamHoster
from ..hoster.streamtape import StreamtapeHoster
from ..hoster.veo import VOEHoster
from ..hoster.vidoza import VidozaHoster
from .common import Episode, Hoster, Language, Provider, SearchResult, Series


def provider_to_hoster(provider: str, url: str) -> Hoster:
    if provider == "VOE":
        return VOEHoster(url)
    if provider == "Doodstream":
        return DoodstreamHoster(url)
    if provider == "Vidoza":
        return VidozaHoster(url)
    if provider == "Streamtape":
        return StreamtapeHoster(url)


def lang_img_src_lang_name_to_lang(name: str) -> Language:
    if name == "english":
        return Language.EN
    if name == "english-german":
        return Language.EN_DESUB
    if name == "japanese-english":
        return Language.JP_ENSUB
    if name == "japanese-german":
        return Language.JP_DESUB
    if name == "german":
        return Language.DE


@dataclass
class SerienStreamEpisode(Episode):
    url: str

    async def process_hoster(self) -> dict[Language, list[Hoster]]:
        async with AsyncClient(verify=False) as client:
            response = await client.get(
                f"{self.url}/staffel-{self.season}/episode-{self.episode_number}"
            )
            soup = BeautifulSoup(response.text, "html.parser")
            watch_episode = soup.find_all(
                "li",
                class_=lambda value: value and value.startswith("episodeLink"),
                attrs={
                    "data-lang-key": True,
                    "data-link-id": True,
                    "data-link-target": True,
                    "data-external-embed": True,
                },
            )
            processed_hoster = {}
            for l in Language:
                processed_hoster[l] = list()

            langs = soup.find("div", class_="changeLanguage").find_all("img")
            lang_map = {}
            for lang in langs:
                lang_name = lang.attrs.get("src").split("/")[-1].rsplit(".", 1)[0]
                data_lang_key = lang.attrs.get("data-lang-key")
                lang_map[data_lang_key] = lang_img_src_lang_name_to_lang(lang_name)

            for episode in watch_episode:
                data_link_id = episode.attrs.get("data-link-id")
                provider = episode.find_next("h4").text

                data_lang_key = episode.attrs.get("data-lang-key")
                lang = lang_map[data_lang_key]

                hoster = provider_to_hoster(
                    provider,
                    f"https://{SerienStreamProvider.host}/redirect/{data_link_id}",
                )

                processed_hoster[lang].append(hoster)

            return processed_hoster


@dataclass
class SerienStreamSeries(Series):
    regisseure: set[str]
    schauspieler: set[str]
    produzent: set[str]
    land: list[str]
    full_description: str
    production_year: str
    tags: set[str]

    def to_markdown(self) -> str:
        return (
            f"# {self.name} {self.production_year}\n{self.full_description}\n\n"
            f"**Regisseure**: {', '.join(self.regisseure)}\\\n"
            f"**Schauspieler**: {', '.join(self.schauspieler)}\\\n"
            f"**Produzent**: {', '.join(self.produzent)}\\\n"
            f"**Land**: {', '.join(self.land)}\\\n"
            f"**Tags**: {', '.join(self.tags)}"
        )


@dataclass
class SerienStreamSearchResult(SearchResult):
    link: str = None
    production_year: str = None
    host: str = None

    async def get_series(self) -> SerienStreamSeries:
        return await SerienStreamProvider.get_series(self)

    @property
    def url(self) -> str:
        return f"https://{self.host}/serie/stream/{self.link}"


@dataclass
class SerienStreamProvider(Provider):
    host: str = "186.2.175.5"

    @staticmethod
    async def search(keyword: str) -> Union[list[SerienStreamSearchResult], None]:
        if keyword.strip() == "":
            return None
        async with AsyncClient(verify=False) as client:
            response = await client.get(
                f"https://{SerienStreamProvider.host}/ajax/seriesSearch?keyword={keyword}"
            )
            results = response.json()
            search_results = []
            for series in results:
                search_results.append(
                    SerienStreamSearchResult(
                        name=unescape(series.get("name")).strip(),
                        link=series.get("link"),
                        description=unescape(series.get("description")),
                        cover=f"https://s.to{series.get('cover')}",
                        production_year=series.get("productionYear"),
                        host=SerienStreamProvider.host,
                    )
                )
            return search_results

    @staticmethod
    async def get_series(search_result: SerienStreamSearchResult) -> SerienStreamSeries:
        async with AsyncClient(verify=False) as client:
            response = await client.get(search_result.url)
            soup = BeautifulSoup(response.text, "html.parser")

            tags = []
            for genre in soup.find_all(
                "a", class_="genreButton clearbutton", attrs={"itemprop": "genre"}
            ):
                tags.append(genre.text)

            actors = []
            for actor in soup.find_all("li", attrs={"itemprop": "actor"}):
                actors.append(actor.find_next("span", attrs={"itemprop": "name"}).text)

            creators = []
            for creator in soup.find_all("li", attrs={"itemprop": "creator"}):
                creators.append(
                    creator.find_next("span", attrs={"itemprop": "name"}).text
                )

            countrys = []
            for country in soup.find_all("li", attrs={"data-content-type": "country"}):
                countrys.append(
                    country.find_next("span", attrs={"itemprop": "name"}).text
                )

            directors = []
            for director in soup.find_all("li", attrs={"itemprop": "director"}):
                directors.append(
                    director.find_next("span", attrs={"itemprop": "name"}).text
                )

            funcs = []

            staffeln = (
                soup.find("div", attrs={"id": "stream"}, class_="hosterSiteDirectNav")
                .find("ul")
                .find_all("a")
            )
            count = 0
            for staffel in staffeln:
                # TODO: Filme
                if staffel.text != "Filme":
                    count += 1
                    if count > 1:
                        funcs.append(get_episodes_from_url(count, search_result.url))

            eps = await gather(
                get_episodes_from_soup(1, search_result.url, soup), *funcs
            )

            feps = []
            for e in eps:
                for b in e:
                    feps.append(b)

            return SerienStreamSeries(
                # cover=f"https://{search_result.host}" + soup.find("div", class_="seriesCoverBox").find("img").attrs.get("data-src"),
                name=unescape(
                    soup.find("h1", attrs={"itemprop": "name"}).find("span").text
                ).strip(),
                production_year=unescape(
                    soup.find("div", class_="series-title").find("small").text
                ).strip(),
                # age=int(soup.find("div", class_="fsk").find("span").text),
                # imdb_link=soup.find("a", class_="imdb-link").attrs.get("href"),
                full_description=unescape(
                    soup.find("p", class_="seri_des").attrs.get("data-full-description")
                ),
                regisseure=directors,
                schauspieler=actors,
                produzent=creators,
                land=countrys,
                tags=tags,
                # rating_value=int(soup.find("span", attrs={"itemprop": "ratingValue"}).text),
                # rating_count=int(soup.find("span", attrs={"itemprop": "ratingCount"}).text),
                # staffeln=count,
                episodes=feps,
            )


async def get_episodes_from_url(staffel: int, url: str) -> list[Episode]:
    async with AsyncClient(verify=False) as client:
        response = await client.get(f"{url}/staffel-{staffel}")
        return await get_episodes_from_page(staffel, url, response.text)


async def get_episodes_from_page(staffel: int, url: str, page: str) -> list[Episode]:
    return await get_episodes_from_soup(
        staffel, url, BeautifulSoup(page, "html.parser")
    )


async def get_episodes_from_soup(
    staffel: int, url: str, soup: BeautifulSoup
) -> list[Episode]:
    episodes = []
    e_count = 0
    for episode in (
        soup.find("table", class_="seasonEpisodesList").find("tbody").find_all("tr")
    ):
        title = episode.find_next("td", class_="seasonEpisodeTitle")

        language = set()
        for flag in episode.find_next("td", class_="editFunctions").find_all("img"):
            lang_name = flag.attrs.get("src").split("/")[-1].rsplit(".", 1)[0]
            language.add(lang_img_src_lang_name_to_lang(lang_name))

        hoster = set()
        for h in episode.find_all_next("i", class_="icon"):
            t = h.attrs.get("title")
            if t == "VOE":
                hoster.add(VOEHoster)
            if t == "Doodstream":
                hoster.add(DoodstreamHoster)
            if t == "Vidoza":
                hoster.add(VidozaHoster)
            if t == "Streamtape":
                hoster.add(StreamtapeHoster)

        e_count += 1
        title_en = title.find("span").text.strip()
        title_de = title.find("strong").text.strip()
        title = (
            f"{title_en} - {title_de}"
            if title_en and title_de
            else title_en or title_de
        )
        episodes.append(
            SerienStreamEpisode(
                url=url,
                title=title,
                season=staffel,
                episode_number=e_count,
                available_hoster=hoster,
                available_language=language,
                total_episode_number=None,
                # language=language,
                # hoster=hoster,
                # number=e_count,
                # staffel=staffel
            )
        )

    return episodes
