# -*- coding: utf-8 -*-
'''
Created on 2018-9-17

@author: xiaoxuan.lp
'''

try: import http.client
except ImportError:
    import http.client as httplib
import urllib.request, urllib.parse, urllib.error
import time
import hashlib
import json
import dingtalk
import itertools
import mimetypes
import hmac
import base64

'''
定义一些系统变量
'''

SYSTEM_GENERATE_VERSION = "taobao-sdk-python-dynamicVersionNo"

P_APPKEY = "app_key"
P_API = "method"
P_ACCESS_TOKEN = "access_token"
P_VERSION = "v"
P_FORMAT = "format"
P_TIMESTAMP = "timestamp"
P_SIGN = "sign"
P_SIGN_METHOD = "sign_method"
P_PARTNER_ID = "partner_id"

P_CODE = 'errcode'
P_MSG = 'errmsg'

def sign(secret, parameters):
    #===========================================================================
    # '''签名方法
    # @param secret: 签名需要的密钥
    # @param parameters: 支持字典和string两种
    # '''
    #===========================================================================
    # 如果parameters 是字典类的话
    if hasattr(parameters, "items"):
        keys = list(parameters.keys())
        keys.sort()
        
        parameters = "%s%s%s" % (secret,
            str().join('%s%s' % (key, parameters[key]) for key in keys),
            secret)
    sign = hashlib.md5(parameters.encode("utf-8")).hexdigest().upper()
    return sign

def mixStr(pstr):
    if(isinstance(pstr, str)):
        return pstr
    elif(isinstance(pstr, str)):
        return pstr.encode('utf-8')
    else:
        return str(pstr)
    
class FileItem(object):
    def __init__(self,filename=None,content=None):
        self.filename = filename
        self.content = content

class MultiPartForm(object):
    """Accumulate the data to be used when posting a form."""

    def __init__(self):
        self.form_fields = []
        self.files = []
        self.boundary = "PYTHON_SDK_BOUNDARY"
        return
    
    def get_content_type(self):
        return 'multipart/form-data;charset=UTF-8; boundary=%s' % self.boundary

    def add_field(self, name, value):
        """Add a simple field to the form data."""
        self.form_fields.append((name, str(value)))
        return

    def add_file(self, fieldname, filename, fileHandle, mimetype=None):
        """Add a file to be uploaded."""
        body = fileHandle.read()
        if mimetype is None:
            mimetype = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
        self.files.append((mixStr(fieldname), mixStr(filename), mixStr(mimetype), mixStr(body)))
        return
    
    def __str__(self):
        """Return a string representing the form data, including attached files."""
        # Build a list of lists, each containing "lines" of the
        # request.  Each part is separated by a boundary string.
        # Once the list is built, return a string where each
        # line is separated by '\r\n'.  
        parts = []
        part_boundary = '--' + self.boundary
        
        # Add the form fields
        parts.extend(
            [ part_boundary,
              'Content-Disposition: form-data; name="%s"' % name,
              'Content-Type: text/plain; charset=UTF-8',
              '',
              value,
            ]
            for name, value in self.form_fields
            )
        
        # Add the files to upload
        parts.extend(
            [ part_boundary,
              'Content-Disposition: form-data; name="%s"; filename="%s"' % \
                 (field_name, filename),
              'Content-Type: %s' % content_type,
              'Content-Transfer-Encoding: binary',
              '',
              body,
            ]
            for field_name, filename, content_type, body in self.files
            )
        
        # Flatten the list and add closing boundary marker,
        # then return CR+LF separated data
        flattened = list(itertools.chain(*parts))
        flattened.append('--' + self.boundary + '--')
        flattened.append('')
        return '\r\n'.join(flattened)

class TopException(Exception):
    #===========================================================================
    # 业务异常类
    #===========================================================================
    def __init__(self):
        self.errcode = None
        self.errmsg = None
        self.application_host = None
        self.service_host = None
    
    def __str__(self, *args, **kwargs):
        sb = "errcode=" + mixStr(self.errcode) +\
            " errmsg=" + mixStr(self.errmsg) +\
            " application_host=" + mixStr(self.application_host) +\
            " service_host=" + mixStr(self.service_host)
        return sb
       
class RequestException(Exception):
    #===========================================================================
    # 请求连接异常类
    #===========================================================================
    pass

class RestApi(object):
    #===========================================================================
    # Rest api的基类
    #===========================================================================
    
    def __init__(self, url=None):
        #=======================================================================
        # 初始化基类
        # Args @param domain: 请求的域名或者ip
        #      @param port: 请求的端口
        #=======================================================================
        if(url == None):
            raise RequestException("domain must not be empty.")
        if(url.find('http://') >= 0):
            self.__port = 80
            pathUrl = url.replace('http://','')
        elif(url.find('https://') >= 0):
            self.__port = 443
            pathUrl = url.replace('https://','')
        else:
            raise RequestException("http protocol is not validate.")
        
        index = pathUrl.find('/')
        if(index > 0):
            self.__domain = pathUrl[0:index]
            self.__path = pathUrl[index:]
        else:
            self.__domain = pathUrl
            self.__path = ''

        # print("domain:" + self.__domain + ",path:" + self.__path + ",port:" + str(self.__port))
        
    def get_request_header(self):
        return {
            'Content-type': 'application/json;charset=UTF-8',
            "Cache-Control": "no-cache",
            "Connection": "Keep-Alive",
        }

    def getHttpMethod(self):
        return "GET"
        
    def getapiname(self):
        return ""
    
    def getMultipartParas(self):
        return [];

    def getTranslateParas(self):
        return {};
    
    def _check_requst(self):
        pass
    
    def getResponse(self, authrize='',accessKey='',accessSecret='',suiteTicket='',corpId='', timeout=30):
        #=======================================================================
        # 获取response结果
        #=======================================================================
        if(self.__port == 443):
            connection = http.client.HTTPSConnection(self.__domain, self.__port, None, None, timeout)
        else:
            connection = http.client.HTTPConnection(self.__domain, self.__port, timeout)
        sys_parameters = {
            P_PARTNER_ID: SYSTEM_GENERATE_VERSION,
        }
        if authrize is not None:
            sys_parameters[P_ACCESS_TOKEN] = authrize
        application_parameter = self.getApplicationParameters()
        sign_parameter = sys_parameters.copy()
        sign_parameter.update(application_parameter)
        
        header = self.get_request_header();
        if(self.getMultipartParas()):
            form = MultiPartForm()
            for key, value in list(application_parameter.items()):
                form.add_field(key, value)
            for key in self.getMultipartParas():
                fileitem = getattr(self,key)
                if(fileitem and isinstance(fileitem,FileItem)):
                    form.add_file(key,fileitem.filename,fileitem.content)
            body = str(form)
            header['Content-type'] = form.get_content_type()
        else:
            body = urllib.parse.urlencode(application_parameter)
        
        if(accessKey != ''):
            timestamp = str(int(round(time.time()))) + '000'
            print(("timestamp:" + timestamp))
            canonicalString = self.getCanonicalStringForIsv(timestamp, suiteTicket)
            print(("canonicalString:" + canonicalString))
            print(("accessSecret:" + accessSecret))
            signature = self.computeSignature(accessSecret, canonicalString)
            print(("signature:" + signature))
            ps = {}
            ps["accessKey"] = accessKey
            ps["signature"] = signature
            ps["timestamp"] = timestamp
            if(suiteTicket != ''):
                ps["suiteTicket"] = suiteTicket
            if(corpId != ''):
                ps["corpId"] = corpId
            queryStr = urllib.parse.urlencode(ps)
            if (self.__path.find("?") > 0):
                fullPath = self.__path + "&" + queryStr
            else:
                fullPath = self.__path + "?" + queryStr
            print(("fullPath:" + fullPath))
        else:
            if (self.__path.find("?") > 0):
                fullPath = (self.__path + "&access_token=" + str(authrize)) if len(str(authrize)) > 0 else self.__path
            else:
                fullPath = (self.__path + "?access_token=" + str(authrize)) if len(str(authrize)) > 0 else self.__path

        if(self.getHttpMethod() == "GET"):
            if (fullPath.find("?") > 0):
                fullPath = fullPath + "&" + body;
            else:
                fullPath = fullPath + "?" + body
            connection.request(self.getHttpMethod(), fullPath, headers=header)
        else:
            if (self.getMultipartParas()):
                body = body
            else:
                body = json.dumps(application_parameter)
            connection.request(self.getHttpMethod(), fullPath, body=body, headers=header)
        response = connection.getresponse();
        if response.status is not 200:
            raise RequestException('invalid http status ' + str(response.status) + ',detail body:' + response.read())
        result = response.read()
        # print("result:" + result)
        jsonobj = json.loads(result)
        if P_CODE in jsonobj and jsonobj[P_CODE] != 0:
            error = TopException()
            error.errcode = jsonobj[P_CODE]
            error.errmsg = jsonobj[P_MSG]
            error.application_host = response.getheader("Application-Host", "")
            error.service_host = response.getheader("Location-Host", "")
            raise error
        return jsonobj
    
    def getCanonicalStringForIsv(self, timestamp, suiteTicket):
        if(suiteTicket != ''):
            return timestamp + '\n' + suiteTicket
        else:
            return timestamp

    def computeSignature(self, secret, canonicalString):
        message = canonicalString.encode(encoding="utf-8")
        sec = secret.encode(encoding="utf-8")
        return str(base64.b64encode(hmac.new(sec, message, digestmod=hashlib.sha256).digest()))
    
    def getApplicationParameters(self):
        application_parameter = {}
        for key, value in self.__dict__.items():
            if not key.startswith("__") and not key in self.getMultipartParas() and not key.startswith("_RestApi__") and value is not None :
                if(key.startswith("_")):
                    application_parameter[key[1:]] = value
                else:
                    application_parameter[key] = value
        #查询翻译字典来规避一些关键字属性
        translate_parameter = self.getTranslateParas()
        for key, value in application_parameter.items():
            if key in translate_parameter:
                application_parameter[translate_parameter[key]] = application_parameter[key]
                del application_parameter[key]
        return application_parameter
