# -*- coding: utf-8 -*-
from enum import Enum

from .core import Core, RequestType


class WeChatPay(object):
    def __init__(self,
                 wechatpay_type,
                 mchid,
                 parivate_key,
                 cert_serial_no,
                 appid,
                 apiv3_key,
                 notify_url=None):
        """
        :param wechatpay_type: 微信支付类型，示例值：WeChatPayType.MINIPROG
        :param mchid: 直连商户号，示例值：'1230000109'
        :param parivate_key: 商户证书私钥，示例值：'MIIEvwIBADANBgkqhkiG9w0BAQE...'
        :param cert_serial_no: 商户证书序列号，示例值：'444F4864EA9B34415...'
        :param appid: 应用ID，示例值：'wxd678efh567hg6787'
        :param apiv3_key: 商户APIv3密钥，示例值：'a12d3924fd499edac8a5efc...'
        :param notify_url: 通知地址，示例值：'https://www.weixin.qq.com/wxpay/pay.php'
        """
        self._type = wechatpay_type
        self._mchid = mchid
        self._appid = appid
        self._notify_url = notify_url
        self._core = Core(mchid=self._mchid,
                          cert_serial_no=cert_serial_no,
                          private_key=parivate_key,
                          apiv3_key=apiv3_key)

    def pay(self,
            description,
            out_trade_no,
            amount,
            payer=None,
            time_expire=None,
            attach=None,
            goods_tag=None,
            detail=None,
            scene_info=None,
            settle_info=None,
            notify_url=None):
        """统一下单
        :return code, message:
        :param description: 商品描述，示例值：'Image形象店-深圳腾大-QQ公仔'
        :param out_trade_no: 商户订单号，示例值：'1217752501201407033233368018'
        :param amount: 订单金额，示例值：{'total':100, 'currency':'CNY'}
        :param payer: 支付者，示例值：{'openid':'oHkLxtx0vUqe-18p_AXTZ1innxkCY'}
        :param time_expire: 交易结束时间，示例值：'2018-06-08T10:34:56+08:00'
        :param attach: 附加数据，示例值：'自定义数据'
        :param goods_tag: 订单优惠标记，示例值：'WXG'
        :param detail: 优惠功能，示例值：{'cost_price':608800, 'invoice_id':'微信123', 'goods_detail':[{'merchant_goods_id':'商品编码', 'wechatpay_goods_id':'1001', 'goods_name':'iPhoneX 256G', 'quantity':1, 'unit_price':828800}]}
        :param scene_info: 场景信息，示例值：{'payer_client_ip':'14.23.150.211', 'device_id':'013467007045764', 'store_info':{'id':'0001', 'name':'腾讯大厦分店', 'area_code':'440305', 'address':'广东省深圳市南山区科技中一道10000号'}}
        :param settle_info: 结算信息，示例值：{'profit_sharing':False}
        :param notify_url: 通知地址，示例值：'https://www.weixin.qq.com/wxpay/pay.php'
        """
        params = {'appid': self._appid, 'mchid': self._mchid, 'notify_url': notify_url or self._notify_url}
        if description:
            params.update({'description': description})
        else:
            raise Exception('description is not assigned.')
        if out_trade_no:
            params.update({'out_trade_no': out_trade_no})
        else:
            raise Exception('out_trade_no is not assigned.')
        if amount:
            params.update({'amount': amount})
        else:
            raise Exception('amount is not assigned.')
        if payer:
            params.update({'payer': payer})
        if scene_info:
            params.update({'scene_info': scene_info})
        if time_expire:
            params.update({'time_expire': time_expire})
        if attach:
            params.update({'attach': attach})
        if goods_tag:
            params.update({'goods_tag': goods_tag})
        if detail:
            params.update({'detail': detail})
        if settle_info:
            params.update({'settle_info': settle_info})
        path = ""
        if self._type in [WeChatPayType.JSAPI, WeChatPayType.MINIPROG]:
            if not payer:
                raise Exception('payer is not assigned')
            path = '/v3/pay/transactions/jsapi'
        elif self._type == WeChatPayType.APP:
            path = '/v3/pay/transactions/app'
        elif self._type == WeChatPayType.H5:
            if not scene_info:
                raise Exception('scene_info is not assigned.')
            path = '/v3/pay/transactions/h5'
        elif self._type == WeChatPayType.NATIVE:
            path = '/v3/pay/transactions/native'
        if path:
            return self._core.request(path, method=RequestType.POST, data=params)
        else:
            raise Exception('path is not assigned')

    def close(self, out_trade_no):
        """关闭订单
        :param out_trade_no: 商户订单号，示例值：'1217752501201407033233368018'
        """
        if not out_trade_no:
            raise Exception('out_trade_no is not assigned.')
        path = '/v3/pay/transactions/out-trade-no/%s/close' % out_trade_no
        params = {'mchid': self._mchid}
        return self._core.request(path, method=RequestType.POST, data=params)

    def query(self, transaction_id=None, out_trade_no=None):
        """查询订单
        :param transaction_id: 微信支付订单号，示例值：1217752501201407033233368018
        :param out_trade_no: 商户订单号，示例值：1217752501201407033233368018
        """
        if not (transaction_id or out_trade_no):
            raise Exception('params is not assigned')
        if transaction_id:
            path = '/v3/pay/transactions/id/%s' % transaction_id
        else:
            path = '/v3/pay/transactions/out-trade-no/%s' % out_trade_no
        path = '%s?mchid=%s' % (path, self._mchid)
        return self._core.request(path)

    def refund(self,
               out_refund_no,
               amount,
               transaction_id=None,
               out_trade_no=None,
               reason=None,
               funds_account=None,
               goods_detail=None,
               notify_url=None):
        """申请退款
        :param out_refund_no: 商户退款单号，示例值：'1217752501201407033233368018'
        :param amount: 金额信息，示例值：{'refund':888, 'total':888, 'currency':'CNY'}
        :param transaction_id: 微信支付订单号，示例值：'1217752501201407033233368018'
        :param out_trade_no: 商户订单号，示例值：'1217752501201407033233368018'
        :param reason: 退款原因，示例值：'商品已售完'
        :param funds_account: 退款资金来源，示例值：'AVAILABLE'
        :param goods_detail: 退款商品，示例值：{'merchant_goods_id':'1217752501201407033233368018', 'wechatpay_goods_id':'1001', 'goods_name':'iPhone6s 16G', 'unit_price':528800, 'refund_amount':528800, 'refund_quantity':1}
        :param notify_url: 通知地址，示例值：'https://www.weixin.qq.com/wxpay/pay.php'
        """
        params = {'notify_url': notify_url or self._notify_url}
        if out_refund_no:
            params.update({'out_refund_no': out_refund_no})
        else:
            raise Exception('out_refund_no is not assigned.')
        if amount:
            params.update({'amount': amount})
        else:
            raise Exception('amount is not assigned.')
        if transaction_id:
            params.update({'transaction_id': transaction_id})
        if out_trade_no:
            params.update({'out_trade_no': out_trade_no})
        if reason:
            params.update({'reason': reason})
        if funds_account:
            params.update({'funds_account': funds_account})
        if goods_detail:
            params.update({'goods_detail': goods_detail})
        path = '/v3/refund/domestic/refunds'
        return self._core.request(path, method=RequestType.POST, data=params)

    def query_refund(self, out_refund_no):
        """查询单笔退款
        :param out_refund_no: 商户退款单号，示例值：'1217752501201407033233368018'
        """
        path = '/v3/refund/domestic/refunds/%s' % out_refund_no
        return self._core.request(path)

    def trade_bill(self, bill_date, bill_type='ALL', tar_type='GZIP'):
        """申请交易账单
        :param bill_date: 账单日期，示例值：'2019-06-11'
        :param bill_type: 账单类型, 默认值：'ALL'
        :param tar_type: 压缩类型，默认值：'GZIP'
        """
        path = '/v3/bill/tradebill?bill_date=%s&bill_type=%s&tar_type=%s' % (
            bill_date, bill_type, tar_type)
        return self._core.request(path)

    def fundflow_bill(self, bill_date, account_type='BASIC', tar_type='GZIP'):
        """申请资金账单
        :param bill_date: 账单日期，示例值：'2019-06-11'
        :param account_type: 资金账户类型, 默认值：'BASIC'，基本账户, 可选：'OPERATION'，运营账户；'FEES'，手续费账户
        :param tar_type: 压缩类型，默认值：'GZIP'
        """
        path = '/v3/bill/fundflowbill?bill_date=%s&account_type=%s&tar_type=%s' % (
            bill_date, account_type, tar_type)
        return self._core.request(path)

    def download_bill(self, url):
        """下载账单
        :param url: 账单下载地址，示例值：'https://api.mch.weixin.qq.com/v3/billdownload/file?token=xxx'
        """
        path = url[len(self._core._gate_way):] if url.startswith(
            self._core._gate_way) else url
        return self._core.request(path)

    # def certificates(self):
    #     """下载微信支付平台证书
    #     """
    #     return self._core.certificates

    def combine_pay(self,
                    combine_out_trade_no,
                    sub_orders,
                    scene_info=None,
                    combine_payer_info=None,
                    time_start=None,
                    time_expire=None,
                    combine_appid=None,
                    combine_mchid=None,
                    notify_url=None):
        """合单支付下单
        :param combine_out_trade_no: 合单商户订单号, 示例值：'P20150806125346'
        :param sub_orders: 子单信息，示例值：[{'mchid':'1900000109', 'attach':'深圳分店', 'amount':{'total_amount':100,'currency':'CNY'}, 'out_trade_no':'20150806125346', 'description':'腾讯充值中心-QQ会员充值', 'settle_info':{'profit_sharing':False, 'subsidy_amount':10}}]
        :param scene_info: 场景信息, 示例值：{'device_id':'POS1:123', 'payer_client_ip':'14.17.22.32'}
        :param combine_payer_info: 支付者, 示例值：{'openid':'oUpF8uMuAJO_M2pxb1Q9zNjWeS6o'}
        :param time_start: 交易起始时间，示例值：'2019-12-31T15:59:59+08:00'
        :param time_expire: 交易结束时间, 示例值：'2019-12-31T15:59:59+08:00'
        :param combine_appid: 合单商户appid, 示例值：'wxd678efh567hg6787'
        :param combine_mchid: 合单发起方商户号，示例值：'1900000109'
        :param notify_url: 通知地址, 示例值：'https://yourapp.com/notify'
        """
        params = {}
        params['combine_appid'] = combine_appid or self._appid
        params['combine_mchid'] = combine_mchid or self._mchid
        params['notify_url'] = notify_url or self._notify_url
        if combine_out_trade_no:
            params.update({'combine_out_trade_no': combine_out_trade_no})
        else:
            raise Exception('combine_out_trade_no is not assigned.')
        if sub_orders:
            params.update({'sub_orders': sub_orders})
        else:
            raise Exception('sub_orders is not assigned.')
        if scene_info:
            params.update({'scene_info': scene_info})
        if combine_payer_info:
            params.update({'combine_payer_info': combine_payer_info})
        if time_start:
            params.update({'time_start': time_start})
        if time_expire:
            params.update({'time_expire': time_expire})
        if self._type in [WeChatPayType.JSAPI, WeChatPayType.MINIPROG]:
            if not combine_payer_info:
                raise Exception('combine_payer_info is not assigned')
            path = '/v3/combine-transactions/jsapi'
        elif self._type == WeChatPayType.APP:
            path = '/v3/combine-transactions/app'
        elif self._type == WeChatPayType.H5:
            if not scene_info:
                raise Exception('scene_info is not assigned.')
            path = '/v3/combine-transactions/h5'
        elif self._type == WeChatPayType.NATIVE:
            path = '/v3/combine-transactions/native'
        return self._core.request(path, method=RequestType.POST, data=params)

    def combine_query(self, combine_out_trade_no):
        """合单查询订单
        :param combine_out_trade_no: 合单商户订单号，示例值：P20150806125346
        """
        params = {}
        if not combine_out_trade_no:
            raise Exception('param combine_out_trade_no is not assigned')
        else:
            params.update({'combine_out_trade_no': combine_out_trade_no})
        path = '/v3/combine-transactions/out-trade-no/%s' % combine_out_trade_no
        return self._core.request(path)

    def combine_close(self, combine_out_trade_no, sub_orders, combine_appid=None):
        """合单关闭订单
        :param combine_out_trade_no: 合单商户订单号，示例值：'P20150806125346'
        :param sub_orders: 子单信息, 示例值：[{'mchid': '1900000109', 'out_trade_no': '20150806125346'}]
        :param combine_appid: 合单商户appid, 示例值：'wxd678efh567hg6787'
        """
        params = {'combine_appid': combine_appid or self._appid}

        if not combine_out_trade_no:
            raise Exception('combine_out_trade_no is not assigned.')
        if not sub_orders:
            raise Exception('sub_orders is not assigned.')
        else:
            params.update({'sub_orders': sub_orders})
        path = '/v3/combine-transactions/out-trade-no/%s/close' % combine_out_trade_no
        return self._core.request(path, method=RequestType.POST, data=params)

    def sign(self, data):
        """计算签名值paySign，供JSAPI、APP、NATIVE调起支付时使用
        :param data: 需要签名的参数清单，示例值：['wx888','1414561699','5K8264ILTKCH16CQ2502S....','prepay_id=wx201410272009395522657....']
        """
        sign_str = '\n'.join(data) + '\n'
        return self._core.sign(sign_str)

    def decrypt_callback(self, headers, body):
        """解密回调接口收到的信息
        """
        return self._core.decrypt_callback(headers, body)


class WeChatPayType(Enum):
    JSAPI = 0
    APP = 1
    H5 = 2
    NATIVE = 3
    MINIPROG = 4
