import re

from importlib import import_module
from urllib.parse import urljoin

from django.conf import settings
from django.core.exceptions import ValidationError as DjangoValidationError
from django.urls import NoReverseMatch

from rest_framework import status
from rest_framework.response import Response
from rest_framework.reverse import reverse
from rest_framework.views import (
    exception_handler as original_exception_handler,

from normandy.base.api.utils import get_api_endpoints
from normandy.base.decorators import api_cache_control

class APIRootView(APIView):
    An API root view that lists the urls that share it's version namespace.

    _ignore_model_permissions = True
    exclude_from_schema = True
    urlconf = None

    def get(self, request, *args, **kwargs):
        ret = {}

        # Get the version namespace
        namespace = getattr(request.resolver_match, "namespace", "")
        for ns in namespace.split(":"):
            if re.match(r"v[0-9]+", ns, re.I):
                namespace = ns

        if self.urlconf:
            urlconf = self.urlconf
            urlconf = import_module(settings.ROOT_URLCONF)
        endpoints = get_api_endpoints(urlconf.urlpatterns)

        for endpoint in sorted(endpoints, key=lambda e: e["pattern"].name):
            name = endpoint["pattern"].name
            if endpoint["method"] == "GET" and namespace in endpoint["namespace"].split(":"):
                allow_cdn = getattr(endpoint["pattern"], "allow_cdn", True)

                if not allow_cdn and settings.APP_SERVER_URL:
                    base = settings.APP_SERVER_URL
                    base = request.build_absolute_uri()

                    full_name = (
                        f'{endpoint["namespace"]}:{name}' if endpoint["namespace"] else name
                    path = reverse(full_name, *args, **kwargs)
                except NoReverseMatch:

                full_url = urljoin(base, path)
                ret[name] = full_url

        return Response(ret)

def exception_handler(exc, context):
    Returns the response that should be used for any given exception.

    Adds support the DRF default to also handle django.core.exceptions.ValidationError

    Any unhandled exceptions may return `None`, which will cause a 500 error
    to be raised.
    response = original_exception_handler(exc, context)

    if response:
        return response

    elif isinstance(exc, DjangoValidationError):
        data = {"messages": exc.messages}
        return Response(data, status=status.HTTP_400_BAD_REQUEST)

    return None