import re
from distutils.version import StrictVersion

import django.conf.urls as urls
from django.conf import settings
from django.contrib.sites.models import Site
from django.urls import resolvers
from django.utils import deprecation

from maintenancemode.models import Maintenance
from maintenancemode.utils.settings import (
    DJANGO_VERSION, MAINTENANCE_ADMIN_IGNORED_URLS, MAINTENANCE_BLOCK_STAFF)

urls.handler503 = 'maintenancemode.views.defaults.temporary_unavailable'
urls.__all__.append('handler503')

_base = deprecation.MiddlewareMixin if DJANGO_VERSION >= StrictVersion('1.10.0') else object


class MaintenanceModeMiddleware(_base):
    def process_request(self, request):
        """
        Check if the current site is in maintenance.
        """

        # First check things that don't require a database access:

        # Allow access if remote ip is in INTERNAL_IPS
        if request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS:
            return None

        # Check if the staff the user is allowed
        if hasattr(request, 'user'):
            if request.user.is_superuser:
                return None

            if not MAINTENANCE_BLOCK_STAFF and request.user.is_staff:
                return None

        # ok let's look at the db
        site = Site.objects.get_current()
        try:
            maintenance = Maintenance.objects.get(site=site)
        except Maintenance.DoesNotExist:
            # Allow access if no matching Maintenance object exists
            return None

        # Allow access if maintenance is not being performed
        if not maintenance.is_being_performed:
            return None

        # Check if a path is explicitly excluded from maintenance mode
        ignored_url_list = set(
            maintenance.ignored_url_patterns() + MAINTENANCE_ADMIN_IGNORED_URLS
        )

        ignored_url_patterns = tuple(
            re.compile(r'{}'.format(url)) for url in ignored_url_list
        )

        request_path = request.path_info.lstrip("/")

        for url in ignored_url_patterns:
            if url.match(request_path):
                return None

        # Otherwise show the user the 503 page
        resolver = resolvers.get_resolver(None)

        resolve = resolver.resolve_error_handler
        callback, param_dict = resolve('503')
        return callback(request, **param_dict)
