from collections import OrderedDict

import pytz
from django.conf import settings
from django.core.urlresolvers import get_script_prefix
from django.http import HttpRequest, HttpResponse
from django.utils import timezone, translation
from django.utils.cache import patch_vary_headers
from django.utils.translation import LANGUAGE_SESSION_KEY
from django.utils.translation.trans_real import (
    check_for_language, get_supported_language_variant, language_code_re,
    parse_accept_lang_header,
)

_supported = None


class LocaleMiddleware:

    """
    This middleware sets the correct locale and timezone
    for a request.
    """

    def process_request(self, request: HttpRequest):
        language = get_language_from_request(request)
        # Normally, this middleware runs *before* the event is set. However, on event frontend pages it
        # might be run a second time by pretix.presale.EventMiddleware and in this case the event is already
        # set and can be taken into account for the decision.
        if hasattr(request, 'event') and not request.path.startswith(get_script_prefix() + 'control'):
            if language not in request.event.settings.locales:
                firstpart = language.split('-')[0]
                if firstpart in request.event.settings.locales:
                    language = firstpart
                else:
                    language = request.event.settings.locale
                    for lang in request.event.settings.locales:
                        if lang.startswith(firstpart + '-'):
                            language = lang
                            break
        translation.activate(language)
        request.LANGUAGE_CODE = translation.get_language()

        tzname = None
        if request.user.is_authenticated():
            tzname = request.user.timezone
        if hasattr(request, 'event'):
            tzname = request.event.settings.timezone
        if tzname:
            try:
                timezone.activate(pytz.timezone(tzname))
                request.timezone = tzname
            except pytz.UnknownTimeZoneError:
                pass
        else:
            timezone.deactivate()

    def process_response(self, request: HttpRequest, response: HttpResponse):
        language = translation.get_language()
        patch_vary_headers(response, ('Accept-Language',))
        if 'Content-Language' not in response:
            response['Content-Language'] = language
        return response


def get_language_from_user_settings(request: HttpRequest) -> str:
    if request.user.is_authenticated():
        lang_code = request.user.locale
        if lang_code in _supported and lang_code is not None and check_for_language(lang_code):
            return lang_code


def get_language_from_session_or_cookie(request: HttpRequest) -> str:
    if hasattr(request, 'session'):
        lang_code = request.session.get(LANGUAGE_SESSION_KEY)
        if lang_code in _supported and lang_code is not None and check_for_language(lang_code):
            return lang_code

    lang_code = request.COOKIES.get(settings.LANGUAGE_COOKIE_NAME)
    try:
        return get_supported_language_variant(lang_code)
    except LookupError:
        pass


def get_language_from_event(request: HttpRequest) -> str:
    if hasattr(request, 'event'):
        lang_code = request.event.settings.locale
        try:
            return get_supported_language_variant(lang_code)
        except LookupError:
            pass


def get_language_from_browser(request: HttpRequest) -> str:
    accept = request.META.get('HTTP_ACCEPT_LANGUAGE', '')
    for accept_lang, unused in parse_accept_lang_header(accept):
        if accept_lang == '*':
            break

        if not language_code_re.search(accept_lang):
            continue

        try:
            return get_supported_language_variant(accept_lang)
        except LookupError:
            continue


def get_default_language():
    try:
        return get_supported_language_variant(settings.LANGUAGE_CODE)
    except LookupError:  # NOQA
        return settings.LANGUAGE_CODE


def get_language_from_request(request: HttpRequest) -> str:
    """
    Analyzes the request to find what language the user wants the system to
    show. Only languages listed in settings.LANGUAGES are taken into account.
    If the user requests a sublanguage where we have a main language, we send
    out the main language.
    """
    global _supported
    if _supported is None:
        _supported = OrderedDict(settings.LANGUAGES)

    return (
        get_language_from_user_settings(request)
        or get_language_from_session_or_cookie(request)
        or get_language_from_event(request)
        or get_language_from_browser(request)
        or get_default_language()
    )


class SecurityMiddleware:

    def _parse_csp(self, header):
        h = {}
        for part in header.split(';'):
            k, v = part.strip().split(' ', 1)
            h[k.strip()] = v
        return h

    def _render_csp(self, h):
        return "; ".join(k + ' ' + v for k, v in h.items())

    def process_response(self, request, resp):
        if settings.DEBUG and resp.status_code >= 400:
            # Don't use CSP on debug error page as it breaks of Django's fancy error
            # pages
            return resp

        resp['X-XSS-Protection'] = '1'
        h = {
            'default-src': "{static}",
            'script-src': '{static} https://checkout.stripe.com https://js.stripe.com',
            'object-src': "'none'",
            'frame-src': '{static} https://checkout.stripe.com https://js.stripe.com',
            'style-src': "{static}",
            'connect-src': "{dynamic} https://checkout.stripe.com",
            'img-src': "{static} data: https://*.stripe.com",
            # form-action is not only used to match on form actions, but also on URLs
            # form-ations redirect to. In the context of e.g. payment providers or
            # single-sign-on this can be nearly anything so we cannot really restrict
            # this. However, we'll restrict it to HTTPS.
            'form-action': "{dynamic} https:",
        }
        if 'Content-Security-Policy' in resp:
            h.update(self._parse_csp(resp['Content-Security-Policy']))

        staticdomain = "'self'"
        dynamicdomain = "'self'"
        if settings.STATIC_URL.startswith('http'):
            staticdomain += " " + settings.STATIC_URL[:settings.STATIC_URL.find('/', 9)]
        if settings.SITE_URL.startswith('http'):
            if settings.SITE_URL.find('/', 9) > 0:
                staticdomain += " " + settings.SITE_URL[:settings.SITE_URL.find('/', 9)]
                dynamicdomain += " " + settings.SITE_URL[:settings.SITE_URL.find('/', 9)]
            else:
                staticdomain += " " + settings.SITE_URL
                dynamicdomain += " " + settings.SITE_URL
        resp['Content-Security-Policy'] = self._render_csp(h).format(static=staticdomain, dynamic=dynamicdomain)
        return resp

@@ -5545,14 +5545,203 @@
     
-'frame
+# frame-src is deprecated but kept for compatibility with CSP 1.0 browsers, e.g. Safari 9%0A            'frame-src': '%7Bstatic%7D https://checkout.stripe.com https://js.stripe.com',%0A            'child
 -src
@@ -6078,16 +6078,17 @@
 # form-a
+c
 tions re

Add child-src