import time
from pathlib import Path

import cherrypy
import requests

import config
import ovenapi


def check_webhook_throttle() -> bool:
    now = time.time()

    # Clean up notification list to recent notifications
    while config.NOTIFICATIONS and now - config.NOTIFICATIONS[0] > 60:
        config.NOTIFICATIONS.pop(0)

    config.NOTIFICATIONS.append(now)

    return not len(config.NOTIFICATIONS) > config.NOTIFICATION_THROTTLE


def webhook_online(stream) -> None:
    if not config.is_webhook_ready():
        return

    data = {"username": f"{config.WEBHOOK_NAME} Online", "content": config.WEBHOOK_ONLINE}

    if config.is_avatar_ready():
        target_av = f"{stream[1]}/{stream[2]}.png"
        avatar = target_av if Path(config.WEBHOOK_AVATAR_PATH, target_av).is_file() else "default.png"
        data["avatar_url"] = f"{config.WEBHOOK_AVATAR_URL}/{avatar}"

    requests.post(config.WEBHOOK_URL, timeout=10, json=data, headers=config.WEBHOOK_HEADERS)


def webhook_offline() -> None:
    if not config.is_webhook_ready():
        return

    data = {"username": f"{config.WEBHOOK_NAME} Offline", "content": config.WEBHOOK_OFFLINE}

    if config.WEBHOOK_AVATAR_PATH and config.WEBHOOK_AVATAR_URL:
        data["avatar_url"] = f"{config.WEBHOOK_AVATAR_URL}/offline.png"

    requests.post(config.WEBHOOK_URL, timeout=10, json=data, headers=config.WEBHOOK_HEADERS)


def check_authorized(host, app, stream, source) -> bool:
    # Are we globally disabled?
    if config.DISABLED:
        return False
    # IP Banned?
    if source in config.BLOCKED_IPS:
        return False
    # Nothing in the Oven API maps a domain to "default" vhost
    # So here we fudge checking default vhost for all apps/streams
    if f"default:{app}:{stream}" in config.DISABLED_KEYS:
        return False
    # Finally check the provided vhost app/stream
    return f"{host}:{app}:{stream}" not in config.DISABLED_KEYS


@cherrypy.tools.register("on_end_request")
def handle_notify() -> None:
    # If we don't have API creds we can't do this, abort
    if not (config.API_USER and config.API_PASS):
        return

    # Get stream list from API
    # Unfortunately Oven doesn't reflect the new stream fast enough so we have to wait :(
    time.sleep(1)
    stream_list = ovenapi.OvenAPI(config.API_USER, config.API_PASS).get_stream_list()

    # If we haven't gone empty->active or active->empty we need to do nothing
    if bool(stream_list) != bool(config.LAST_STREAM_LIST):
        if not check_webhook_throttle():
            cherrypy.log("Webhook throttle limit hit, ignoring")
            return

        # Dispatch the appropriate webhook
        webhook_online(stream_list[0]) if stream_list else webhook_offline()

    # Save our stream list into a durable value
    config.LAST_STREAM_LIST = stream_list.copy()


class Admission:
    # /admission to control/trigger sessions
    @cherrypy.expose
    @cherrypy.tools.json_in()
    @cherrypy.tools.json_out()
    @cherrypy.tools.handle_notify()
    def default(self) -> dict:
        # Fast fail if we have no json payload
        try:
            input_json = cherrypy.request.json
        except AttributeError:
            cherrypy.response.status = 400
            return {}

        # If this is a viewer, allow it with no processing
        # This should never happen since we won't enable webhooks for viewing
        if input_json["request"]["direction"] == "outgoing":
            return {"allowed": True}

        # Figure out scheme, host, app and stream name for recording who is live
        _, _, host, app, path = input_json["request"]["url"].split("/")[:5]
        stream = path.split("?")[0]

        # If we are closing, return a fast 200
        if input_json["request"]["status"] == "closing":
            return {}

        # Get client IP for ACL checking
        ip = input_json["client"]["real_ip"]

        # Check if stream is authorized
        if not check_authorized(host, app, stream, ip):
            cherrypy.log(f"Unauthorized stream key: {app}/{stream}")
            return {"allowed": False}

        # Compile and dispatch our response
        return {"allowed": True}