# -*- coding: utf-8 -*-
from __future__ import unicode_literals

import json
import logging
import re
import sys
import types

from django.core.serializers.json import DjangoJSONEncoder
from django.urls import URLPattern, URLResolver, get_script_prefix
from django.utils import six

from djangojs.conf import settings

logger = logging.getLogger(__name__)


__all__ = (
    'urls_as_dict',
    'urls_as_json',
)

RE_KWARG = re.compile(r"(\(\?P\<(.*?)\>.*?\))")  # Pattern for recongnizing named parameters in urls
RE_ARG = re.compile(r"(\(.*?\))")  # Pattern for recognizing unnamed url parameters
RE_OPT = re.compile(r"(?:\w|/)(?:\?|\*)")  # Pattern for recognizing optionnal character
RE_OPT_GRP = re.compile(r"\(\?\:.*\)(?:\?|\*)")  # Pattern for recognizing optionnal group
RE_ESCAPE = re.compile(r'([^\\]?)\\')  # Recognize escape characters
RE_START_END = re.compile(r'[\$\^]')  # Recognize start and end charaters

try:  # check for django-cms
    from cms.appresolver import AppRegexURLResolver
    CMS_APP_RESOLVER = True
except:
    CMS_APP_RESOLVER = False  # we can live without it


def urls_as_dict():
    '''
    Get the URLs mapping as a dictionnary
    '''
    module = settings.JS_URL_CONFS
    urls = {}
    if settings.JS_URLS_ENABLED:
        if isinstance(module, (six.text_type, six.string_types)):
            urls = _get_urls(module)
        else:
            for item in module:
                urls = dict(urls.items() + _get_urls(item).items())
    else:
        return {}

    return urls


def urls_as_json():
    '''
    Get the URLs mapping as JSON
    '''
    return json.dumps(urls_as_dict(), cls=DjangoJSONEncoder)


def _get_urls_for_pattern(pattern, prefix='', namespace=None):
    urls = {}

    if prefix is '':
        prefix = get_script_prefix()

    if issubclass(pattern.__class__, URLPattern):
        if settings.JS_URLS_UNNAMED:
            mod_name, obj_name = pattern.callback.__module__, pattern.callback.__name__
            try:
                module = __import__(mod_name, fromlist=[obj_name])
                obj = getattr(module, obj_name)
                func_name = "{0}.{1}".format(mod_name, obj_name) if isinstance(obj, types.FunctionType) else None
                pattern_name = pattern.name or func_name
            except:
                pattern_name = pattern.name
        else:
            pattern_name = pattern.name

        if pattern_name:
            if settings.JS_URLS and pattern_name not in settings.JS_URLS:
                return {}
            if settings.JS_URLS_EXCLUDE and pattern_name in settings.JS_URLS_EXCLUDE:
                return {}
            if namespace:
                pattern_name = ':'.join((namespace, pattern_name))
            full_url = prefix + pattern.pattern.regex.pattern
            for char in ['^', '$']:
                full_url = full_url.replace(char, '')
            # remove optionnal non capturing groups
            opt_grp_matches = RE_OPT_GRP.findall(full_url)
            if opt_grp_matches:
                for match in opt_grp_matches:
                    full_url = full_url.replace(match, '')
            # remove optionnal characters
            opt_matches = RE_OPT.findall(full_url)
            if opt_matches:
                for match in opt_matches:
                    full_url = full_url.replace(match, '')
            # handle kwargs, args
            kwarg_matches = RE_KWARG.findall(full_url)
            if kwarg_matches:
                for el in kwarg_matches:
                    # prepare the output for JS resolver
                    full_url = full_url.replace(el[0], "<%s>" % el[1])
            # after processing all kwargs try args
            args_matches = RE_ARG.findall(full_url)
            if args_matches:
                for el in args_matches:
                    full_url = full_url.replace(el, "<>")  # replace by a empty parameter name
            # Unescape charaters
            full_url = RE_ESCAPE.sub(r'\1', full_url)
            urls[pattern_name] = full_url
    elif (CMS_APP_RESOLVER) and (issubclass(pattern.__class__, AppRegexURLResolver)):  # hack for django-cms
        for p in pattern.url_patterns:
            urls.update(_get_urls_for_pattern(p, prefix=prefix, namespace=namespace))
    elif issubclass(pattern.__class__, URLResolver):
        if pattern.urlconf_name:
            if pattern.namespace and not pattern.app_name:
                # Namespace without app_name
                nss = [pattern.namespace]
            else:
                # Add urls twice: for app and instance namespace
                nss = set((pattern.namespace, pattern.app_name))
            for ns in nss:
                namespaces = [nsp for nsp in (namespace, ns) if nsp]
                namespaces = ':'.join(namespaces)
                if settings.JS_URLS_NAMESPACES and namespaces and namespaces not in settings.JS_URLS_NAMESPACES:
                    continue
                if settings.JS_URLS_NAMESPACES_EXCLUDE and namespaces in settings.JS_URLS_NAMESPACES_EXCLUDE:
                    continue
                new_prefix = '%s%s' % (prefix, pattern.pattern.regex.pattern)
                urls.update(_get_urls(pattern.urlconf_name, new_prefix, namespaces))

    return urls


def _get_urls(module, prefix='', namespace=None):
    urls = {}
    if isinstance(module, (six.text_type, six.string_types)):
        try:
            __import__(module)
            root_urls = sys.modules[module]
            patterns = root_urls.urlpatterns
        except ImportError:  # die silently
            patterns = tuple()
    elif isinstance(module, (list, tuple)):
        patterns = module
    elif isinstance(module, types.ModuleType):
        patterns = module.urlpatterns
    else:
        raise TypeError('Unsupported type: %s' % type(module))

    for pattern in patterns:
        urls.update(_get_urls_for_pattern(pattern, prefix=prefix, namespace=namespace))

    return urls
