import logging
import re
from itertools import chain
from urllib.parse import urlparse

from streamlink.plugin import Plugin
from streamlink.plugin.api import StreamMapper, validate
from streamlink.stream import HLSStream, HTTPStream, RTMPStream
from streamlink.utils import absolute_url

log = logging.getLogger(__name__)

HLS_PLAYLIST_BASE = "http://www.smashcast.tv{0}"
LIVE_API = "http://www.smashcast.tv/api/media/live/{0}?showHidden=true&liveonly=false"
PLAYER_API = "http://www.smashcast.tv/api/player/config/{0}/{1}?embed=false&showHidden=true"
SWF_BASE = "http://edge.vie.hitbox.tv/static/player/flowplayer/"
SWF_URL = SWF_BASE + "flowplayer.commercial-3.2.16.swf"
VOD_BASE_URL = "http://www.smashcast.tv/"

_quality_re = re.compile(r"(\d+p)$")
_url_re = re.compile(r"""
    http(s)?://(www\.)?(hitbox|smashcast).tv
    /(?P<channel>[^/]+)
    (?:
        (?:/videos)?/(?P<media_id>[^/]+)
    )?
""", re.VERBOSE)

_live_schema = validate.Schema(
    {
        "livestream": [{
            "media_user_name": validate.text,
            validate.optional("media_hosted_media"): object,
            "media_is_live": validate.all(
                validate.text,
                validate.transform(int),
                validate.transform(bool)
            ),
            "media_id": validate.text
        }],
    },
    validate.get("livestream"),
    validate.length(1),
    validate.get(0)
)
_player_schema = validate.Schema(
    {
        "clip": {
            "baseUrl": validate.any(None, validate.text),
            "bitrates": validate.all(
                validate.filter(lambda b: b.get("url") and b.get("label")),
                [{
                    "label": validate.text,
                    "url": validate.text,
                }],
            )
        },
        validate.optional("playlist"): [{
            validate.optional("connectionProvider"): validate.text,
            validate.optional("netConnectionUrl"): validate.text,
            validate.optional("bitrates"): [{
                "label": validate.text,
                "url": validate.text,
                "provider": validate.text
            }]
        }],
        "plugins": validate.all(
            dict,
            validate.filter(lambda k, v: k in ["rtmp", "rtmpHitbox", "hls"]),
            {
                validate.text: {
                    validate.optional("netConnectionUrl"): validate.text,
                    "url": validate.text
                }
            }
        )
    }
)


class Hitbox(Plugin):
    @classmethod
    def can_handle_url(cls, url):
        return _url_re.match(url)

    def _get_quality(self, label):
        match = _quality_re.search(label)
        if match:
            return match.group(1)

        return "live"

    def _create_hls_streams(self, bitrate):
        url = bitrate["url"]
        quality = self._get_quality(bitrate["label"])

        if not url.startswith("http"):
            url = HLS_PLAYLIST_BASE.format(url)

        if bitrate["label"] == "Auto":
            try:
                streams = HLSStream.parse_variant_playlist(self.session, url)
                return streams.items()
            except OSError as err:
                log.warning("Failed to extract HLS streams: {0}".format(err))
        else:
            return quality, HLSStream(self.session, url)

    def _create_rtmp_stream(self, rtmp, swf_url, bitrate):
        quality = self._get_quality(bitrate["label"])
        url = bitrate["url"]
        stream = RTMPStream(self.session, {
            "rtmp": rtmp,
            "pageUrl": self.url,
            "playpath": url,
            "swfVfy": swf_url,
            "live": True
        })

        return quality, stream

    def _get_live_streams(self, player):
        mappers = []
        swf_url = SWF_URL
        for playlist in player.get("playlist", []):
            bitrates = playlist.get("bitrates")
            provider = playlist.get("connectionProvider")
            rtmp = None

            if bitrates:
                rtmp = playlist.get("netConnectionUrl")
            elif provider and provider in player["plugins"]:
                provider = player["plugins"][provider]
                swf_name = provider["url"]
                swf_url = SWF_BASE + swf_name
                rtmp = provider["netConnectionUrl"]
                bitrates = player["clip"]["bitrates"]
            else:
                continue

            mapper = StreamMapper(
                cmp=lambda provider, bitrate: bitrate["provider"].startswith(provider)
            )
            mapper.map("hls", self._create_hls_streams)
            mapper.map("rtmp", self._create_rtmp_stream, rtmp, swf_url)
            mappers.append(mapper(bitrates))

        return chain.from_iterable(mappers)

    def _create_video_stream(self, cls, base_url, bitrate):
        url = absolute_url(base_url, bitrate["url"])
        if bitrate["label"].lower() == "auto":
            try:
                return cls.parse_variant_playlist(self.session, url).items()
            except OSError as err:
                log.warning("Failed to extract HLS streams: {0}".format(err))
                return

        quality = self._get_quality(bitrate["label"])
        return quality, cls(self.session, url)

    def _get_video_streams(self, player):
        base_url = player["clip"]["baseUrl"] or VOD_BASE_URL
        mapper = StreamMapper(
            cmp=lambda ext, bitrate: urlparse(bitrate["url"]).path.endswith(ext)
        )
        mapper.map(".m3u8", self._create_video_stream, HLSStream, base_url)
        mapper.map(".mp4", self._create_video_stream, HTTPStream, base_url)
        mapper.map(".flv", self._create_video_stream, HTTPStream, base_url)

        return mapper(player["clip"]["bitrates"])

    def _get_streams(self):
        match = _url_re.match(self.url)
        if not match:
            return

        channel, media_id = match.group("channel", "media_id")
        log.debug("Matched URL: channel={0}, media_id={1}".format(channel, media_id))
        if not media_id:
            res = self.session.http.get(LIVE_API.format(channel))
            livestream = self.session.http.json(res, schema=_live_schema)
            if livestream.get("media_hosted_media"):
                hosted = _live_schema.validate(livestream["media_hosted_media"])
                log.info("{0} is hosting {1}".format(livestream["media_user_name"], hosted["media_user_name"]))
                livestream = hosted

            if not livestream["media_is_live"]:
                return

            media_id = livestream["media_id"]
            media_type = "live"
        else:
            media_type = "video"

        res = self.session.http.get(PLAYER_API.format(media_type, media_id))
        player = self.session.http.json(res, schema=_player_schema)

        if media_type == "live":
            return self._get_live_streams(player)
        else:
            return self._get_video_streams(player)


__plugin__ = Hitbox
