# -*- coding: utf-8 -*-
import re
from urllib import urlencode

from .exceptions import NotFound, MethodNotAllowed
from .constants import HTTP_METHODS

# this regular expression for matching dynamic routing rule.
# Ex: /example/<int:test>
# this RE string can match this part: "<int:test>"
_rule_re = re.compile(r'''
    <
    (?:
        (?P<type>int|str|float)   # the type of variable
        \:
    )?
    (?P<variable>[a-zA-Z][a-zA-Z0-9_]*)         # the name of variable
    >
''', re.VERBOSE)

_type_map = {
    'int': r'\d+',
    'float': r'\d+(?:\.\d+)',
    'str': r'(?=.*[a-z])[\w-]+',
}


def parse_rule(rule):
    """Parse the rule of url. This func is a generater.

        s = '/example/<int:t>/test'
        l = []
        for key, value in parse_rule(s):
            l.append((key, value))

    The l is: [(example, None), (t, int), (test, None)]
    """
    rule = rule.rstrip('/').lstrip('/')
    rule_list = rule.split('/')
    for item in rule_list:
        m = _rule_re.match(item)
        if m is None:
            yield None, None, item
        else:
            data_dict = m.groupdict()
            yield data_dict['type'], data_dict['variable'], None


class Rule(object):

    def __init__(self, rule_str, rule_name=None, methods=('GET',)):
        if not rule_str.startswith('/'):
            raise ValueError('urls must start with a leading slash')
        # the origin url rule string.
        self.rule = rule_str

        # the list to store the variable-type pair.
        # Can use this list to build url. Example:
        # [('example', None), ('test', <type 'int'>), ('test2', <type 'int'>)]
        # we can use this list to build url:
        # /example/<int:test>/<int:test2>
        self.match_order = []

        # the name of the rule, it is a flag to mapping a function
        self.rule_name = rule_name

        # the support methods of the rule
        self.methods = set(item.upper() for item in methods)
        if 'GET' in self.methods and 'HEAD' not in methods:
            self.methods.add('HEAD')

        # flag for judge whether the rule is a dynamic rule
        self.dynamic = False

        # if the rule is a dynamic rule, use this param to store the variables
        # It will be like this: {'t': <type 'int'>}
        self.type_variable = {}

        # analysize the rule of url
        # it will set self.match_order, self.dynamic, self.type_variable
        self._analysize_rule(self.rule)

        # the url like "/example/{test}/{test2}"
        # through this param can build the real url if given the relative params.
        self.base_rule = self.build_base_rule()

        # the url in Regular Expression.
        # This is NOT a string, instance a object of RE
        self.rule_re = re.compile(self.complie_rule())

    def complie_rule(self):
        """Comple the rule of url into a string of Regular Expression.

            >>> r = Rule('/example/<int:test>/<int:test2>', methods=['GET, POST'])
            >>> print r.complie_rule()
            ^/example/(?P<test>\d+)/(?P<test2>\d+)$
        """
        rule_re = '^/'
        for variable, _type in self.match_order:
            if not _type:
                rule_re += variable + '/'
            else:
                type_replace = _type_map[_type.__name__]
                rule_re += r'(?P<{variable}>{type_replace})/'.format(
                    variable=variable, type_replace=type_replace
                )
        if rule_re == '^//':
            return '^/$'
        return rule_re.rstrip('/') + '$'

    def build_base_rule(self):
        """To build the base url. Example:
            >>> r = Rule('/example/<int:test>/<int:test2>', methods=['GET, POST'])
            >>> print r.build_base_rule()
            /example/{test}/{test2}
        """
        if not self.dynamic:
            return self.rule
        rule = '/'
        for variable, _type in self.match_order:
            if not _type:
                rule += variable + '/'
            else:
                rule += '{' + variable + '}' + '/'
        return rule.rstrip('/')

    def build_url(self, **kwargs):
        """According the params to build full url. Example:
            " /example/{id}  -> /example/1 "
        """
        variable_dict = self.get_variables()
        unknown_variable = set()
        # check type and find out unknown variable
        for key in kwargs:
            value = kwargs[key]