# ex:ts=4:sw=4:sts=4:et
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
import datetime
import json
import logging
import re
import time
from urllib.parse import parse_qs
from urllib.parse import urlparse

from svtplay_dl.error import ServiceError
from svtplay_dl.fetcher.dash import dashparse
from svtplay_dl.fetcher.hls import HLS
from svtplay_dl.fetcher.hls import hlsparse
from svtplay_dl.service import OpenGraphThumbMixin
from svtplay_dl.service import Service
from svtplay_dl.subtitle import subtitle
from svtplay_dl.utils.http import download_thumbnails


class Tv4play(Service, OpenGraphThumbMixin):
    supported_domains = ["tv4play.se"]

    def get(self):
        token = self._login()
        if token is None:
            yield ServiceError("You need a token to access the website. see https://svtplay-dl.se/tv4play/")
            return

        match = self._getjson(self.get_urldata())
        if not match:
            yield ServiceError("Can't find json data")
            return
        jansson = json.loads(match.group(1))

        if "params" not in jansson["query"]:
            yield ServiceError("Cant find video id for the video")
            return

        key_check = None
        for key in jansson["props"]["apolloStateFromServer"]["ROOT_QUERY"].keys():
            if key.startswith("media"):
                key_check = key
            if key.startswith("short"):
                key_check = key
        what = jansson["props"]["apolloStateFromServer"]["ROOT_QUERY"][key_check]["__ref"]

        if what.startswith("Series:"):
            yield ServiceError("Use the video page not the series page")
            return
        else:
            vid = jansson["props"]["apolloStateFromServer"][what]["id"]

        url = f"https://playback2.a2d.tv/play/{vid}?service=tv4play&device=browser&protocol=hls%2Cdash&drm=widevine&browser=GoogleChrome&capabilities=live-drm-adstitch-2%2Cyospace3"
        res = self.http.request("get", url, headers={"Authorization": f"Bearer {token}"})
        if res.status_code > 400:
            yield ServiceError("Can't play this video because you don't have access to it")
            return
        jansson = res.json()

        item = jansson["metadata"]
        if item["isDrmProtected"]:
            yield ServiceError("We can't download DRM protected content from this site. This isn't a svtplay-dl issue.")
            return

        if item["isLive"]:
            self.config.set("live", True)
        if item["seasonNumber"] > 0:
            self.output["season"] = item["seasonNumber"]
        if item["episodeNumber"] and item["episodeNumber"] > 0:
            self.output["episode"] = item["episodeNumber"]
        self.output["title"] = item["seriesTitle"]
        self.output["episodename"] = item["title"]
        self.output["id"] = str(vid)
        self.output["episodethumbnailurl"] = item["image"]

        if vid is None:
            yield ServiceError("Cant find video id for the video")
            return

        entries = []
        subs = []
        if jansson["playbackItem"]["type"] == "hls":
            hls = hlsparse(
                self.config,
                self.http.request("get", jansson["playbackItem"]["manifestUrl"]),
                jansson["playbackItem"]["manifestUrl"],
                output=self.output,
                httpobject=self.http,
            )
            entries.append(hls)
            mpd = dashparse(
                self.config,
                self.http.request("get", jansson["playbackItem"]["manifestUrl"].replace(".m3u8", ".mpd")),
                jansson["playbackItem"]["manifestUrl"].replace(".m3u8", ".mpd"),
                output=self.output,
                httpobject=self.http,
            )
            entries.append(mpd)

        m3u8 = []
        for entry in entries:
            for i in entry:
                if isinstance(i, subtitle):
                    subs.append(i)
                else:
                    if isinstance(i, HLS):
                        m3u8.append(i)
                    else:
                        yield i

        # For some reason non-swedish videos have a dub audio as default when it should be the other one
        for i in m3u8:
            if i.audio_role == "main" and i.language == "qas":
                i.audio_role = "alternate"
            elif i.audio_role == "alternate" and i.language != "qas":
                i.audio_role = "main"
            yield i

        if not self.config.get("get_all_subtitles"):
            for i in subs:
                if i.subfix == "sv":
                    yield i
        else:
            for i in subs:
                yield i

    def _getjson(self, data):
        match = re.search(r"application\/json\">(.*\})<\/script>", data)
        return match

    def _login(self):
        if self.config.get("token") is None:
            return None
        res = self.http.request(
            "post",
            "https://auth.tv4.a2d.tv/v2/auth/token",
            json={"grant_type": "refresh_token", "refresh_token": self.config.get("token"), "profile_id": "default", "is_child": False},
            headers={"client-name": "tv4-web"},
        )
        if res.status_code >= 400:
            return None
        return res.json()["access_token"]

    def find_all_episodes(self, config):
        episodes = []
        items = []
        seasonq = None

        parse = urlparse(self.url)
        if parse.path.startswith("/klipp"):
            logging.warning("-A on clips is not supported.")
            return episodes
        if parse.path.startswith("/video"):
            logging.warning("Use program page instead of the video one.")
            return episodes

        token = self._login()
        if token is None:
            logging.error("You need a token to access the website. see https://svtplay-dl.se/tv4play/")
            return episodes

        if parse.path.startswith("/lista"):
            match = re.search(r"/lista/([^\/]+)", parse.path)
            if not match:
                logging.error("Bad lista url?")
                return episodes
            show = match.group(1)
            episodes = self._graphlista(token, show)
            return episodes

        query = parse_qs(parse.query)
        seasonq = query.get("season", None)
        showid, jansson, kind = self._get_seriesid(self.get_urldata(), dict())
        if showid is None:
            logging.error("Cant find any videos")
            return episodes
        if showid is False:
            logging.error("Can't play this video because you don't have access to it")
            return episodes
        if kind == "Movie":
            return [f"https://www.tv4play.se/video/{showid}"]

        jansson = self._graphdetails(token, showid)
        graph_list = None
        for season in reversed(jansson["data"]["media"]["allSeasonLinks"]):
            if seasonq:
                if seasonq[0] == season["seasonId"]:
                    graph_list = self._graphql(token, season["seasonId"])
            else:
                graph_list = self._graphql(token, season["seasonId"])

            if graph_list:
                for i in graph_list:
                    if i not in items:
                        items.append(i)

        for item in items:
            episodes.append(f"https://www.tv4play.se/video/{item}")

        if not episodes:
            logging.warning("Can't find any videos")
        else:
            if not self.config.get("reverse_list"):
                episodes = episodes[::-1]
            if config.get("all_last") > 0:
                return episodes[: config.get("all_last")]
        return episodes

    def _get_seriesid(self, data, jansson):
        match = self._getjson(data)
        if not match:
            return None, jansson, None
        jansson = json.loads(match.group(1))
        if "params" not in jansson["query"]:
            return None, jansson, None
        showid = jansson["query"]["params"][0]
        key_check = None
        for key in jansson["props"]["apolloStateFromServer"]["ROOT_QUERY"].keys():
            if key.startswith("media"):
                key_check = key
        what = jansson["props"]["apolloStateFromServer"]["ROOT_QUERY"][key_check]["__ref"]

        if what.startswith("Episode"):
            if "series" not in jansson["props"]["apolloStateFromServer"][what]:
                return False, jansson, what[: what.index(":")]
            series = jansson["props"]["apolloStateFromServer"][what]["series"]["__ref"].replace("Series:", "")
            res = self.http.request("get", f"https://www.tv4play.se/program/{series}/")
            showid, jansson, _ = self._get_seriesid(res.text, jansson)
        return showid, jansson, what[: what.index(":")]

    def _graphlista(self, token, show):
        episodes = []
        nr = 0
        total = 100
        while nr < total:
            data = {
                "operationName": "MediaPanel",
                "query": "query MediaPanel($panelId: ID!, $mediaInput: MediaPanelContentInput!, $skipSuggestedEpisode: Boolean!, $skipProgress: Boolean!) { panel(id: $panelId) { __typename ... on MediaPanel { __typename ...MediaPanelFields } } }\nfragment ImageFieldsFull on Image { source meta { muteBgColor { hex __typename } __typename } __typename }\nfragment ImageFieldsLight on Image { __typename source isFallback }\nfragment LabelFields on Label { __typename airtime announcement recurringBroadcast hideOnPanels }\nfragment MediaPanelFields on MediaPanel { id title displayHint { __typename mediaPanelImageRatio } content(input: $mediaInput) { __typename pageInfo { __typename ...PageInfoFields } items { __typename ...MediaPanelItemFields } } __typename }\nfragment MediaPanelItemFields on MediaPanelItem { __typename ...MediaPanelSeriesItemFields ...MediaPanelMovieItemFields }\nfragment MediaPanelMovieItemFields on MediaPanelMovieItem { __typename movie { __typename ...MovieFieldsLight } }\nfragment MediaPanelSeriesItemFields on MediaPanelSeriesItem { __typename series { __typename ...SeriesFieldsLight } }\nfragment MovieFieldsLight on Movie { id title slug editorialInfoText mediaClassification genres progress @skip(if: $skipProgress) { __typename percent position timeLeft } label { ...LabelFields __typename } images { main16x9 { ...ImageFieldsFull __typename } cover2x3 { ...ImageFieldsFull __typename } main16x9Annotated { ...ImageFieldsFull __typename } brandLogo { ...ImageFieldsLight __typename } __typename } trailers { mp4 webm __typename } duration { readableShort seconds __typename } isDrmProtected isLiveContent vimondId access { __typename hasAccess } playableFrom { __typename isoString readableDate timestamp } playableUntil { __typename isoString timestamp readableRemaining } liveEventEnd { __typename isoString readableDate timestamp } synopsis { __typename brief short } isPollFeatureEnabled upsell { __typename tierId tierName } isStartOverEnabled __typename }\nfragment PageInfoFields on PageInfo { __typename hasNextPage nextPageOffset totalCount }\nfragment SeriesFieldsLight on Series { id title slug editorialInfoText mediaClassification label { ...LabelFields __typename } suggestedEpisode @skip(if: $skipSuggestedEpisode) { __typename episode { __typename id title progress @skip(if: $skipProgress) { __typename percent position } playableFrom { __typename isoString } duration { readableShort seconds __typename } isDrmProtected isLiveContent vimondId access { __typename hasAccess } } } images { cover2x3 { ...ImageFieldsFull __typename } main16x9 { ...ImageFieldsFull __typename } main16x9Annotated { ...ImageFieldsFull __typename } brandLogo { ...ImageFieldsLight __typename } __typename } synopsis { __typename brief } trailers { mp4 webm __typename } isPollFeatureEnabled upsell { __typename tierId tierName } __typename }",
                "variables": {
                    "mediaInput": {"limit": 10, "offset": nr},
                    "panelId": show,
                    "skipProgress": True,
                    "skipSuggestedEpisode": True,
                },
            }
            res = self.http.request(
                "post",
                "https://nordic-gateway.tv4.a2d.tv/graphql",
                headers={"Client-Name": "tv4-web", "Client-Version": "5.4.0", "Content-Type": "application/json", "Authorization": f"Bearer {token}"},
                json=data,
            )
            janson = res.json()
            total = janson["data"]["panel"]["content"]["pageInfo"]["totalCount"]

            for item in janson["data"]["panel"]["content"]["items"]:
                stuff = []

                if "movie" in item:
                    if not item["movie"]["access"]["hasAccess"]:
                        continue
                    showid = item["movie"]["id"]
                    episodes.append(f"https://www.tv4play.se/video/{showid}")
                    continue
                elif "series" in item:
                    showid = item["series"]["id"]

                jansson2 = self._graphdetails(token, showid)
                for season in jansson2["data"]["media"]["allSeasonLinks"]:
                    graph_list = self._graphql(token, season["seasonId"])
                    for i in graph_list:
                        if i not in stuff:
                            stuff.append(i)

                for item in stuff:
                    episodes.append(f"https://www.tv4play.se/video/{item}")

            nr += 10
        return episodes

    def _graphdetails(self, token, show):
        data = {
            "operationName": "ContentDetailsPage",
            "query": "query ContentDetailsPage($programId: ID!, $recommendationsInput: MediaRecommendationsInput!, $seriesSeasonInput: SeriesSeasonInput!) {\n  media(id: $programId) {\n    __typename\n    ... on Movie {\n      __typename\n      id\n      title\n      genres\n      slug\n      productionYear\n      progress {\n        __typename\n        percent\n        position\n      }\n      productionCountries {\n        __typename\n        countryCode\n        name\n      }\n      playableFrom {\n        __typename\n        isoString\n        humanDateTime\n      }\n      playableUntil {\n        __typename\n        isoString\n        humanDateTime\n        readableDistance(type: DAYS_LEFT)\n      }\n      video {\n        __typename\n        ...VideoFields\n      }\n      parentalRating {\n        __typename\n        ...ParentalRatingFields\n      }\n      credits {\n        __typename\n        ...MovieCreditsFields\n      }\n      label {\n        __typename\n        ...LabelFields\n      }\n      images {\n        __typename\n        main16x7 {\n          __typename\n          ...ImageFieldsLight\n        }\n        main16x9 {\n          __typename\n          ...ImageFieldsFull\n        }\n        poster2x3 {\n          __typename\n          ...ImageFieldsLight\n        }\n        logo {\n          __typename\n          ...ImageFieldsLight\n        }\n      }\n      synopsis {\n        __typename\n        brief\n        long\n        medium\n        short\n      }\n      trailers {\n        __typename\n        mp4\n        webm\n      }\n      recommendations(input: $recommendationsInput) {\n        __typename\n        pageInfo {\n          __typename\n          ...PageInfoFields\n        }\n        items {\n          __typename\n          ...RecommendedSeriesMediaItem\n          ...RecommendedMovieMediaItem\n        }\n      }\n      hasPanels\n      isPollFeatureEnabled\n      humanCallToAction\n      upsell {\n        __typename\n        tierId\n      }\n    }\n    ... on Series {\n      __typename\n      id\n      title\n      numberOfAvailableSeasons\n      genres\n      category\n      slug\n      hasPanels\n      isPollFeatureEnabled\n      upsell {\n        __typename\n        tierId\n      }\n      cdpPageOverride {\n        __typename\n        id\n      }\n      upcomingEpisode {\n        __typename\n        ...UpcomingEpisodeFields\n      }\n      trailers {\n        __typename\n        mp4\n        webm\n      }\n      parentalRating {\n        __typename\n        ...ParentalRatingFields\n      }\n      credits {\n        __typename\n        ...SeriesCreditsFields\n      }\n      label {\n        __typename\n        ...LabelFields\n      }\n      images {\n        __typename\n        main16x7 {\n          __typename\n          ...ImageFieldsLight\n        }\n        main16x9 {\n          __typename\n          ...ImageFieldsFull\n        }\n        poster2x3 {\n          __typename\n          ...ImageFieldsLight\n        }\n        logo {\n          __typename\n          ...ImageFieldsLight\n        }\n      }\n      synopsis {\n        __typename\n        brief\n        long\n      }\n      allSeasonLinks {\n        __typename\n        seasonId\n        title\n        numberOfEpisodes\n      }\n      seasonLinks(seriesSeasonInput: $seriesSeasonInput) {\n        __typename\n        items {\n          __typename\n          seasonId\n          numberOfEpisodes\n        }\n      }\n      suggestedEpisode {\n        __typename\n        humanCallToAction\n        episode {\n          __typename\n          id\n          playableFrom {\n            __typename\n            isoString\n          }\n          playableUntil {\n            __typename\n            isoString\n          }\n          progress {\n            __typename\n            percent\n            position\n          }\n          video {\n            __typename\n            ...VideoFields\n          }\n        }\n      }\n      recommendations(input: $recommendationsInput) {\n        __typename\n        pageInfo {\n          __typename\n          ...PageInfoFields\n        }\n        items {\n          __typename\n          ...RecommendedSeriesMediaItem\n          ...RecommendedMovieMediaItem\n        }\n      }\n    }\n    ... on SportEvent {\n      __typename\n      id\n      league\n      arena\n      country\n      round\n      inStudio\n      commentators\n      access {\n        __typename\n        hasAccess\n      }\n      title\n      productionYear\n      images {\n        __typename\n        main16x7 {\n          __typename\n          ...ImageFieldsFull\n        }\n        main16x9 {\n          __typename\n          ...ImageFieldsFull\n        }\n        poster2x3 {\n          __typename\n          ...ImageFieldsLight\n        }\n      }\n      trailers {\n        __typename\n        mp4\n      }\n      synopsis {\n        __typename\n        brief\n        short\n        long\n        medium\n      }\n      playableFrom {\n        __typename\n        isoString\n        humanDateTime\n      }\n      playableUntil {\n        __typename\n        isoString\n        humanDateTime\n        readableDistance(type: DAYS_LEFT)\n      }\n      liveEventEnd {\n        __typename\n        isoString\n      }\n      isLiveContent\n    }\n  }\n}\nfragment VideoFields on Video {\n  __typename\n  duration {\n    __typename\n    readableShort\n    seconds\n  }\n  id\n  isDrmProtected\n  isLiveContent\n  vimondId\n  access {\n    __typename\n    hasAccess\n  }\n}\nfragment ParentalRatingFields on ParentalRating {\n  __typename\n  finland {\n    __typename\n    ageRestriction\n    reason\n  }\n  sweden {\n    __typename\n    ageRecommendation\n    suitableForChildren\n  }\n}\nfragment MovieCreditsFields on MovieCredits {\n  __typename\n  actors {\n    __typename\n    characterName\n    name\n    type\n  }\n  directors {\n    __typename\n    name\n    type\n  }\n}\nfragment LabelFields on Label {\n  __typename\n  airtime\n  announcement\n  contentDetailsPage\n  recurringBroadcast\n}\nfragment ImageFieldsLight on Image {\n  __typename\n  source\n}\nfragment ImageFieldsFull on Image {\n  __typename\n  source\n  meta {\n    __typename\n    muteBgColor {\n      __typename\n      hex\n    }\n  }\n}\nfragment PageInfoFields on PageInfo {\n  __typename\n  hasNextPage\n  nextPageOffset\n  totalCount\n}\nfragment RecommendedSeriesMediaItem on RecommendedSeries {\n  __typename\n  series {\n    __typename\n    id\n    title\n    images {\n      __typename\n      cover2x3 {\n        __typename\n        source\n      }\n      main16x9 {\n        __typename\n        source\n        meta {\n          __typename\n          muteBgColor {\n            __typename\n            hex\n          }\n        }\n      }\n    }\n    label {\n      __typename\n      ...LabelFields\n    }\n    isPollFeatureEnabled\n  }\n}\nfragment RecommendedMovieMediaItem on RecommendedMovie {\n  __typename\n  movie {\n    __typename\n    id\n    title\n    images {\n      __typename\n      cover2x3 {\n        __typename\n        source\n      }\n      main16x9 {\n        __typename\n        source\n        meta {\n          __typename\n          muteBgColor {\n            __typename\n            hex\n          }\n        }\n      }\n    }\n    label {\n      __typename\n      ...LabelFields\n    }\n    isPollFeatureEnabled\n  }\n}\nfragment UpcomingEpisodeFields on UpcomingEpisode {\n  __typename\n  id\n  title\n  playableFrom {\n    __typename\n    humanDateTime\n    isoString\n  }\n  image {\n    __typename\n    main16x9 {\n      __typename\n      ...ImageFieldsLight\n    }\n  }\n}\nfragment SeriesCreditsFields on SeriesCredits {\n  __typename\n  directors {\n    __typename\n    name\n    type\n  }\n  hosts {\n    __typename\n    name\n    type\n  }\n  actors {\n    __typename\n    characterName\n    name\n    type\n  }\n}",
            "variables": {
                "programId": show,
                "recommendationsInput": {"limit": 10, "offset": 0, "types": ["MOVIE", "SERIES"]},
                "seriesSeasonInput": {"limit": 10, "offset": 0},
            },
        }
        res = self.http.request(
            "post",
            "https://nordic-gateway.tv4.a2d.tv/graphql",
            headers={"Client-Name": "tv4-web", "Client-Version": "5.4.0", "Content-Type": "application/json", "Authorization": f"Bearer {token}"},
            json=data,
        )
        return res.json()

    def _graphql(self, token, show):
        items = []
        nr = 0
        total = 100
        while nr <= total:
            data = {
                "operationName": "SeasonEpisodes",
                "extensions": {
                    "clientLibrary": {"name": "@apollo/client", "version": "4.1.6"},
                    "persistedQuery": {"version": 1, "sha256Hash": "899adcd82ba7f5772ac9ec4bc20a35f7867237f3fa75cb04714d894a6166e444"},
                },
                "variables": {"input": {"limit": 12, "offset": nr, "sortOrder": "ASC"}, "seasonId": show},
            }

            res = self.http.request(
                "post",
                "https://nordic-gateway.tv4.a2d.tv/graphql",
                headers={"Client-Name": "tv4-web", "Client-Version": "5.4.0", "Content-Type": "application/json", "Authorization": f"Bearer {token}"},
                json=data,
            )
            janson = res.json()
            total = janson["data"]["season"]["episodes"]["pageInfo"]["totalCount"]
            for mediatype in janson["data"]["season"]["episodes"]["items"]:
                if mediatype["upsell"]:
                    continue
                if time.time() < int(datetime.datetime.fromisoformat(mediatype["playableFrom"]["isoString"].replace("Z", "+00:00")).timestamp()):
                    continue
                items.append(mediatype["id"])
            nr += 12
        return items

    def get_thumbnail(self, options):
        download_thumbnails(self.output, options, [(False, self.output["episodethumbnailurl"])])


class Tv4(Service, OpenGraphThumbMixin):
    supported_domains = ["tv4.se"]

    def get(self):
        match = re.search(r"application\/json\"\>(\{.*\})\<\/script", self.get_urldata())
        if not match:
            yield ServiceError("Can't find video data'")
            return
        janson = json.loads(match.group(1))
        self.output["id"] = janson["query"]["id"]
        self.output["title"] = janson["query"]["slug"]
        if janson["query"]["type"] == "Article":
            videopd = janson["props"]["apolloState"][f"Article:{janson['query']['id']}"]["featuredAsset"]["__ref"]
            vidasset = janson["props"]["apolloState"][videopd]["asset"]["__ref"]
            self.output["id"] = janson["props"]["apolloState"][vidasset]["id"]
        url = f"https://playback2.a2d.tv/play/{self.output['id']}?service=tv4&device=browser&protocol=hls%2Cdash&drm=widevine&capabilities=live-drm-adstitch-2%2Cexpired_assets"
        res = self.http.request("get", url, cookies=self.cookies)
        if res.status_code > 200:
            yield ServiceError("Can't play this because the video is geoblocked.")
            return
        if res.json()["playbackItem"]["type"] == "hls":
            yield from hlsparse(
                self.config,
                self.http.request("get", res.json()["playbackItem"]["manifestUrl"]),
                res.json()["playbackItem"]["manifestUrl"],
                output=self.output,
            )
