from django.conf import settings
from django.contrib.admindocs.views import simplify_regex
from django.utils import six
from importlib import import_module

from rest_framework.views import APIView


class UrlParser(object):

    def get_apis(self, url_patterns=None, urlconf=None, filter_path=None, exclude_namespaces=None):
        """
        Returns all the DRF APIViews found in the project URLs
        patterns -- supply list of patterns (optional)
        exclude_namespaces -- list of namespaces to ignore (optional)
        """

        if not url_patterns and urlconf:
            if isinstance(urlconf, six.string_types):
                urls = import_module(urlconf)
            else:
                urls = urlconf
            url_patterns = urls.urlpatterns
        elif not url_patterns and not urlconf:
            urls = import_module(settings.ROOT_URLCONF)
            url_patterns = urls.urlpatterns

        formatted_apis = self.format_api_patterns(
            url_patterns,
            filter_path=filter_path,
            exclude_namespaces=exclude_namespaces,
        )

        return formatted_apis

    def format_api_patterns(self, url_patterns, prefix='', filter_path=None, exclude_namespaces=None):
        """
        Uses recursion to format url tree.
        patterns -- urlpatterns list
        prefix -- (optional) Prefix for URL pattern
        """
        url_patterns_list = []

        for pattern in url_patterns:

            if hasattr(pattern, 'url_patterns'):
                if exclude_namespaces and pattern.namespace in exclude_namespaces:
                    continue
                pref = prefix + pattern.regex.pattern
                url_patterns_list.extend(self.format_api_patterns(
                    pattern.url_patterns,
                    pref,
                    filter_path=filter_path,
                    exclude_namespaces=exclude_namespaces,
                ))
            else:
                endpoint_data = self.gather_endpoint_data(pattern, prefix, filter_path=filter_path)

                if endpoint_data:
                    url_patterns_list.append(endpoint_data)

        if filter_path:
            url_patterns_list = [api for api in url_patterns_list if filter_path in api['path'].strip('/')]

        return url_patterns_list

    def gather_endpoint_data(self, pattern, prefix='', filter_path=None):
        """
        Creates a dictionary for matched API urls

        pattern -- the pattern to parse
        prefix -- the API path prefix (used by recursion)
        """
        callback = self.filter_api_view_callbacks(pattern)

        if callback:
            path = simplify_regex(prefix + pattern.regex.pattern)

            if self.is_router_api_root(callback) or filter_path and filter_path not in path:
                return

            path = path.replace('<', '{').replace('>', '}')

            # These additional RegexURLPatterns paths are duplicated by DRF and not needed.
            if '.{format}' in path:
                return

            return {
                'path': path,
                'pattern': pattern,
                'callback': callback,
            }

    @classmethod
    def filter_api_view_callbacks(cls, url_pattern):
        if not hasattr(url_pattern, 'callback'):
            return

        if hasattr(url_pattern.callback, 'cls') and issubclass(url_pattern.callback.cls, APIView):
            return url_pattern.callback.cls

    @classmethod
    def is_router_api_root(cls, callback):
        """
        If URL's callback is rest_framework.routers.APIRoot returns True
        """
        if callback.__module__ == 'rest_framework.routers':
            return True