# coding=utf-8

import uuid
import requests
import json
import xmltodict
import time
from hashlib import md5
from django.conf import settings
from util.splitaccount_tool import SplitAccountTool
from util.exceptions import CustomError
from apps.foundation.models import BizLog

# 微信支付sign_type
WEIXIN_SIGN_TYPE = 'MD5'
# 服务器IP地址
WEIXIN_SPBILL_CREATE_IP = '81.70.58.181'
# 微信支付用途
WEIXIN_BODY = u'小程序支付'
# 微信统一下单URL
WEIXIN_UNIFIED_ORDER_URL = 'https://api.mch.weixin.qq.com/pay/unifiedorder'
# 微信查询订单URL
WEIXIN_QUERY_ORDER_URL = 'https://api.mch.weixin.qq.com/pay/orderquery'
# 微信支付回调API
WEIXIN_CALLBACK_API = 'https://jpm.zzly.vip/api/wechat_notify/'


class SplitAccountFuc(object):
    '''直连服务商  分账  文档https://pay.weixin.qq.com/wiki/doc/apiv3/index.shtml'''

    def __init__(self,appid, mchid, cert_serial_no, apiv3_key, proxy=None):
        self._appid = appid
        self._mchid = mchid
        self._core = SplitAccountTool(appid, mchid, cert_serial_no, apiv3_key, proxy=proxy)

    def splitaccount_order(self, transaction_id, out_order_no, receivers):
        '''
        请求分账  微信订单支付成功后，服务商代特约商户发起分账请求，将结算后的钱分到分账接收方
        注意：对同一笔订单最多能发起50次分账请求，每次请求最多分给50个接收方
        此接口采用异步处理模式，即在接收到商户请求后，优先受理请求再异步处理，最终的分账结果可以通过查询分账接口获取
        请求分账里边的openid  是需要先调用添加分账接收方接口添加分账关系
        '''
        path = "/v3/profitsharing/orders"
        params = {
            'appid': self._appid,
            'transaction_id': transaction_id,    # 微信支付订单号
            'out_order_no': out_order_no,      # 商户系统内部的分账单号，在商户系统内部唯一，同一分账单号多次请求等同一次。只能是数字、大小写字母_-|*@
            'receivers': [],
            'unfreeze_unsplit': True  # 是否解冻剩余未分金额   如果只分一次就填true
        }

        for item in receivers:
            receiver_item = {
                'type': 'PERSONAL_OPENID',
                'account': item['account'],
                'amount': int(round(item['amount'], 0)),   # 单位为分  只能为整数  不能超过原订单支付金额及最大分账比例金额
                'description': item['description']
            }

            params['receivers'].append(receiver_item)

        code, message = self._core.request(path, SplitAccountTool.POST, data=params)
        result = json.loads(message)
        if code != 200:
            raise CustomError(u'[{}]分账失败！原因：{}'.format(out_order_no, result))
        return result

    def splitaccount_addreceiver(self, account):
        '''添加分账接收方'''
        path = "/v3/profitsharing/receivers/add"
        params = {
            'appid': self._appid,
            'type': "PERSONAL_OPENID",
            'account': account,  # 接收人的openid
            'relation_type': "USER",  # body子商户与接收方的关系
        }

        code, message = self._core.request(path, SplitAccountTool.POST, data=params)
        result = json.loads(message)
        if code != 200:
            raise CustomError(u'[{}]添加分账接收方失败！原因：{}'.format(account, result))

    def splitaccount_deletereceiver(self, account):
        '''删除分账接收方'''
        path = "/v3/profitsharing/receivers/delete"
        params = {
            'appid': self._appid,
            'type': "PERSONAL_OPENID",
            'account': account,  # 接收人的openid
        }

        code, message = self._core.request(path, SplitAccountTool.POST, data=params)
        result = json.loads(message)
        if code != 200:
            raise CustomError(u'[{}]删除分账接收方失败！原因：{}'.format(account, result))

    def splitaccount_orderquery(self, transaction_id, out_order_no):
        '''
        查询分账结果
        transaction_id  微信支付订单号
        out_order_no  商户分账单号
        '''
        if transaction_id and out_order_no:
            path = '/v3/profitsharing/orders/%s?transaction_id=%s' % (out_order_no, transaction_id)
        else:
            raise CustomError(u'[%s]查询分账结果失败！原因：参数错误！' % out_order_no)

        code, message = self._core.request(path)
        result = json.loads(message)
        if code != 200:
            raise CustomError(u'[{}]查询分账结果失败！原因：{}'.format(out_order_no, result))
        return result

    def splitaccount_return(self):
        '''请求分账回退'''
        pass

    def splitaccount_returnquery(self):
        '''查询分账回退结果'''
        pass

    def splitaccount_unfreeze(self, transaction_id, out_order_no):
        '''解冻剩余资金'''
        path = "/v3/profitsharing/orders"
        params = {
            'transaction_id': transaction_id,  # 微信支付订单号
            'out_order_no': out_order_no,  # 商户系统内部的分账单号，在商户系统内部唯一，同一分账单号多次请求等同一次。只能是数字、大小写字母_-|*@
            'description': "解冻资金"
        }

        code, message = self._core.request(path, SplitAccountTool.POST, data=params)
        result = json.loads(message)
        if code != 200:
            raise CustomError(u'[{}]解冻剩余资金失败！原因：{}'.format(out_order_no, result))
        return result

    def splitaccount_amountquery(self):
        '''查询剩余待分金额'''
        pass

    def splitaccount_configquery(self):
        '''查询最大分账比例'''
        pass

    def splitaccount_bill(self):
        '''申请分账账单'''
        pass


class WeChatResponse():
    def __init__(self,appid, agent_num, agent_key):
        self.params = {
            "appid": appid,
            'mch_id': agent_num,
            'nonce_str': '',
            'sign_type': WEIXIN_SIGN_TYPE,
            'sign': '',
            'out_trade_no': '',
        }
        self.prepay_id = None
        self.merchant_key = agent_key

    # 查询订单
    def orderquery(self, out_trade_no):
        self.params['out_trade_no'] = out_trade_no
        self.params['nonce_str'] = generate_nonce_str()
        self.params['sign'] = generate_sign(self.params, self.merchant_key)
        data = xmltodict.unparse({'xml': self.params}, pretty=True, full_document=False).encode('utf-8')
        headers = {'Content-Type': 'application/xml'}
        res = requests.post(WEIXIN_QUERY_ORDER_URL, data=data, headers=headers)
        if res.status_code != 200:
            raise CustomError(u'微信请求失败！')
        result = json.loads(json.dumps(xmltodict.parse(res.content)))

        if result['xml']['return_code'] != 'SUCCESS':
            raise CustomError(u'微信通信失败！[%s]' % result['xml']['return_msg'])
        print(u'微信交易状态！[%s]' % (result['xml']['trade_state_desc']))
        if result['xml']['trade_state'] == 'NOTPAY':
            return result['xml']['total_fee']
            # raise CustomError(u'微信交易状态！[%s]' % (result['xml']['trade_state_desc']))
        # 其他状态，返回金额0
        return 0
        # return result['xml']['total_fee']

class WechatPay():

    def __init__(self, appid, mch_id, merchant_key):
        self.params = {
            'appid': appid,
            'mch_id': mch_id,
            'nonce_str': '',
            'sign_type': WEIXIN_SIGN_TYPE,
            'body': WEIXIN_BODY,
            'out_trade_no': '',
            'total_fee': '',
            'spbill_create_ip': WEIXIN_SPBILL_CREATE_IP,
            'notify_url': WEIXIN_CALLBACK_API + appid + '/',
            'trade_type': 'JSAPI'
        }
        self.prepay_id = None
        self.merchant_key = merchant_key

    def getAppString(self):
        data = {
            'appId': self.params['appid'],
            'signType': WEIXIN_SIGN_TYPE,
            'package': "prepay_id={}".format(self.prepay_id),
            'nonceStr': generate_nonce_str(),
            'timeStamp': str(int(time.time()))
        }
        data['paySign'] = generate_sign(data, self.merchant_key)
        data.pop('appId')
        return data

    def unifiedOrder(self,out_trade_no,total_fee, openid, profit_sharing):
        self.params['profit_sharing'] = profit_sharing  # 是否分账参数  Y  需要分账  N 不分账  字母大写默认不分账
        self.params['out_trade_no'] = out_trade_no
        self.params['total_fee'] = int(round(total_fee, 0))
        self.params['openid'] = openid
        self.params['nonce_str'] = generate_nonce_str()
        self.params['sign'] = generate_sign(self.params, self.merchant_key)
        data = xmltodict.unparse({'xml': self.params}, pretty=True, full_document=False).encode('utf-8')

        headers = {'Content-Type': 'application/xml'}
        res = requests.post(WEIXIN_UNIFIED_ORDER_URL, data=data, headers=headers)

        if res.status_code != 200:
            raise CustomError(u'微信请求失败！')

        result = json.loads(json.dumps(xmltodict.parse(res.content)))
        if result['xml']['return_code'] != 'SUCCESS':
            raise CustomError(u'微信通信失败！[%s]' % result['xml']['return_msg'])
        if result['xml']['result_code'] != 'SUCCESS':
            raise CustomError(u'微信交易失败！[%s:%s]' % (result['xml']['err_code'],result['xml']['err_code_des']))

        self.prepay_id = result['xml']['prepay_id']

        return result['xml']

class WechatPayNotify():
    def __init__(self,params, merchant_key):
        self.params = params
        self.merchant_key = merchant_key

    def handle(self):
        resp_dict = json.loads(json.dumps(xmltodict.parse(self.params)))['xml']
        return_code = resp_dict['return_code']

        if return_code != 'SUCCESS':
            return None

        if not validate_sign(resp_dict, self.merchant_key):
            return None

        return resp_dict

    @staticmethod
    def response_ok():
        return_info = {
            'return_code': 'SUCCESS',
            'return_msg': 'OK'
        }
        return generate_response_data(return_info)

    @staticmethod
    def response_fail():
        return_info = {
            'return_code': 'FAIL',
            'return_msg': 'FAIL'
        }
        return generate_response_data(return_info)

def generate_nonce_str():
    """
    生成随机字符串
    """
    return str(uuid.uuid4()).replace('-', '')

def generate_sign(params, merchant_key):
    """
    生成md5签名的参数
    """
    if 'sign' in params:
        params.pop('sign')
    src = '&'.join(['%s=%s' % (k, v) for k, v in sorted(params.items())]) + '&key=%s' % merchant_key
    return md5(src.encode('utf-8')).hexdigest().upper()

def validate_sign(resp_dict, merchant_key):
    """
    验证微信返回的签名
    """
    if 'sign' not in resp_dict:
        return False
    wx_sign = resp_dict['sign']
    sign = generate_sign(resp_dict, merchant_key)
    if sign == wx_sign:
        return True
    return False

def generate_response_data(resp_dict):
    """
    字典转xml
    """
    return xmltodict.unparse({'xml': resp_dict}, pretty=True, full_document=False).encode('utf-8')
