#!/usr/bin/env python
# -*- coding:utf-8 -*-
#
#   Author  :   RobertDing
#   E-mail  :   robertdingx@gmail.com
#   Date    :   16/07/06 19:46:40
#   Desc    :   分期接口
#

import json
import hashlib
import functools
from datetime import datetime

from django.db import transaction
from django.core.exceptions import ObjectDoesNotExist

from gm_types.error import ERROR
from gm_types.gaia import XIAOYING_INSTALLMENT_STATUS
from gm_types.gaia import XIAOYING_CONFIRM_CODE, PAYMENT_CHANNEL

from rpc.context import get_rpc_remote_invoker
from rpc.decorators import bind, bind_context
from rpc.cache import installment_cache
from rpc.tool.dict_mixin import to_dict
from rpc.tool.log_tool import installment_callback_logger, logging_exception
from pay.tool.installment_error import InstallmentError, XiaoYingError, GmeiError

from pay.tool.installment import generate_extend_info, xiaoying_refund
from pay.tool.installment import Client, create_borrower_image
from pay.tool.installment import Request
from pay.tool.purchase_tool import settlement_paid_with_log
from pay.models.installment import Bank, Borrower, BorrowerBind, Installment
from pay.models.installment import BorrowerRelative
from pay.manager.settlement_manager import can_cancled_settlement_2_paid

from api.models import Settlement, Order


def paybind(endpoint):
    def _wrapper(fn):
        @functools.wraps(fn)
        def wrapper(*args, **kwargs):
            try:
                result = fn(*args, **kwargs)
            except ObjectDoesNotExist:
                raise
            return result

        return bind(endpoint)(wrapper)

    return _wrapper


@paybind('pay/installment/cities')
def pay_installment_city(partner):
    return Client().city()


@bind('pay/installment/banks')
def pay_installment_bank(partner):
    banks = Bank.objects.filter(partner=partner)
    return {x.code: to_dict(x) for x in banks}


@bind('pay/installment/calc')
def pay_installment_calculate(partner, settlement_id, amount):
    amount = Settlement.objects.get(id=settlement_id).service_price * 100
    amount = int(amount)
    client = Client()
    p6 = client.calculate(amount, 6)
    p12 = client.calculate(amount, 12)
    m = lambda p, t: {
        'period': p,
        'total': t['repayTotal'] / 100.0,
        'period_pay': t['repayData'][0]['repayTotal'] / 100.0
    }
    return {6: m(6, p6), 12: m(12, p12)}


@bind_context('pay/installment/register')
def pay_installment_register(ctx, partner, settlement_id, name, id_card, phone, password):
    if Borrower.objects.filter(partner=partner, card_id=id_card).exists():
        raise GmeiError(ERROR.USER_ALREADY_REIGST)
    password = hashlib.md5(password).hexdigest()
    register = Client().register(name, password, phone, id_card)
    uid = register['uid']
    borrower = Borrower.objects.create(
        partner=partner, person=ctx.session.user.person, name=name,
        card_id=id_card, phone=phone, third_password=password, third_uid=uid)
    if register['hasBindSecureBankCard']:
        # 没绑过卡
        if not borrower.binds.exclude(sms_code="").exists():
            card_info = register['secureBankCard']
            bank = Bank.objects.get(partner=partner, code=card_info['bankCode'])
            BorrowerBind.objects.create(
                partner=partner, bank=bank, borrower=borrower, name="",
                phone="", card_code=card_info['cardNo'], province='', city='',
                sms_code='99999998')
    return {'third_uid': uid, 'borrower_id': borrower.id}


@bind('pay/installment/userinfo')
def pay_installment_userinfo(partner, borrower_id, user_info, contacts, id_card):
    borrower = Borrower.objects.get(id=borrower_id)
    borrower.name = user_info['name']
    borrower.gender = user_info['gender']
    borrower.age = user_info['age']
    borrower.marriage = user_info['marriage']
    borrower.education = user_info['education']
    borrower.province = user_info['province']
    borrower.city = user_info['city']
    borrower.district = user_info['district']
    borrower.address = user_info['address']
    borrower.save()

    create_borrower_image(partner, borrower, id_card)

    BorrowerRelative.objects.filter(borrower=borrower).update(deleted=True)
    for contact in contacts:
        BorrowerRelative.objects.create(
            partner=partner, borrower=borrower, name=contact['name'],
            phone=contact['phone'], relation_type=contact['contact_type'],
            relationship=contact['relation'])

    return {}


@bind('pay/installment/check_bank')
def pay_installment_check_bank(partner, borrower_id, name, bank_code, province, city, card, phone):
    if BorrowerBind.objects.filter(borrower_id=borrower_id, card_code=card) \
            .exclude(sms_code="").exists():
        raise GmeiError(ERROR.CARD_ALREADY_BINDED)
    borrower = Borrower.objects.get(id=borrower_id)
    sms_code = ""
    binded_before = False  # 是否之前在小赢绑定过银行卡
    try:
        verified_data = Client(borrower.third_uid).check_bank(
            bank_code, card, phone, province, city)
    except InstallmentError as e:
        if e.data == -3000:  # 绑定卡已存在
            ticket = 1
            verified_data = {}
            sms_code = '999999999'
            binded_before = True
        else:
            raise
    else:
        ticket = verified_data['ticket']
    bank = Bank.objects.get(partner=partner, code=bank_code)
    bind = BorrowerBind.objects.create(
        partner=partner, bank=bank, borrower=borrower, name=name,
        phone=phone, card_code=card, province=province, city=city,
        sms_code=sms_code)
    _cache_key = "verified_data::{}::{}".format(borrower.id, bind.id)
    installment_cache.setex(_cache_key, 20 * 60, json.dumps(verified_data))
    return {'ticket': ticket, 'bind_id': bind.id, 'binded_before': binded_before}


@bind_context('pay/installment/bind_bank')
def pay_installment_bind_bank(ctx, partner, bind_id, sms_code):
    bind = BorrowerBind.objects.get(id=bind_id)
    _cache_key = "verified_data::{}::{}".format(bind.borrower.id, bind.id)
    verified_data = installment_cache.get(_cache_key)
    if not verified_data:
        raise GmeiError(ERROR.SMS_CODE_FAIL)
    Client(bind.borrower.third_uid).bind_bank(
        verified_data=verified_data, sms_code=sms_code)
    bind.sms_code = sms_code
    bind.save()
    return {}


@bind_context('pay/installment/loan')
def pay_installment_loan(ctx, partner, borrower_id, settlement_id, periods):
    if Installment.objects.filter(partner=partner, settlement_id=settlement_id).exists():
        raise GmeiError(ERROR.SETTLEMENT_HAS_INSTALLMENT)
    borrower = Borrower.objects.get(id=borrower_id)
    settlement = Settlement.objects.get(id=settlement_id)
    if not settlement.items.first().order.is_stage:
        raise GmeiError(ERROR.ORDER_IS_NOT_STAGE)
    amount = int(settlement.service_price * 100)
    extend = generate_extend_info(borrower, settlement, periods)
    data = Client(borrower.third_uid).create_loan(amount, periods, extend=extend)
    detail = ctx.gaia_local['pay/installment/calc'](
        partner=partner, settlement_id=settlement_id, amount=amount).unwrap()
    installment = Installment.objects.create(
        partner=partner, settlement=settlement, borrower=borrower,
        period_repay=detail[int(periods)]['period_pay'] * 100,
        bind_bank=borrower.binds.latest('created_time'), periods=periods,
        third_loan_id="", third_loan_order_id=data['loanOrderId'],
        status=XIAOYING_INSTALLMENT_STATUS.TO_BE_AUDIT)
    return {'installment_id': installment.id}


@bind_context('pay/installment/status')
def pay_installment_status(ctx, partner, settlement_id):
    borrower = ctx.session.user.person.borrower
    installment = Installment.objects.get(
        settlement_id=settlement_id, borrower=borrower, partner=partner)
    return {
        'status': installment['status'],
        'description': installment['description'],
        'supply_code': installment['supply_code'],
    }


@bind('pay/installment/replenish')
def pay_installment_replenish(partner, installment_id, card):
    installment = Installment.objects.get(id=installment_id)
    borrower = installment.borrower
    borrower.images.update(deleted=True)
    front, back, hold = create_borrower_image(partner, borrower, card)
    # XXX 妈蛋 小赢要求改格式
    card = {
        'id_card_front_url': front.public_url,
        'id_card_back_url': back.public_url,
        'id_card_hold_url': hold.public_url,
    }
    Client(borrower.third_uid).loan_replenish(installment.third_loan_order_id, card)
    installment.status = XIAOYING_INSTALLMENT_STATUS.TO_BE_AUDIT
    installment.save()
    return {}


@bind('pay/installment/refund')
def pay_installment_refund(partner, order_id):
    order = Order.objects.get(id=order_id)
    try:
        installment = order.settlementitem.settlement.installment
        old_status = installment.status
        installment.status = XIAOYING_INSTALLMENT_STATUS.REFUND
        installment.refund_time = datetime.now()
        installment.save()
        # 不管小赢给什么状态, 我们坚持认为放弃贷款成功
        Client(installment.borrower.third_uid).confirm_loan(
            installment.third_loan_order_id, XIAOYING_CONFIRM_CODE.NEGATIVE)

    except ObjectDoesNotExist:
        # XXX 用户没有完成分期支付
        return {}
    except InstallmentError:
        # XXX 审核完成前取消订单, 对方不给错误码, 我们也不管他们
        return {}
    if old_status == XIAOYING_INSTALLMENT_STATUS.AUDIT_SUCCESS:
        xiaoying_refund(order_id)
    return {}


@bind_context('pay/installment/user')
def pay_installment_user(ctx, partner):
    register = False
    borrower_id = None
    user_info = False
    bind_bank = False
    bind_id = None
    installment_id = None
    person = ctx.session.user.person
    if person.borrower_set.exists():
        borrower = person.borrower_set.latest('created_time')
        borrower_id = borrower.id
        if borrower.third_password:
            try:
                user = Client().register(
                    borrower.name, borrower.third_password,
                    borrower.phone, borrower.card_id)

                if user['hasBindSecureBankCard']:
                    # 没绑过卡
                    if not borrower.binds.exclude(sms_code="").exists():
                        card_info = user['secureBankCard']
                        bank = Bank.objects.get(partner=partner, code=card_info['bankCode'])
                        BorrowerBind.objects.create(
                            partner=partner, bank=bank, borrower=borrower,
                            name="", phone="", card_code=card_info['cardNo'],
                            province='', city='', sms_code='99999998')
            except InstallmentError:
                register = False
            else:
                register = True
        if borrower.age:  # 用户是否填写过个人信息
            user_info = True
        binds = borrower.binds.exclude(sms_code="")
        if binds.exists():
            bind_bank = True
            bind_id = binds.latest('created_time').id
        if borrower.installments.exists():
            installment = borrower.installments.latest('created_time')
            installment_id = installment.id
    return {
        'register': register,
        'borrower_id': borrower_id,
        'user_info': user_info,
        'bind_bank': bind_bank,
        'bind_id': bind_id,
        'installment_id': installment_id,
    }


@bind_context('pay/installment/callback')
def pay_installment_callback(ctx, partner, data):
    raise Exception("xiaoying应该在很久之前就没有合作了，不应该会有请求发过来")

    client = Client()  # 初始化密钥
    info = Request.decrypt(data)
    installment_callback_logger.info(dict(partner=partner, data=info, raw_data=data))

    def process_audit(kwargs):
        loan_order_id = kwargs['loanOrderId']
        result = kwargs['result']
        installment = Installment.objects.get(third_loan_order_id=loan_order_id)
        if installment.status != XIAOYING_INSTALLMENT_STATUS.TO_BE_AUDIT:
            installment_callback_logger.info({
                'message': u'审核回调, 订单状态非审核中',
                'status': installment.status,
                'third_loan_order_id': loan_order_id,
                'action': 'audit',
                'result': result,
            })
            return
        if result == 1:
            installment.status = XIAOYING_INSTALLMENT_STATUS.AUDIT_SUCCESS
            trade_data = dict(
                trade_no=installment.third_loan_order_id,
                gmt_payment=installment.settlement.service_price, **kwargs)
            with transaction.atomic():
                can_cancled_settlement_2_paid(installment.settlement)
                settlement_paid_with_log(
                    ctx=ctx,
                    settlement=installment.settlement,
                    payment_channel=PAYMENT_CHANNEL.XIAOYING,
                    trade_data=trade_data,
                )
        elif result == 2:
            installment.status = XIAOYING_INSTALLMENT_STATUS.AUDIT_REFUSE
        elif result == 3:
            installment.status = XIAOYING_INSTALLMENT_STATUS.TO_BE_REPLENISH
            installment.supply_code = kwargs['supplyCode']
        installment.audit_time = datetime.now()
        installment.save()

    def process_loan(kwargs):
        loan_order_id = kwargs['loanOrderId']
        result = kwargs['result']
        installment = Installment.objects.get(third_loan_order_id=loan_order_id)
        if installment.status != XIAOYING_INSTALLMENT_STATUS.WAIT_LEND:
            installment_callback_logger.info({
                'message': u'放款回调, 订单状态非等待放款',
                'status': installment.status,
                'third_loan_order_id': loan_order_id,
                'action': 'loan',
                'result': result,
            })
            return
        installment.confirm_time = datetime.fromtimestamp(kwargs['lendTime'])
        installment.status = {
            0: XIAOYING_INSTALLMENT_STATUS.BORROW_FAIL,
            1: XIAOYING_INSTALLMENT_STATUS.MONEY_SUCCESS,
        }[result]
        installment.save()

    def process_repay(kwargs):
        loan_id = kwargs['loanId']
        repay_flag = kwargs['repayFlag']
        repay_info = kwargs['repayInfo']
        pass

    cb_data = json.loads(info)
    notice_type = cb_data.pop('noticeType')
    try:
        ret = locals()['process_%s' % notice_type](cb_data)
    except:
        ret, msg = 1, '服务异常'
        logging_exception()
    else:
        ret, msg = 0, 'ok'
    return Request.encrypt_param(dict(ret=ret, msg=msg))
