"""
$description Pakistani live TV channels and video on-demand service. OTT service from mjunoon.
$url mjunoon.tv
$type live, vod
$metadata author
$metadata category
$metadata title
$region Pakistan
"""

from __future__ import annotations

import binascii
import re
from typing import Literal
from urllib.parse import urljoin

from streamlink.logger import getLogger
from streamlink.plugin import Plugin, PluginError, pluginmatcher
from streamlink.plugin.api import validate
from streamlink.stream.hls import HLSStream
from streamlink.utils.crypto import AES, unpad
from streamlink.utils.parse import parse_json


log = getLogger(__name__)


@pluginmatcher(
    re.compile(r"https?://(?:www\.)?mjunoon\.tv/(?:watch/)?([\w-]+)"),
)
class Mjunoon(Plugin):
    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: dict[str, Literal[2]] = {
        "aes-256-cbc": AES.MODE_CBC,
    }

    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: dict[str, str], encrypted_data: str):
        if not (algorithm := self.encryption_algorithm.get(cipher_data["algorithm"])):
            raise PluginError(f"Invalid encryption algorithm: {cipher_data['algorithm']}")

        key = bytes(cipher_data["key"], "utf-8")
        iv = bytes(cipher_data["iv"], "utf-8")
        cipher = AES.new(key, algorithm, iv=iv)

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

    def get_stream(self, slug, js_data):
        token_data = {
            "token": self.cache.get("token"),
            "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):
        slug = self.match.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
