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

from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad

from streamlink.plugin import Plugin
from streamlink.plugin.api import validate
from streamlink.stream import HLSStream
from streamlink.utils import parse_json

log = logging.getLogger(__name__)


class Mjunoon(Plugin):
    url_re = re.compile(r'https?://(?:www\.)?mjunoon\.tv/(?:watch/)?([\w-]+)')

    login_url = 'https://cdn2.mjunoon.tv:9191/v2/auth/login'
    stream_url = 'https://cdn2.mjunoon.tv:9191/v2/streaming-url'

    is_live_channel_re = re.compile(r'"isLiveBroadcast":\s*"(true|undefined)"')

    main_chunk_js_url_re = re.compile(
        r'<script src="(/static/js/main\.\w+\.chunk\.js)"></script>'
    )

    js_credentials_re = re.compile(
        r'data:{email:"(?P<email>.*?)",password:"(?P<password>.*?)"}'
    )

    js_cipher_data_re = re.compile(
        r'createDecipheriv\("(?P<algorithm>.*?)","(?P<key>.*?)","(?P<iv>.*?)"\)'
    )

    token_schema = validate.Schema({
        'token': str,
        'token_type': str,
        'expires_in': int,
    })

    encrypted_data_schema = validate.Schema({
        'eData': str,
    }, validate.get('eData'))

    stream_schema = validate.Schema({
        'data': {
            'live_stream_url': validate.url(),
            'channel_name': str,
            'meta_title': validate.any(None, str),
            'genres': validate.all(
                validate.transform(lambda x: x.split(',')[0]),
                str,
            ),
        },
    }, validate.get('data'))

    encryption_algorithm = {
        'aes-256-cbc': AES.MODE_CBC,
    }

    author = None
    category = None
    title = None

    @classmethod
    def can_handle_url(cls, url):
        return cls.url_re.match(url) is not None

    def get_author(self):
        return self.author

    def get_category(self):
        return self.category

    def get_title(self):
        return self.title

    def get_data(self):
        js_data = {}
        res = self.session.http.get(self.url)

        m = self.is_live_channel_re.search(res.text)
        if not m:
            return

        if m.group(1) == "true":
            js_data['type'] = 'channel'
        else:
            js_data['type'] = 'episode'

        m = self.main_chunk_js_url_re.search(res.text)
        if not m:
            log.error('Failed to get main chunk JS URL')
            return

        res = self.session.http.get(urljoin(self.url, m.group(1)))

        m = self.js_credentials_re.search(res.text)
        if not m:
            log.error('Failed to get credentials')
            return

        js_data['credentials'] = m.groupdict()

        m = self.js_cipher_data_re.search(res.text)
        if not m:
            log.error('Failed to get cipher data')
            return

        js_data['cipher_data'] = m.groupdict()

        return js_data

    def decrypt_data(self, cipher_data, encrypted_data):
        cipher = AES.new(
            bytes(cipher_data['key'], 'utf-8'),
            self.encryption_algorithm[cipher_data['algorithm']],
            bytes(cipher_data['iv'], 'utf-8'),
        )

        return unpad(cipher.decrypt(binascii.unhexlify(encrypted_data)), 16, 'pkcs7')

    def get_stream(self, slug, js_data):
        token_data = {}
        token_data['token'] = self.cache.get('token')
        token_data['token_type'] = self.cache.get('token_type')

        if token_data['token'] and token_data['token_type']:
            log.debug('Using cached token')
        else:
            log.debug('Getting new token')

            res = self.session.http.post(
                self.login_url,
                json=js_data['credentials'],
            )
            token_data = self.session.http.json(res, schema=self.token_schema)
            log.debug(f'Token={token_data["token"]}')

            self.cache.set('token', token_data['token'], expires=token_data['expires_in'])
            self.cache.set('token_type', token_data['token_type'], expires=token_data['expires_in'])

        headers = {'Authorization': f'{token_data["token_type"]} {token_data["token"]}'}
        data = {
            'slug': slug,
            'type': js_data['type'],
        }
        res = self.session.http.post(
            self.stream_url,
            headers=headers,
            json=data,
        )
        encrypted_data = self.session.http.json(
            res,
            schema=self.encrypted_data_schema
        )

        stream_data = parse_json(
            self.decrypt_data(js_data['cipher_data'], encrypted_data),
            schema=self.stream_schema,
        )

        self.author = stream_data['channel_name']
        self.category = stream_data['genres']
        self.title = stream_data['meta_title']

        return stream_data['live_stream_url']

    def _get_streams(self):
        m = self.url_re.match(self.url)
        slug = m.group(1)
        log.debug(f'Slug={slug}')

        js_data = self.get_data()
        if not js_data:
            return

        log.debug(f'JS data={js_data}')

        hls_url = self.get_stream(slug, js_data)
        log.debug(f'HLS URL={hls_url}')

        return HLSStream.parse_variant_playlist(self.session, hls_url)


__plugin__ = Mjunoon
