"""
@author: sazima
@time: 2019/8/14 下午21:00
@desc:
"""
from typing import Dict, Iterable, Any, List, Tuple, Union

from django.urls import path, re_path

from .decorator import RequestMapping


class Urls(object):
    def __init__(self, urlpatterns):
        self.urlpatterns = urlpatterns


class UrlPattern(list):
    def __init__(self):
        self.urlpatterns: List[Union[path, re_path]] = list()

    def register(self, clazz):
        class_request_mapping: RequestMapping = getattr(clazz, 'request_mapping', None)
        if class_request_mapping is None:
            raise RuntimeError('view class should use request_mapping decorator.')

        # path value on class decorator
        class_path_value = class_request_mapping.value

        url_patterns_dict: Dict[Tuple[str, str], Dict] = dict()
        for func_name in dir(clazz):
            func = getattr(clazz, func_name)
            mapping: RequestMapping = getattr(func, 'request_mapping', None)
            if mapping is None:
                continue
            request_method = mapping.method
            # path value on method decorator
            method_path_value = mapping.value
            path_type = mapping.path_type
            full_value = class_path_value + method_path_value
            full_value = self._fix_path_value(full_value, path_type)

            if (path_type, full_value) in url_patterns_dict:
                temp_func_name = url_patterns_dict[(path_type, full_value)].setdefault(request_method, func_name)
                # check if method and path are duplicated
                assert temp_func_name == func_name, "path: {} with method: {} is duplicated".format(
                    full_value,
                    request_method
                )
            else:
                url_patterns_dict[(path_type, full_value)] = {request_method: func_name}

        self.update_urlpatterns(clazz, url_patterns_dict)

    def update_urlpatterns(self, clazz, url_patterns_dict):
        for (path_type, full_value), action in url_patterns_dict.items():
            if path_type == 'path':
                self.urlpatterns.append(
                    path(full_value, clazz.as_django_request_mapping_view(action))
                )
            elif path_type == 're_path':
                self.urlpatterns.append(
                    re_path(full_value, clazz.as_django_request_mapping_view(action))
                )
            else:
                raise RuntimeError('not a valid path_type')

    def __iter__(self, *args, **kwargs):
        for item in self.urlpatterns:
            yield item

    @staticmethod
    def _fix_path_value(full_value: str, path_type: str) -> str:
        # Remove redundant slants
        full_value = full_value.replace('//', '/', 1)
        if full_value.startswith('/'):
            full_value = full_value[1:]
        if path_type == 're_path':
            full_value = '^' + full_value
        return full_value

    @property
    def urls(self) -> Urls:
        """
        make to support:
            pattern = UrlPattern()
            pattern.register(UserView)
            urlpatterns = [path(r'', include(pattern.urls)]
        """
        return Urls(self.urlpatterns)

    def append(self, value: Any):
        return self.urlpatterns.append(value)

    def extend(self, iterable: Iterable[Any]):
        return self.urlpatterns.extend(iterable)

    def reverse(self):
        return self.urlpatterns.reverse()