# coding=utf-8
from __future__ import absolute_import, unicode_literals

import json
from datetime import datetime
import dateutil.parser

from django.db import transaction
from gm_types.pay import SUFPAY_STATUS, SUFPAY_CHANNEL, APP_TYPE
from gm_types.pay import REFUND_STATUS, METHOD, CHANNEL
from gm_types.gaia import ORDER_OPERATION_TYPE, ORDER_OPERATION_ROLE
from helios.rpc.exceptions import RPCFaultException

from rpc.tool.error_code import CODES, gen
from rpc.tool.log_tool import logging_exception
from rpc.decorators import bind_context, bind
from api.tool.user_tool import get_user_from_context
from weikuan.models import HospitalPay

HOSPITAL_PAYMENT = APP_TYPE.HOSPITAL_PAYMENT
HOSPITAL_PAYMENT_REFUND = APP_TYPE.HOSPITAL_PAYMENT_REFUND

_channel_key_apple = str("apple")
_channel_key_alipay = str("huabei")
_channel_key_wechat = str("wechat")
_channel_key_installment = str("installment")


_PayTools = {}

sign_to_channel = {
    _channel_key_alipay: CHANNEL.ALIPAY,
}

sign_to_method = {
    _channel_key_alipay: METHOD.ALI_OLD_APP,
}

_CHANNEL_TO_PAYMENT_CHANNEL = {
    CHANNEL.ALIPAY: SUFPAY_CHANNEL.HUABEI,
}

def payment_channel(channel):
    return _CHANNEL_TO_PAYMENT_CHANNEL[channel]


@bind_context('hospital_pay/pay', login_required=True)
def pay_hospital_pay(ctx, channel, hospital_pay_id, ip, period=None):
    with transaction.atomic():
        user = get_user_from_context(ctx)
        channel_sign = channel
        channel = sign_to_channel.get(channel_sign, None)
        method = sign_to_method.get(channel_sign, None)
        if not channel or not method:
            return gen(CODES.UNKNOWN_ERROR)

        extra = {}
        if channel == CHANNEL.ALIPAY:
            #花呗分期数校验
            if period and str(period) in ('3', '6', '12'):
                extra['hb_fq_num'] = str(period)
            else:
                return gen(CODES.ORDER_PAY_CHECK_ERROR)

        try:
            hospital_pay = HospitalPay.objects.select_for_update().get(pk=hospital_pay_id)
            if hospital_pay.order.user != user:
                raise gen(CODES.NO_PERMISSION)
        except HospitalPay.DoesNotExist:
            raise gen(CODES.SETTLEMENT_DOES_NOT_EXIST)

        #状态校验
        if hospital_pay.status == SUFPAY_STATUS.PAID:
            raise gen(CODES.SETTLEMENT_PAID)
        elif hospital_pay.status == SUFPAY_STATUS.REFUNDED:
            raise gen(CODES.SETTLEMENT_CANCELED)

        title = u'更美--{}'.format(hospital_pay.order.name)
        prv_res = ctx.rpc_remote['cadus/unify/get_or_create_payment'](
            app_type=HOSPITAL_PAYMENT,
            app_order_id=hospital_pay.id,
            app_notify_url='hospital_pay/pay/union_notify',
            total_fee=str(hospital_pay.total_fee),
            title=title
        ).unwrap()
        hospital_pay.pay_start_time = datetime.now()
        hospital_pay.period = period
        hospital_pay.save()

        try:
            result = ctx.rpc_remote['cadus/unify/prepay'](
                app_type=HOSPITAL_PAYMENT,
                app_order_id=hospital_pay.id,
                channel=channel,
                method=method,
                extra=extra
            ).unwrap()
        except RPCFaultException:
            logging_exception()
            return gen(CODES.ORDER_PAY_STATUS_ERROR)
        if channel == CHANNEL.ALIPAY:
            return {"order_info": result}


@bind_context('hospital_pay/pay/union_notify')
def pay_union_notify(ctx, data):
    with transaction.atomic():
        try:
            hospital_pay = HospitalPay.objects.select_for_update().get(pk=data['app_order_id'])
            user = hospital_pay.order.user
        except HospitalPay.DoesNotExist:
            return gen(CODES.SETTLEMENT_DOES_NOT_EXIST)

        if data['total_fee'] != hospital_pay.total_fee:
            raise Exception('通知金额不一致')

        hospital_pay.out_trade_no = data['out_trade_no']
        hospital_pay.channel = payment_channel(data['channel'])
        hospital_pay.notify_data = json.dumps(data)
        hospital_pay.transaction_id = data['transaction_id']
        hospital_pay.paid_time = dateutil.parser.parse(data['paid_time'])
        if data.get('extra'):
            if data['extra'].get('hb_fq_num'):
                hospital_pay.period = data['extra']['hb_fq_num']
        hospital_pay.save()
        hospital_pay.operate(user.person, ORDER_OPERATION_TYPE.PAY, ORDER_OPERATION_ROLE.USER)
    return "SUCCESS"


def _refund_hospital_pay(ctx, hospital_pay):
    prv_res = ctx.rpc_remote['cadus/unify/refund/get_or_create_refund'](
        payment_app_type=HOSPITAL_PAYMENT,
        payment_app_order_id=hospital_pay.id,
        refund_app_type=HOSPITAL_PAYMENT_REFUND,
        refund_app_refund_id=hospital_pay.id,
        refund_app_notify_url='hospital_pay/refund/notify',
        refund_fee=hospital_pay.total_fee
    ).unwrap()
    try:
        result = ctx.rpc_remote['cadus/unify/refund/start'](
            refund_app_type=HOSPITAL_PAYMENT_REFUND,
            refund_app_refund_id=hospital_pay.id,
        ).unwrap()
    except RPCFaultException:
        logging_exception()
        return False
    return result['start_refund_success']


@bind_context('hospital_pay/refund/notify')
def refund_union_notify(ctx, data):
    with transaction.atomic():
        try:
            hospital_pay = HospitalPay.objects.select_for_update().get(pk=data['refund_app_refund_id'])
            user = hospital_pay.order.user
        except HospitalPay.DoesNotExist:
            return gen(CODES.REFUND_ORDER_NOT_FOUND)

        if data['refund_fee'] != hospital_pay.total_fee:
            raise Exception('通知金额不一致')

        hospital_pay.refund_time = datetime.now()
        notify_data = json.loads(hospital_pay.notify_data)
        notify_data['refund_id'] = data['refund_id']
        notify_data['fail_reason'] = REFUND_STATUS.getDesc(data['fail_reason'])
        notify_data['out_refund_no'] = data['out_refund_no']
        notify_data['refund_fee'] = data['refund_fee']
        hospital_pay.notify_data = json.dumps(notify_data)
        hospital_pay.save()
        if data['status'] == REFUND_STATUS.SUCCESS:
            hospital_pay.operate(user.person, ORDER_OPERATION_TYPE.REFUNDED, ORDER_OPERATION_ROLE.USER)
        elif data['status'] == REFUND_STATUS.FAIL:
            hospital_pay.operate(user.person, ORDER_OPERATION_TYPE.REFUND_ERROR, ORDER_OPERATION_ROLE.USER)
    return "SUCCESS"
