import requests
import re

from rest_framework_jwt.settings import api_settings
from rest_framework.permissions import AllowAny
from rest_framework.generics import GenericAPIView
from django.conf import settings
from django.http import HttpResponse
from django.http import QueryDict
from urllib.parse import urlparse

jwt_get_username_from_payload = api_settings.JWT_PAYLOAD_GET_USERNAME_HANDLER
jwt_decode_handler = api_settings.JWT_DECODE_HANDLER
jwt_response_payload_handler = api_settings.JWT_RESPONSE_PAYLOAD_HANDLER


class AllowAnyApiView(GenericAPIView):
    """
    该API不需要通过任何认证
    """
    permission_classes = (AllowAny, )
    authentication_classes = ()

    def __init__(self, *args, **kwargs):
        super(AllowAnyApiView, self).__init__(*args, **kwargs)
        self.user = None

    def initial(self, request, *args, **kwargs):
        super(AllowAnyApiView, self).initial(request, *args, **kwargs)
        self.user = request.user


class OAProxyMonitorApiView(AllowAnyApiView):
    def __init__(self, *args, **kwargs):
        super(OAProxyMonitorApiView, self).__init__(*args, **kwargs)

    def dispatch(self, request, *args, **kwargs):
        self.args = args
        self.kwargs = kwargs
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request
        self.headers = self.default_response_headers
        try:
            self.initial(request, *args, **kwargs)
            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(),
                                  self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed

            remote_url = "http://{0}:{1}{2}".format(
                settings.OA_MONITOR_HOST, settings.OA_MONITOR_PORT, request.META["PATH_INFO"])
            response = self.proxy_view(request, remote_url)
            handler(request, *args, **kwargs)
        except Exception as exc:
            response = self.handle_exception(exc)
        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response

    def proxy_view(self, request, url, requests_args=None):
        requests_args = (requests_args or {}).copy()
        headers = self.get_headers(request.META)
        params = request.GET.copy()

        if 'headers' not in requests_args:
            requests_args['headers'] = {}
        if 'data' not in requests_args:
            requests_args['data'] = request.body
        if 'params' not in requests_args:
            requests_args['params'] = QueryDict('', mutable=True)

        headers.update(requests_args['headers'])
        params.update(requests_args['params'])

        for key in list(headers.keys()):
            if key.lower() == 'content-length':
                del headers[key]

        requests_args['headers'] = headers
        requests_args['params'] = params
        if requests_args['headers'].get("AUTHORIZATION"):
            requests_args['headers'].pop("AUTHORIZATION")
        response = requests.request(request.method, url, **requests_args)
        proxy_response = HttpResponse(response.content, status=response.status_code)

        excluded_headers = set([
            'connection',
            'keep-alive',
            'proxy-authenticate',
            'proxy-authorization',
            'te',
            'trailers',
            'transfer-encoding',
            'upgrade',
            'content-encoding',
            'content-length',
        ])
        for key, value in response.headers.items():
            if key.lower() in excluded_headers:
                continue
            elif key.lower() == 'location':
                proxy_response[key] = self.make_absolute_location(response.url, value)
            else:
                proxy_response[key] = value

        return proxy_response

    def make_absolute_location(self, base_url, location):
        absolute_pattern = re.compile(r'^[a-zA-Z]+://.*$')
        if absolute_pattern.match(location):
            return location

        parsed_url = urlparse(base_url)

        if location.startswith('//'):
            return parsed_url.scheme + ':' + location

        elif location.startswith('/'):
            return parsed_url.scheme + '://' + parsed_url.netloc + location

        else:
            return parsed_url.scheme + '://' + parsed_url.netloc + parsed_url.path.rsplit('/', 1)[0] + '/' + location

    def get_headers(self, environ):
        headers = {}
        for key, value in environ.items():
            if key.startswith('HTTP_') and key != 'HTTP_HOST':
                headers[key[5:].replace('_', '-')] = value
            elif key in ('CONTENT_TYPE', 'CONTENT_LENGTH'):
                headers[key.replace('_', '-')] = value

        return headers
