import logging
import re
import sys
from urllib.parse import urljoin

from streamlink.plugin import Plugin, pluginmatcher
from streamlink.plugin.api import validate
from streamlink.stream.hls import HLSStream
from streamlink.stream.http import HTTPStream

log = logging.getLogger(__name__)


@pluginmatcher(re.compile(
    r"https?://(?:www\.)?booyah\.live/(?:(?P<type>channels|clips|vods)/)?(?P<id>[^?]+)"
))
class Booyah(Plugin):
    auth_api_url = 'https://booyah.live/api/v3/auths/sessions'
    vod_api_url = 'https://booyah.live/api/v3/playbacks/{0}'
    live_api_url = 'https://booyah.live/api/v3/channels/{0}'
    streams_api_url = 'https://booyah.live/api/v3/channels/{0}/streams'

    auth_schema = validate.Schema({
        'expiry_time': int,
        'uid': int,
    })

    vod_schema = validate.Schema({
        'user': {
            'nickname': str,
        },
        'playback': {
            'name': str,
            'endpoint_list': [{
                'stream_url': validate.url(),
                'resolution': validate.all(
                    int,
                    validate.transform(lambda x: f'{x}p'),
                ),
            }],
        },
    })

    live_schema = validate.Schema({
        'user': {
            'nickname': str,
        },
        'channel': {
            'channel_id': int,
            'name': str,
            'is_streaming': bool,
            validate.optional('hostee'): {
                'channel_id': int,
                'nickname': str,
            },
        },
    })

    @classmethod
    def stream_weight(cls, stream):
        if stream == "source":
            return sys.maxsize, "source"
        return super().stream_weight(stream)

    def do_auth(self):
        res = self.session.http.post(self.auth_api_url)
        self.session.http.json(res, self.auth_schema)

    def get_vod(self, id):
        res = self.session.http.get(self.vod_api_url.format(id))
        user_data = self.session.http.json(res, schema=self.vod_schema)

        self.author = user_data['user']['nickname']
        self.category = 'VOD'
        self.title = user_data['playback']['name']

        for stream in user_data['playback']['endpoint_list']:
            if stream['stream_url'].endswith('.mp4'):
                yield stream['resolution'], HTTPStream(
                    self.session,
                    stream['stream_url'],
                )
            else:
                yield stream['resolution'], HLSStream(
                    self.session,
                    stream['stream_url'],
                )

    def get_live(self, id):
        res = self.session.http.get(self.live_api_url.format(id))
        user_data = self.session.http.json(res, schema=self.live_schema)

        if user_data['channel']['is_streaming']:
            self.category = 'Live'
            stream_id = user_data['channel']['channel_id']
        elif 'hostee' in user_data['channel']:
            self.category = f'Hosted by {user_data["channel"]["hostee"]["nickname"]}'
            stream_id = user_data['channel']['hostee']['channel_id']
        else:
            log.info('User is offline')
            return

        self.author = user_data['user']['nickname']
        self.title = user_data['channel']['name']

        res = self.session.http.get(self.streams_api_url.format(stream_id))
        streams = self.session.http.json(res, schema=validate.Schema({
            "default_mirror": str,
            "mirror_list": [{
                "name": str,
                "url_domain": validate.url(),
            }],
            "source_stream_url_path": str,
            "stream_addr_list": [{
                "resolution": str,
                "url_path": str,
            }],
        }))

        mirror = (
            next(filter(lambda item: item["name"] == streams["default_mirror"], streams["mirror_list"]), None)
            or next(iter(streams["mirror_list"]), None)
        )
        if not mirror:
            return

        auto = next(filter(lambda item: item["resolution"] == "Auto", streams["stream_addr_list"]), None)
        if auto:
            yield from HLSStream.parse_variant_playlist(self.session, urljoin(mirror["url_domain"], auto["url_path"])).items()

        if streams["source_stream_url_path"]:
            yield "source", HLSStream(self.session, urljoin(mirror["url_domain"], streams["source_stream_url_path"]))

    def _get_streams(self):
        self.do_auth()

        url_data = self.match.groupdict()
        log.debug(f'ID={url_data["id"]}')

        if not url_data['type'] or url_data['type'] == 'channels':
            log.debug('Type=Live')
            return self.get_live(url_data['id'])
        else:
            log.debug('Type=VOD')
            return self.get_vod(url_data['id'])


__plugin__ = Booyah
