import hashlib
import re
import time
import random
import requests
from company.models import CompanyCert

def wx_format_url_str(params, api_key=None):
    """
    字典序排序
    :param params:
    :param api_key:
    :return:
    """
    url = "&".join(['%s=%s' % (key, params[key]) for key in sorted(params)])
    if api_key:
        url = '%s&key=%s' % (url, api_key)
    return url
def calculate_sign(params, api_key):
    """
    计算签名
    :param params:
    :param api_key:
    :return:
    """
    # 签名步骤一：按字典序排序参数, 在string后加入KEY
    url = wx_format_url_str(params, api_key)
    # 签名步骤二：MD5加密, 所有字符转为大写
    return hashlib.md5(url.encode('utf-8')).hexdigest().upper()
def create_nonce_str():
    """
    生成32位随机字符串
    :return:
    """
    chars = 'abcdefghijklmnopqrstuvwxyz0123456789'
    return "".join([chars[random.randint(0, len(chars) - 1)] for i in range(32)])
def dict_to_xml(params):
    xml = ["<xml>", ]
    for k, v in params.items():
        xml.append('<%s>%s</%s>' % (k, v, k))
    xml.append('</xml>')
    return ''.join(xml)
def dict_to_xml2(params):
    xml = ["<xml>", ]
    for k, v in params.items():
        xml.append('<%s><![CDATA[%s]]></%s>' % (k, v, k))
    xml.append('</xml>')
    return ''.join(xml)
def xml_to_dict(xml):
    xml = xml.strip()
    if xml[:5].upper() != "<XML>" and xml[-6:].upper() != "</XML>":
        return None, None

    result = {}
    sign = None
    content = ''.join(xml[5:-6].strip().split('\n'))

    pattern = re.compile(r"<(?P<key>.+)>(?P<value>.+)</(?P=key)>")
    m = pattern.match(content)
    while m:
        key = m.group("key").strip()
        value = m.group("value").strip()
        if value != "<![CDATA[]]>":
            pattern_inner = re.compile(r"<!\[CDATA\[(?P<inner_val>.+)\]\]>")
            inner_m = pattern_inner.match(value)
            if inner_m:
                value = inner_m.group("inner_val").strip()
            if key == "sign":
                sign = value
            else:
                result[key] = value

        next_index = m.end("value") + len(key) + 3
        if next_index >= len(content):
            break
        content = content[next_index:]
        m = pattern.match(content)

    return sign, result
#创建一个随机的订单号, 方法: 时间戳 + 1000内随机数
def create_trade_no():
    now = int(time.time())
    trade_no = str(now) + str(random.randint(0,1000)).zfill(4)
    return trade_no

class WeiXinPay(object):
    def __init__(self, mch_appid, mchid, api_key, WXPAY_CLIENT_CERT_PATH, WXPAY_CLIENT_KEY_PATH):
        self.api_key = api_key
        self.WXPAY_CLIENT_CERT_PATH = WXPAY_CLIENT_CERT_PATH
        self.WXPAY_CLIENT_KEY_PATH = WXPAY_CLIENT_KEY_PATH
        self.url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers"
        self.params = {
            "mch_appid": mch_appid,
            "mchid": mchid
        }

    def update_params(self, kwargs):
        self.params["desc"] = "默认付款描述信息"
        self.params["check_name"] = "FORCE_CHECK"
        self.params.update(kwargs)
    #付款用
    def post_xml(self):
        sign = calculate_sign(self.params, self.api_key)
        self.params["sign"] = sign
        xml = dict_to_xml(self.params)
        response = requests.post(self.url, data=xml.encode('utf-8'),
                                 cert=(self.WXPAY_CLIENT_CERT_PATH, self.WXPAY_CLIENT_KEY_PATH))
        return xml_to_dict(response.text)

    #查询用, 参数名称有变!
    def post_xml2(self):
        self.params = {
            "appid": self.params['mch_appid'],
            "mch_id": self.params['mchid'],
            "partner_trade_no": self.params['partner_trade_no'],
            "nonce_str": self.params['nonce_str']
        }
        sign = calculate_sign(self.params, self.api_key)
        self.params["sign"] = sign
        xml = dict_to_xml2(self.params)
        response = requests.post(self.url, data=xml.encode('utf-8'),
                                 cert=(self.WXPAY_CLIENT_CERT_PATH, self.WXPAY_CLIENT_KEY_PATH))
        return xml_to_dict(response.text)

class Pay(WeiXinPay):
    def __init__(self, mch_appid, mchid, api_key, WXPAY_CLIENT_CERT_PATH, WXPAY_CLIENT_KEY_PATH):
        super(Pay, self).__init__(mch_appid, mchid, api_key, WXPAY_CLIENT_CERT_PATH, WXPAY_CLIENT_KEY_PATH)

    def post(self, openid, trade_no, amount, ip, name, nonce_str, desc):
        kwargs = {
            "openid": openid,
            "partner_trade_no": trade_no,
            "amount": amount,
            "spbill_create_ip": ip,
            "re_user_name": name,
            "nonce_str": nonce_str,
            "desc": desc
        }
        self.update_params(kwargs)
        return self.post_xml()[1]

class PayQuery(WeiXinPay):
    """
    查询企业付款
    """
    def __init__(self, mch_appid, mchid, api_key, WXPAY_CLIENT_CERT_PATH, WXPAY_CLIENT_KEY_PATH):
        super(PayQuery, self).__init__(mch_appid, mchid, api_key, WXPAY_CLIENT_CERT_PATH, WXPAY_CLIENT_KEY_PATH)
        self.url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/gettransferinfo"

    def post(self, trade_no, nonce_str):
        kwargs = {
            "partner_trade_no": trade_no,
            "nonce_str": nonce_str
        }
        self.update_params(kwargs)
        return self.post_xml2()[1]

def start_wx_enterprise_pay(companyCertId=None,openid=None,amount=None,trade_no=None,ip=None,name=None,desc=None):
    if trade_no:
        trade_no = str(trade_no)
    else:
        trade_no = create_trade_no()
    nonce_str = create_nonce_str()
    if not companyCertId or not openid or not amount or not name or not desc:
        return {'e': 'lack of params'}
    c = CompanyCert.objects.filter(id=int(companyCertId))
    if c:
        cert = c[0]
        pay = Pay(cert.appid, cert.mch_no, cert.api_key, cert.cert_file, cert.key_file)
        response = pay.post(openid=openid,
                            trade_no=trade_no,
                            amount=amount,
                            ip=ip,
                            name=name,
                            nonce_str=nonce_str,
                            desc=desc
                            )
        if response and response["return_code"] == "SUCCESS":
            return response
        else:
            return response
    else:
        return {'e': 'invalid companyCertId'}

def start_query_wx_enterprise_pay(trade_no=None, companyCertId=None):
    if not trade_no:
        return {'e': 'no trade number'}
    if not companyCertId:
        return {'e': 'no companyCertId'}
    c = CompanyCert.objects.filter(id=int(companyCertId))
    if c:
        cert = c[0]
        query = PayQuery(cert.appid, cert.mch_no, cert.api_key, cert.cert_file, cert.key_file)
        nonce_str = create_nonce_str()
        response = query.post(trade_no=trade_no, nonce_str=nonce_str)
        if response and response["return_code"] == "SUCCESS":
            return response
        else:
            return response
    else:
        return {'e': 'invalid companyCertId'}

