from __future__ import annotations

import base64
import json
import re as rex
import os
import requests
from langcodes import Language
#from typing import Any, Optional, Union
import datetime
import click

from vinetrimmer.objects import MenuTrack, TextTrack, Title, Tracks, Track
from vinetrimmer.services.BaseService import BaseService


class Jio(BaseService):
    """
    Service code for Viacom18's JioCinema streaming service (https://www.jiocinema.com/).

    \b
    Authorization: Token
    Security: UHD@L3 FHD@L3

    """

    PHONE_NUMBER = "" # Add number with country code

    ALIASES = ["JIO", "JioCinema"]
    #GEOFENCE = ["in2"]
    
    @staticmethod
    @click.command(name="Jio", short_help="https://www.jiocinema.com")
    @click.argument("title", type=str)
    @click.option("-m", "--movie", is_flag=True, default=False, help="Title is a Movie.")
    @click.pass_context
    def cli(ctx, **kwargs):
        return Jio(ctx, **kwargs)

    def __init__(self, ctx, title: str, movie: bool):
        self.title = title
        self.movie = movie
        super().__init__(ctx)

        assert ctx.parent is not None

        self.vcodec = ctx.parent.params["vcodec"]
        self.acodec = ctx.parent.params["acodec"]
        self.range = ctx.parent.params["range_"]

        self.profile = ctx.obj.profile

        self.token: str
        self.refresh_token: str
        self.license_api = None

        self.configure()

    def get_titles(self):
        titles = []
        if self.movie:
            res = self.session.get(
                url='https://content-jiovoot.voot.com/psapi/voot/v1/voot-tv/content/query/asset-details?ids=include%3A{id}&devicePlatformType=androidtv&responseType=common&page=1'.format(id=self.title)
            )
            try:
                data = res.json()['result'][0]
                self.log.debug(json.dumps(data, indent=4))
            except json.JSONDecodeError:
                raise ValueError(f"Failed to load title manifest: {res.text}")

            titles.append(Title(
                id_=data['id'],
                type_=Title.Types.MOVIE,
                name=rex.sub(r'\([^)]*\)', '', data["fullTitle"]).strip(),
                year=data.get("releaseYear"),
                original_lang=Language.find(data['languages'][0]),
                source=self.ALIASES[0],
                service_data=data
            ))

        else:
            def get_recursive_episodes(season_id):
                total_attempts = 1
                recursive_episodes = []
                season_params = {
                    'sort': 'episode:asc',
                    'responseType': 'common',
                    'id': season_id,
                    'page': 1
                }
                while True:
                    episode = self.session.get(url='https://content-jiovoot.voot.com/psapi/voot/v1/voot-web/content/generic/series-wise-episode', params=season_params).json()
                    if any(episode["result"]):
                        total_attempts += 1
                        recursive_episodes.extend(episode["result"])
                        season_params.update({'page': total_attempts})
                    else:
                        break
                return recursive_episodes
            # params = {
            #     'sort': 'season:asc',
            #     'id': self.title,
            #     'responseType': 'common'
            # }
            re = self.session.get(url='https://content-jiovoot.voot.com/psapi/voot/v1/voot-tv/view/show/{id}?devicePlatformType=androidtv&responseType=common&page=1'.format(id=self.title)).json()['trays'][1]
            self.log.debug(json.dumps(re, indent=4))
            for season in re['trayTabs']:
                season_id = season["id"]
                recursive_episodes = get_recursive_episodes(season_id)
                self.log.debug(json.dumps(recursive_episodes, indent=4))
                for episodes in recursive_episodes:
                    titles.append(Title(
                        id_=episodes["id"],
                        type_=Title.Types.TV,
                        name=rex.sub(r'\([^)]*\)', '', episodes["showName"]).strip(),
                        season=int(float(episodes["season"])),
                        episode=int(float(episodes["episode"])),
                        episode_name=episodes["fullTitle"],
                        original_lang=Language.find(episodes['languages'][0]),
                        source=self.ALIASES[0],
                        service_data=episodes
                    ))

        return titles

    def get_tracks(self, title: Title) -> Tracks:
        #self.log.debug(json.dumps(title.service_data, indent=4))
        json_data = {
            '4k': True,
            'ageGroup': '18+',
            'appVersion': '4.0.9',
            'bitrateProfile': 'xxxhdpi',
            'capability': {
                'drmCapability': {
                    'aesSupport': 'yes',
                    'fairPlayDrmSupport': 'yes',
                    'playreadyDrmSupport': 'yes',
                    'widevineDRMSupport': 'L1',
                },
                'frameRateCapability': [
                    {
                        'frameRateSupport': '60fps',
                        'videoQuality': '2160p',
                    },
                ],
            },
            'continueWatchingRequired': False,
            'dolby': True,
            'downloadRequest': False,
            'hevc': False,
            'kidsSafe': False,
            'manufacturer': 'NVIDIA',
            'model': 'SHIELDTV',
            'multiAudioRequired': True,
            'osVersion': '12',
            'parentalPinValid': True,
            'x-apisignatures': 'o668nxgzwff',
        }

        try:
            res = self.session.post(
                url = f'https://apis-jiovoot.voot.com/playbackjv/v3/{title.id}',
                json=json_data,
            )
        except requests.exceptions.RequestException:
            self.refresh()
            try:
                res = self.session.post(
                    url = f'https://apis-jiovoot.voot.com/playbackjv/v3/{title.id}',
                    json=json_data
                )
            except requests.exceptions.RequestException:
                self.log.exit("Unable to retrive manifest")

        res = res.json()
        self.log.debug(json.dumps(res, indent=4))
        self.license_api = res['data']['playbackUrls'][0].get('licenseurl')
        vid_url = res['data']['playbackUrls'][0].get('url')

        if "mpd" in vid_url:
            tracks = Tracks.from_mpd(
                url=vid_url,
                session=self.session,
                #lang=title.original_lang,
                source=self.ALIASES[0]
            )
        else:
            self.log.exit('No mpd found')
        
        self.log.info(f"Getting audio from Various manifests for potential higher bitrate or better codec")
        for device in ['androidtablet']: #'androidmobile', 'androidweb' ==> what more devices ?
            self.session.headers.update({'x-platform': device})
            audio_mpd_url = self.session.post(url=f'https://apis-jiovoot.voot.com/playbackjv/v3/{title.id}', json=json_data)
            if audio_mpd_url.status_code != 200:
                self.log.warning("Unable to retrive manifest")
            else:
                audio_mpd_url = audio_mpd_url.json()['data']['playbackUrls'][0].get('url')
                if "mpd" in audio_mpd_url:
                    audio_mpd = Tracks([
                        x for x in iter(Tracks.from_mpd(
                            url=audio_mpd_url,
                            session=self.session,
                            source=self.ALIASES[0],
                            #lang=title.original_lang,
                        ))
                    ])
                    tracks.add(audio_mpd.audios)
                else:
                    self.log.warning('No mpd found')

        for track in tracks:
            #track.language = Language.get('ta')
            track.needs_proxy = True

        return tracks

    def get_chapters(self, title: Title) -> list[MenuTrack]:
        return []

    def certificate(self, **kwargs) -> None:
        return self.license(**kwargs)

    def license(self, challenge: bytes, **_) -> bytes:
        assert self.license_api is not None
        self.session.headers.update({
            'x-playbackid': '5ec82c75-6fda-4b47-b2a5-84b8d9079675',
            'x-feature-code': 'ytvjywxwkn',
            'origin': 'https://www.jiocinema.com',
            'referer': 'https://www.jiocinema.com/',
        })
        return self.session.post(
            url=self.license_api,
            data=challenge,  # expects bytes
        ).content

    def refresh(self) -> None:
        self.log.info(" + Refreshing auth tokens...")
        res = self.session.post(
            url="https://auth-jiocinema.voot.com/tokenservice/apis/v4/refreshtoken",
            json={
                'appName': 'RJIL_JioCinema',
                'deviceId': '332536276',
                'refreshToken': self.refresh_token,
                'appVersion': '5.6.0'
            }
        )
        if res.status_code != 200:
            return self.log.warning('Tokens cannot be Refreshed. Something went wrong..')

        self.token = res.json()["authToken"]
        self.refresh_token = res.json()["refreshTokenId"]
        self.session.headers.update({'accesstoken': self.token})
        token_cache_path = self.get_cache("token_{profile}.json".format(profile=self.profile))
        old_data = json.load(open(token_cache_path, "r", encoding="utf-8"))
        old_data.update({
            'authToken': self.token,
            'refreshToken': self.refresh_token
        })
        json.dump(old_data, open(token_cache_path, "w", encoding="utf-8"), indent=4)

    def login(self):
        self.log.info(' + Logging into JioCinema')
        if not self.PHONE_NUMBER:
            self.log.exit('Please provide Jiocinema registered Phone number....')
        guest = self.session.post(
            url="https://auth-jiocinema.voot.com/tokenservice/apis/v4/guest",
            json={
                'appName': 'RJIL_JioCinema',
                'deviceType': 'phone',
                'os': 'ios',
                'deviceId': '332536276',
                'freshLaunch': False,
                'adId': '332536276',
                'appVersion': '5.6.0',
            }
        )
        headers = {
            'accesstoken': guest.json()["authToken"],
            'appname': 'RJIL_JioCinema',
            'devicetype': 'phone',
            'os': 'ios'
        }
        send = self.session.post(
            url="https://auth-jiocinema.voot.com/userservice/apis/v4/loginotp/send",
            headers=headers,
            json={
                'number': '{}'.format(base64.b64encode(self.PHONE_NUMBER.encode("utf-8")).decode("utf-8")),
                'appVersion': '5.6.0'
            }
        )
        if send.status_code != 200:
            self.log.exit("OTP Send Failed!")
        else:
            self.log.info("OTP has been sent. Please write it down below and press Enter")
            otp = input()
            verify_data = {
                'deviceInfo': {
                    'consumptionDeviceName': 'iPhone',
                    'info': {
                        'platform': {
                            'name': 'iPhone OS',
                        },
                        'androidId': '332536276',
                        'type': 'iOS',
                    },
                },
                'appVersion': '5.6.0',
                'number': '{}'.format(base64.b64encode(self.PHONE_NUMBER.encode("utf-8")).decode("utf-8")),
                'otp': '{}'.format(otp)
            }
            verify = self.session.post(
                url="https://auth-jiocinema.voot.com/userservice/apis/v4/loginotp/verify",
                headers=headers,
                json=verify_data
            )
            if verify.status_code != 200:
                self.log.exit("Cannot be verified")
            self.log.info(" + Verified!")

            return verify.json()

    def configure(self) -> None:
        token_cache_path = self.get_cache("token_{profile}.json".format(profile=self.profile))
        if os.path.isfile(token_cache_path):
            tokens = json.load(open(token_cache_path, "r", encoding="utf-8"))
            self.log.info(" + Using cached auth tokens...")
        else:
            tokens = self.login()
            os.makedirs(os.path.dirname(token_cache_path), exist_ok=True)
            with open(token_cache_path, "w", encoding="utf-8") as file:
                json.dump(tokens, file, indent=4)
        self.token = tokens["authToken"]
        self.refresh_token = tokens["refreshToken"]
        self.session.headers.update({
            'deviceid': '332536276',
            'accesstoken': self.token,
            'appname': 'RJIL_JioCinema',
            'uniqueid': 'be277ebe-e50b-441e-bc37-bd803286f3d5',
            'user-agent': 'Dalvik/2.1.0 (Linux; U; Android 9; SHIELD Android TV Build/PPR1.180610.011)',
            'x-apisignatures': 'o668nxgzwff',
            'x-platform': 'androidtv', # base device
            'x-platform-token': 'android',
        })