# -*- coding: utf-8 -*-
"""
    proxy.py
    ~~~~~~~~
    ⚡⚡⚡ Fast, Lightweight, Pluggable, TLS interception capable proxy server focused on
    Network monitoring, controls & Application development, testing, debugging.

    :copyright: (c) 2013-present by Abhinav Singh and contributors.
    :license: BSD, see LICENSE for more details.
"""
import os
import json
import logging
from typing import List, Tuple, Any, Dict

from .plugin import ProxyDashboardWebsocketPlugin

from ..common.utils import build_http_response, bytes_

from ..http import httpStatusCodes
from ..http.parser import HttpParser
from ..http.websocket import WebsocketFrame
from ..http.server import HttpWebServerPlugin, HttpWebServerBasePlugin, httpProtocolTypes

logger = logging.getLogger(__name__)


class ProxyDashboard(HttpWebServerBasePlugin):
    """Proxy Dashboard."""

    # Redirects to /dashboard/
    REDIRECT_ROUTES = [
        (httpProtocolTypes.HTTP, r'/dashboard$'),
        (httpProtocolTypes.HTTPS, r'/dashboard$'),
        (httpProtocolTypes.HTTP, r'/dashboard/proxy.html$'),
        (httpProtocolTypes.HTTPS, r'/dashboard/proxy.html$'),
    ]

    # Index html route
    INDEX_ROUTES = [
        (httpProtocolTypes.HTTP, r'/dashboard/$'),
        (httpProtocolTypes.HTTPS, r'/dashboard/$'),
    ]

    # Handles WebsocketAPI requests for dashboard
    WS_ROUTES = [
        (httpProtocolTypes.WEBSOCKET, r'/dashboard$'),
    ]

    def __init__(self, *args: Any, **kwargs: Any) -> None:
        super().__init__(*args, **kwargs)
        self.plugins: Dict[str, ProxyDashboardWebsocketPlugin] = {}
        if b'ProxyDashboardWebsocketPlugin' in self.flags.plugins:
            for klass in self.flags.plugins[b'ProxyDashboardWebsocketPlugin']:
                p = klass(self.flags, self.client, self.event_queue)
                for method in p.methods():
                    self.plugins[method] = p

    def routes(self) -> List[Tuple[int, str]]:
        return ProxyDashboard.REDIRECT_ROUTES + \
            ProxyDashboard.INDEX_ROUTES + \
            ProxyDashboard.WS_ROUTES

    def handle_request(self, request: HttpParser) -> None:
        if request.path == b'/dashboard/':
            self.client.queue(
                HttpWebServerPlugin.read_and_build_static_file_response(
                    os.path.join(
                        self.flags.static_server_dir,
                        'dashboard', 'proxy.html',
                    ),
                    self.flags.min_compression_limit,
                ),
            )
        elif request.path in (
                b'/dashboard',
                b'/dashboard/proxy.html',
        ):
            self.client.queue(
                memoryview(
                    build_http_response(
                        httpStatusCodes.PERMANENT_REDIRECT, reason=b'Permanent Redirect',
                        headers={
                            b'Location': b'/dashboard/',
                            b'Content-Length': b'0',
                            b'Connection': b'close',
                        },
                    ),
                ),
            )

    def on_websocket_open(self) -> None:
        logger.info('app ws opened')

    def on_websocket_message(self, frame: WebsocketFrame) -> None:
        try:
            assert frame.data
            message = json.loads(frame.data)
        except UnicodeDecodeError:
            logger.error(frame.data)
            logger.info(frame.opcode)
            return

        method = message['method']
        if method == 'ping':
            self.reply({'id': message['id'], 'response': 'pong'})
        elif method in self.plugins:
            self.plugins[method].handle_message(message)
        else:
            logger.info(frame.data)
            logger.info(frame.opcode)
            self.reply({'id': message['id'], 'response': 'not_implemented'})

    def on_client_connection_close(self) -> None:
        logger.info('app ws closed')
        # TODO(abhinavsingh): unsubscribe

    def reply(self, data: Dict[str, Any]) -> None:
        self.client.queue(
            memoryview(
                WebsocketFrame.text(
                    bytes_(
                        json.dumps(data),
                    ),
                ),
            ),
        )
