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

import io
import json
import os
import random
import re
import string
import time
from functools import wraps
from hashlib import sha1

import six

try:
    from secrets import choice
except ImportError:
    from random import choice

string_types = (six.string_types, six.text_type, six.binary_type)

re_type = type(re.compile("regex_test"))


def get_signature(token, timestamp, nonce, *args):
    sign = [token, timestamp, nonce] + list(args)
    sign.sort()
    sign = to_binary(''.join(sign))
    return sha1(sign).hexdigest()


def check_signature(token, timestamp, nonce, signature):
    if not (token and timestamp and nonce and signature):
        return False
    sign = get_signature(token, timestamp, nonce)
    return sign == signature


def check_token(token):
    return re.match('^[A-Za-z0-9]{3,32}$', token)


def cached_property(method):
    prop_name = '_{}'.format(method.__name__)

    @wraps(method)
    def wrapped_func(self, *args, **kwargs):
        if not hasattr(self, prop_name):
            setattr(self, prop_name, method(self, *args, **kwargs))
        return getattr(self, prop_name)

    return property(wrapped_func)


def to_text(value, encoding="utf-8"):
    if isinstance(value, six.text_type):
        return value
    if isinstance(value, six.binary_type):
        return value.decode(encoding)
    return six.text_type(value)


def to_binary(value, encoding="utf-8"):
    if isinstance(value, six.binary_type):
        return value
    if isinstance(value, six.text_type):
        return value.encode(encoding)
    return six.binary_type(value)


def is_string(value):
    return isinstance(value, string_types)


def byte2int(s, index=0):
    """Get the ASCII int value of a character in a string.

    :param s: a string
    :param index: the position of desired character

    :return: ASCII int value
    """
    if six.PY2:
        return ord(s[index])
    return s[index]


def generate_token(length=''):
    if not length:
        length = random.randint(3, 32)
    length = int(length)
    assert 3 <= length <= 32
    letters = string.ascii_letters + string.digits
    return ''.join(choice(letters) for _ in range(length))


def json_loads(s):
    s = to_text(s)
    return json.loads(s)


def json_dumps(d):
    return json.dumps(d)


def pay_sign_dict(
    appid,
    pay_sign_key,
    add_noncestr=True,
    add_timestamp=True,
    add_appid=True,
    **kwargs
):
    """
    支付参数签名
    """
    assert pay_sign_key, "PAY SIGN KEY IS EMPTY"

    if add_appid:
        kwargs.update({'appid': appid})

    if add_noncestr:
        kwargs.update({'noncestr': generate_token()})

    if add_timestamp:
        kwargs.update({'timestamp': int(time.time())})

    params = kwargs.items()

    _params = [
        (k.lower(), v) for k, v in kwargs.items() if k.lower() != "appid"
    ]
    _params += [('appid', appid), ('appkey', pay_sign_key)]
    _params.sort()

    sign = '&'.join(["%s=%s" % (str(p[0]), str(p[1]))
                     for p in _params]).encode("utf-8")
    sign = sha1(sign).hexdigest()
    sign_type = 'SHA1'

    return dict(params), sign, sign_type


def make_error_page(url):
    with io.open(
        os.path.join(os.path.dirname(__file__), 'contrib/error.html'),
        'r',
        encoding='utf-8'
    ) as error_page:
        return error_page.read().replace('{url}', url)


def is_regex(value):
    return isinstance(value, re_type)

[((23, 15, 23, 39), 're.compile', 're.compile', ({(23, 26, 23, 38): '"""regex_test"""'}, {}), "('regex_test')", False, 'import re\n'), ((41, 11, 41, 49), 're.match', 're.match', ({(41, 20, 41, 41): '"""^[A-Za-z0-9]{3,32}$"""', (41, 43, 41, 48): 'token'}, {}), "('^[A-Za-z0-9]{3,32}$', token)", False, 'import re\n'), ((47, 5, 47, 18), 'functools.wraps', 'wraps', ({(47, 11, 47, 17): 'method'}, {}), '(method)', False, 'from functools import wraps\n'), ((61, 11, 61, 31), 'six.text_type', 'six.text_type', ({(61, 25, 61, 30): 'value'}, {}), '(value)', False, 'import six\n'), ((69, 11, 69, 33), 'six.binary_type', 'six.binary_type', ({(69, 27, 69, 32): 'value'}, {}), '(value)', False, 'import six\n'), ((100, 11, 100, 24), 'json.loads', 'json.loads', ({(100, 22, 100, 23): 's'}, {}), '(s)', False, 'import json\n'), ((104, 11, 104, 24), 'json.dumps', 'json.dumps', ({(104, 22, 104, 23): 'd'}, {}), '(d)', False, 'import json\n'), ((91, 17, 91, 38), 'random.randint', 'random.randint', ({(91, 32, 91, 33): '3', (91, 35, 91, 37): '32'}, {}), '(3, 32)', False, 'import random\n'), ((30, 11, 30, 21), 'hashlib.sha1', 'sha1', ({(30, 16, 30, 20): 'sign'}, {}), '(sign)', False, 'from hashlib import sha1\n'), ((95, 19, 95, 34), 'random.choice', 'choice', ({(95, 26, 95, 33): 'letters'}, {}), '(letters)', False, 'from random import choice\n'), ((139, 11, 139, 21), 'hashlib.sha1', 'sha1', ({(139, 16, 139, 20): 'sign'}, {}), '(sign)', False, 'from hashlib import sha1\n'), ((147, 21, 147, 46), 'os.path.dirname', 'os.path.dirname', ({(147, 37, 147, 45): '__file__'}, {}), '(__file__)', False, 'import os\n'), ((127, 40, 127, 51), 'time.time', 'time.time', ({}, {}), '()', False, 'import time\n')]