#! /usr/bin/env python
# -*- coding: utf-8 -*-
# __author__: vv
# Date: 2019/11/7

import json
import time
from datetime import datetime
from api.models import Merchant
from django.conf import settings
from django.db import transaction
from yibao_pay_tool import yibao
from rpc.tool.error_code import gen
from rpc.tool.error_code import CODES
from rpc.decorators import bind_context
from gm_types.gaia import YIBAOPAY_STATUS
from gm_types.artemis import RECHARGE_STATUS
from yibaopay.models import MerchantPayOrder
from api.tool.log_tool import yibao_pay_logger
from yop_security_utils import decrypt, verify_rsa
from api.tool.user_tool import get_user_from_context
from api.tool.log_tool import logging_exception
from rpc.context import get_rpc_remote_invoker
from helios.rpc import RPCFaultException
from pay.tool import random_tool


@bind_context('api/doctor/merchantpayorder/create', login_required=True)
def create_merchantpayrrder(ctx, doctor_id, data):
    """
    创建商家充值订单  # 这个接口必须保证幂等, 防止重复点击出现脏数据
    directPayType: https://open.yeepay.com/docs/e-commerceprotocols/5b7288815c99d0005da3d30d.html
    :param ctx:
    :param doctor_id:
    :param data: {"directPayType": "CMBCHINA_B2C", "amount": 100000}
    :return:
    """
    doctor = get_user_from_context(ctx)

    try:
        merchant = Merchant.objects.filter(doctor_id=doctor_id).first()
        if not merchant:
            raise Exception
    except Exception:
        if doctor.is_merchant and doctor.merchant:
            merchant = doctor.merchant
        else:
            merchant = None

    if not merchant:
        return {'url': ""}

    bank_code = data.get('bank_code')   # 支付编码
    paytype = data.get('directpaytype')  # 支付方式
    amount, good_detail = data.get('amount') or 0.01, {}

    # 创建网银支付单
    merchant_order = MerchantPayOrder.objects.create(
        id=random_tool.generate_id(id_length=12),
        merchant_id=merchant.id,
        third_app_id=settings.YIBAO_MERCHANTNO,
        paytype=paytype,
        amount=amount
    )

    # TODO 创建充值记录
    rpc_client = get_rpc_remote_invoker()
    rpc_result = {}
    try:
        rpc_result = rpc_client['artemis/account/doctor/auto_recharge/create'](
            doctor_id=merchant.doctor_id, data={
                'pay_id': merchant_order.id,
                'amount': amount, 'channel': paytype,
                'bank_code': bank_code
            }
        ).unwrap()
    except RPCFaultException:
        logging_exception()

    # 生成支付链接
    good_detail['goodsDesc'] = '%s-%s' % (
        merchant.doctor.hospital.name, merchant.doctor.name
    )
    good_detail['goodsName'] = good_detail['goodsDesc']

    token, result = yibao.get_token({
        'orderId': str(merchant_order.id),
        'orderAmount': str(merchant_order.amount),
        'goodsParamExt': json.dumps(good_detail),
    })

    recharge_id = rpc_result.get('recharge_id', '')

    if not token:
        yibao_pay_logger.error("ERROR: %s" % json.dumps(result))
        return {'url': '', 'recharge_id': recharge_id}

    casher = {
        "token": token,
        "directPayType": bank_code,
        "timestamp": str(time.time()).split('.')[0],
    }
    yibao_pay_logger.info("INFO: 【%s】" % json.dumps(casher))
    url = yibao.gen_direct_url(casher)
    yibao_pay_logger.info("INFO: 【%s】" % url)
    return {'url': url, "recharge_id": recharge_id}


@bind_context('api/merchantpayorder/purchase_notify')
def verify_notify_data(ctx, notify_data):
    """
    易宝支付回调数据
    验证数据流程:
     1. 签名验证
     2. 订单验证
     3. 金额验证
     4. 商户验证
    :param ctx:
    :param notify_data:
    :return:
    """
    # TODO 验证数据签名
    # TODO 修改订单状态
    # 这个数据是加密数据
    yibao_pay_logger.info("SUCCESS: %s" % json.dumps(notify_data))

    paid_success = False

    # 开始解密
    encry_response = notify_data.get('response', {})
    customerIdentification = notify_data.get('customerIdentification', {})
    decry_source_data, sign_to_base64 = decrypt(encry_response)
    verify_sign = verify_rsa(decry_source_data, sign_to_base64)

    decry_source_data = json.loads(decry_source_data)
    status = decry_source_data.get('status')
    order_id = decry_source_data.get('orderId')
    uniqueOrderNo = decry_source_data.get('uniqueOrderNo')
    pay_amount = float(decry_source_data.get('payAmount', 0))
    paySuccessDate = decry_source_data.get('paySuccessDate')
    merchantNo = decry_source_data.get('merchantNo')

    # 签名验证
    if not verify_sign:
        yibao_pay_logger.error("verify_rsa failed: %s" % json.dumps(decry_source_data))
        raise gen(CODES.VERIFY_FAILED)

    # 商户验证
    if customerIdentification != settings.YIBAO_APP_KEY or merchantNo != settings.YIBAO_MERCHANTNO:
        yibao_pay_logger.error("customerIdentification failed: %s" % json.dumps(decry_source_data))
        return gen(CODES.VERIFY_FAILED)

    # 订单验证
    try:
        mpo = MerchantPayOrder.objects.get(id=order_id)
    except MerchantPayOrder.DoesNotExist:
        yibao_pay_logger.error("MerchantPayOrder.DoesNotExist failed: %s" % json.dumps(decry_source_data))
        return gen(CODES.VERIFY_FAILED)

    # 验证金额
    if mpo.amount != pay_amount:
        yibao_pay_logger.error("merchantNo failed: %s" % json.dumps(decry_source_data))
        return gen(CODES.VERIFY_FAILED)

    rpc_client = get_rpc_remote_invoker()
    # 交易成功
    if status == "SUCCESS":
        MerchantPayOrder.objects.update_or_create(
            id=order_id,
            defaults={
                "pay_amount": pay_amount,
                "uniqueOrderNo": uniqueOrderNo,
                "status": YIBAOPAY_STATUS.HAS_PAY,
                "paid_time": paySuccessDate,
                "notify_data": json.dumps(notify_data),
            }
        )
        try:
            rpc_client['artemis/account/doctor/auto_recharge/update'](
                    pay_id=order_id, data={
                    'status': RECHARGE_STATUS.PAY_SUCCESS,
                    'trade_code': uniqueOrderNo,
                    'recharge_time': paySuccessDate,
                }).unwrap()
        except RPCFaultException:
            logging_exception()

        paid_success = True

    if paid_success:
        return gen(CODES.SUCCESS)
    else:
        return gen(CODES.UNKNOWN_ERROR)


