# -*- coding: utf-8 -*-

from six.moves.urllib.parse import quote, unquote, urlparse, urlencode
import hmac
import time
import hashlib
import logging
from requests.auth import AuthBase
from .cos_comm import to_unicode, to_bytes, to_str

logger = logging.getLogger(__name__)


def filter_headers(data):
    """只设置host content-type 还有x开头的头部.

    :param data(dict): 所有的头部信息.
    :return(dict): 计算进签名的头部.
    """
    valid_headers = [
        "cache-control",
        "content-disposition",
        "content-encoding",
        "content-type",
        "content-md5",
        "content-length",
        "expect",
        "expires",
        "host",
        "if-match",
        "if-modified-since",
        "if-none-match",
        "if-unmodified-since",
        "origin",
        "range",
        "transfer-encoding",
    ]
    headers = {}
    for i in data:
        if str.lower(i) in valid_headers or str.lower(i).startswith("x-cos-"):
            headers[i] = data[i]
    return headers


class CosS3Auth(AuthBase):

    def __init__(self, conf, key=None, params={}, expire=10000, sign_host=None):
        self._secret_id = conf._secret_id if conf._secret_id else \
            (conf._credential_inst.secret_id if conf._credential_inst else None)
        self._secret_key = conf._secret_key if conf._secret_key else \
            (conf._credential_inst.secret_key if conf._credential_inst else None)
        self._anonymous = conf._anonymous
        self._expire = expire
        self._params = params
        self._sign_params = conf._sign_params

        # 如果API指定了是否签名host，则以具体API为准，如果未指定则以配置为准
        if sign_host is not None:
            self._sign_host = bool(sign_host)
        else:
            self._sign_host = conf._sign_host

        if key:
            key = to_unicode(key)
            if key[0] == u'/':
                self._path = key
            else:
                self._path = u'/' + key
        else:
            self._path = u'/'

    def __call__(self, r):

        # 匿名请求直接返回
        if self._anonymous:
            r.headers['Authorization'] = ""
            logger.debug("anonymous reqeust")
            return r

        path = self._path
        uri_params = {}
        if self._sign_params:
            uri_params = self._params
        headers = filter_headers(r.headers)

        # 如果headers中不包含host头域，则从url中提取host，并且加入签名计算
        if self._sign_host:

            # 判断headers中是否包含host头域
            contain_host = False
            for i in headers:
                if str.lower(i) == "host":  # 兼容host/Host/HOST等
                    contain_host = True
                    break

            # 从url中提取host
            if not contain_host:
                url_parsed = urlparse(r.url)
                if url_parsed.hostname is not None:
                    headers["host"] = url_parsed.hostname

        # reserved keywords in headers urlencode are -_.~, notice that / should be encoded and space should not be encoded to plus sign(+)
        headers = dict([(quote(to_bytes(to_str(k)), '-_.~').lower(), quote(to_bytes(to_str(v)), '-_.~')) for k, v in
                        headers.items()])  # headers中的key转换为小写，value进行encode
        uri_params = dict([(quote(to_bytes(to_str(k)), '-_.~').lower(), quote(to_bytes(to_str(v)), '-_.~')) for k, v in
                           uri_params.items()])
        format_str = u"{method}\n{host}\n{params}\n{headers}\n".format(
            method=r.method.lower(),
            host=path,
            params='&'.join(map(lambda tupl: "%s=%s" % (tupl[0], tupl[1]), sorted(uri_params.items()))),
            headers='&'.join(map(lambda tupl: "%s=%s" % (tupl[0], tupl[1]), sorted(headers.items())))
        )
        logger.debug("format str: " + format_str)

        start_sign_time = int(time.time())
        sign_time = "{bg_time};{ed_time}".format(bg_time=start_sign_time - 60, ed_time=start_sign_time + self._expire)
        sha1 = hashlib.sha1()
        sha1.update(to_bytes(format_str))

        str_to_sign = "sha1\n{time}\n{sha1}\n".format(time=sign_time, sha1=sha1.hexdigest())
        logger.debug('str_to_sign: ' + str(str_to_sign))
        sign_key = hmac.new(to_bytes(self._secret_key), to_bytes(sign_time), hashlib.sha1).hexdigest()
        sign = hmac.new(to_bytes(sign_key), to_bytes(str_to_sign), hashlib.sha1).hexdigest()
        logger.debug('sign_key: ' + str(sign_key))
        logger.debug('sign: ' + str(sign))
        sign_tpl = "q-sign-algorithm=sha1&q-ak={ak}&q-sign-time={sign_time}&q-key-time={key_time}&q-header-list={headers}&q-url-param-list={params}&q-signature={sign}"

        r.headers['Authorization'] = sign_tpl.format(
            ak=self._secret_id,
            sign_time=sign_time,
            key_time=sign_time,
            params=';'.join(sorted(uri_params.keys())),
            headers=';'.join(sorted(headers.keys())),
            sign=sign
        )
        logger.debug("sign_key" + str(sign_key))
        logger.debug(r.headers['Authorization'])
        logger.debug("request headers: " + str(r.headers))
        return r


class CosRtmpAuth(AuthBase):

    def __init__(self, conf, bucket=None, channel=None, params={}, expire=3600, presign_expire=0):
        self._secret_id = conf._secret_id
        self._secret_key = conf._secret_key
        self._token = conf._token
        self._anonymous = conf._anonymous
        self._expire = expire
        self._presign_expire = presign_expire
        self._params = params
        if self._token:
            self._params['q-token'] = self._token
        self._path = u'/' + bucket + u'/' + channel

    def get_rtmp_sign(self):
        # get rtmp string
        canonicalized_param = ''
        for k, v in self._params.items():
            canonicalized_param += '{key}={value}&'.format(key=k, value=v)
        if self._presign_expire >= 60:
            canonicalized_param += 'presign={value}'.format(value=self._presign_expire)
        canonicalized_param = canonicalized_param.rstrip('&')
        rtmp_str = u"{path}\n{params}\n".format(path=self._path, params=canonicalized_param)
        logger.debug("rtmp str: " + rtmp_str)

        sha1 = hashlib.sha1()
        sha1.update(to_bytes(rtmp_str))
        # get time
        sign_time = int(time.time())
        sign_time_str = "{start_time};{end_time}".format(start_time=sign_time - 60, end_time=sign_time + self._expire)
        str_to_sign = "sha1\n{time}\n{sha1}\n".format(time=sign_time_str, sha1=sha1.hexdigest())
        logger.debug('str_to_sign: ' + str(str_to_sign))
        # get sinature
        signature = hmac.new(to_bytes(self._secret_key), to_bytes(str_to_sign), hashlib.sha1).hexdigest()
        logger.debug('signature: ' + str(signature))
        rtmp_sign = "q-sign-algorithm=sha1&q-ak={ak}&q-sign-time={sign_time}&q-key-time={key_time}&q-signature={sign}".format(
            ak=self._secret_id, sign_time=sign_time_str, key_time=sign_time_str, sign=signature)
        if canonicalized_param != '':
            return rtmp_sign + "&{params}".format(params=canonicalized_param)
        else:
            return rtmp_sign


if __name__ == "__main__":
    pass
