#!/usr/bin/env python
# coding=utf-8
import datetime
import json

from dateutil.relativedelta import relativedelta
from django.db import transaction
from django.db.models import Sum
from gm_types.doctor import BUDAN_LURU_STATUS
from gm_types.gaia import BUDAN_STATUS, BUDAN_OPERATOR, BUDAN_OPERATE, ORDER_STATUS, BUDAN_TYPE, SINGLE_TYPE
from gm_types.doctor import BUDAN_LURU_STATUS

from api.models.bd_transfer import BDTransferSingleRecord, BDTransferSingleRecordLog
from api.models import BuDanLuRu, BuDanRealtedOrder
from rpc.decorators import bind_context, list_interface
from rpc.tool.error_code import CODES, gen
from api.tool.user_tool import filter_user_nick_name
from api.tool.user_tool import get_doctor_from_context
from api.models import Order, BuDan, BuDanRecord, BuDanSettlement, Doctor, BuDanLuRu


def get_all_doctors(merchant, from_select=False):
    # 找到商户下所有的用户,包括商户本身. e.g.私立医院 BCCKGJLSHZ
    if from_select:
        user_ids = [merchant.user_id]
        doctor_ids = [merchant.id]
    else:
        doctors = merchant.merchant_doctors()
        user_ids = [d.user_id for d in doctors]
        doctor_ids = [d.id for d in doctors]
    return doctor_ids, user_ids


@bind_context('doctor/budan/create')
def new_budan(ctx, phone, order_id, dev_projects, comment, budan_type=BUDAN_TYPE.GENERAL_BUDAN,
              bdtransfer_singlerecord_id=None):
    doctor = get_doctor_from_context(ctx)
    if not doctor:
        return gen(CODES.DOCTOR_NOT_FOUND)

    params = dict(doctor_id=doctor.id, dev_projects=dev_projects, user_phone=phone,
                  create_user=doctor.user, happen_time=datetime.datetime.now(),
                  budan_type=budan_type, platform=BUDAN_OPERATOR.DOCTOR, comment=comment)
    if order_id:
        order = Order.objects.get(id=order_id)
        params['order_id'] = order_id
        params['service_id'] = order.service_id
        params['user_id'] = order.user_id
    if budan_type == BUDAN_TYPE.GENERAL_BUDAN:
        #计算应补佣金
        projects = json.loads(dev_projects)
        pay_ment = 0
        for item in projects:
            commission_rate = item.get('commission_rate', 10)
            pay_ment += int(item['money']) * int(commission_rate) / 100.0
        params.update(payment=pay_ment)
    with transaction.atomic():
        budan = BuDan.create_budan(**params)
        if bdtransfer_singlerecord_id:
            budan.bdtransfer_singlerecord_id = bdtransfer_singlerecord_id
            budan.save()
            bdtransfer_singlerecord_obj = BDTransferSingleRecord.objects.get(id=bdtransfer_singlerecord_id)
            bdtransfer_singlerecord_obj.status = SINGLE_TYPE.HAS_ORDER_FORM
            bdtransfer_singlerecord_obj.budan = budan
            bdtransfer_singlerecord_obj.save()
            BDTransferSingleRecordLog.objects.create(singlerecord=bdtransfer_singlerecord_obj,
                                                     status=bdtransfer_singlerecord_obj.status)
    return {
        'id': budan.id,
    }


@bind_context('doctor/budan/settlement')
@list_interface(offset_name='start_num', limit_name='count', element_model=BuDan)
def budan_settlment(ctx, month, doctor_id=None, from_select=False, bd_type=None, order_id=None, phone=None,
                    start_num=0, count=10):
    if doctor_id is None:
        doctor = get_doctor_from_context(ctx)
    else:
        doctor = Doctor.objects.get(id=doctor_id)
    if not doctor:
        return gen(CODES.DOCTOR_NOT_FOUND)

    if month:
        # month 格式是 %Y-%m
        dts = month.split('-')
        month = datetime.date(int(dts[0]), int(dts[1]), 1)
    else:
        now = datetime.datetime.now()
        month = datetime.date(now.year, now.month, 1)

    if doctor.is_merchant and not from_select:
        doctor_ids, user_ids = get_all_doctors(doctor)
        result = statistic_current_month_info(doctor, doctor_ids, user_ids)
    else:
        doctor_ids = [doctor.id]
        result = {}
    s_date = datetime.date(2019, 4, 1)
    tmp = month - s_date
    if tmp.days < 0:
        f = BuDan.objects.filter(
            doctor_id__in=doctor_ids,
            create_time__year=month.year,
            create_time__month=month.month,
        )
    else:
        if not bd_type:
            f = BuDan.objects.filter(
                doctor_id__in=doctor_ids,
                create_time__year=month.year,
                create_time__month=month.month,
                budan_type=BUDAN_TYPE.GENERAL_BUDAN
            )
        else:
            f = BuDan.objects.filter(
                doctor_id__in=doctor_ids,
                create_time__year=month.year,
                create_time__month=month.month,
                budan_type=BUDAN_TYPE.TRANSFER_BUDAN
            )
    if order_id:
        f = f.filter(order_id=order_id)
    if phone:
        f = f.filter(user_phone=phone)
    result['total'] = f.count()
    result['budans'] = []
    budans = f.order_by('-create_time')[start_num:start_num + count]
    budan_id_to_rel_order_ids = BuDanRealtedOrder.batch_get_order_ids_by_budan_ids([_.id for _ in budans])
    for item in budans:
        data = item.data()
        data['bdtransfer_ref_order_ids'] = budan_id_to_rel_order_ids.get(data['id'], [])
        result['budans'].append(data)
    return result


@bind_context('doctor/budan/detail')
def budan_detail(ctx, budan_id=None, order_id=None):
    doctor = get_doctor_from_context(ctx)
    if not doctor:
        return gen(CODES.DOCTOR_NOT_FOUND)

    budan = None
    if budan_id:
        budan = BuDan.objects.get(id=budan_id)
    if order_id:
        budan = BuDan.objects.filter(order_id=order_id).order_by('-create_time').first()

    if not budan:  # 补单不存在返回None
        return None

    # if budan and budan.doctor_id != doctor.id:
    #     return gen(CODES.BUDAN_NO_PERMISSION)

    data = budan.data()
    data['records'] = []
    for record in budan.records.order_by('-id'):
        item = {
            'id': record.id,
            'username': filter_user_nick_name(record.user),
            'operate_desc': BUDAN_OPERATE.getDesc(record.operate),
            'role_desc': BUDAN_OPERATOR.getDesc(record.role),
            'message': record.message,
            'create_time': record.create_time.strftime('%Y-%m-%d %H:%M'),
        }
        data['records'].append(item)

    # 获取补单相关联的订单信息
    related_order_ids = BuDanRealtedOrder.get_order_ids_by_budan_id(budan_id)
    if related_order_ids:
        data['related_order'] = BuDanRealtedOrder.batch_get_to_dict_data_by_order_ids(related_order_ids, budan_id)
    else:
        data['related_order'] = []
    return data


@bind_context('doctor/budan/appeal')
def appeal_budan(ctx, budan_id, reason):
    doctor = get_doctor_from_context(ctx)
    if not doctor:
        return gen(CODES.DOCTOR_NOT_FOUND)

    with transaction.atomic():
        budan = BuDan.objects.select_for_update().get(id=budan_id)
        if not budan or budan.doctor_id != doctor.id:
            return gen(CODES.BUDAN_NO_PERMISSION)
        if budan.status != BUDAN_STATUS.CREATED:  # 只有 录入状态才允许申诉
            return gen(CODES.BUDAN_STATUS_ERROR)

        budan.status = BUDAN_STATUS.APPEAL
        budan.save()
        BuDanRecord.objects.create(
            budan=budan, user_id=doctor.user_id, message=reason,
            role=BUDAN_OPERATOR.DOCTOR, operate=BUDAN_OPERATE.APPEAL,
        )


@bind_context('doctor/budan/cancel')
def cancel_budan(ctx, budan_id):
    doctor = get_doctor_from_context(ctx)
    if not doctor:
        return gen(CODES.DOCTOR_NOT_FOUND)

    with transaction.atomic():
        budan = BuDan.objects.select_for_update().get(id=budan_id)
        if not budan or budan.doctor_id != doctor.id or budan.create_user_id != doctor.user_id:
            #  只能撤销自己创建的补单
            return gen(CODES.BUDAN_NO_PERMISSION)
        if budan.status not in [BUDAN_STATUS.CREATED, BUDAN_STATUS.APPEAL]:  # 只有 录入/申诉 状态才允许撤销
            return gen(CODES.BUDAN_STATUS_ERROR)
        # 只能撤销本月内创建的补单，历史补单只读
        if str(budan.create_time)[:7] != str(datetime.date.today())[:7]:
            return gen(CODES.BUDAN_HISTORY_READNOLY)
        budan.status = BUDAN_STATUS.CANCEL
        budan.save()
        if budan.bdtransfer_singlerecord_id:
            bdtransfer_singlerecord_obj = BDTransferSingleRecord.objects.get(id=budan.bdtransfer_singlerecord_id)
            bdtransfer_singlerecord_obj.status = SINGLE_TYPE.HAS_ACCEPT
            bdtransfer_singlerecord_obj.budan_id = None
            bdtransfer_singlerecord_obj.save()
        BuDanRecord.objects.create(
            budan=budan, user_id=doctor.user_id,
            role=BUDAN_OPERATOR.DOCTOR, operate=BUDAN_OPERATE.CANCEL,
        )


def statistic_current_month_info(doctor, doctor_ids, user_ids):
    """
        doctor是商户, doctor_ids是商户及旗下所有的医生
        此方法返回数据只用于一般补单，剔除转诊补单
    """
    now = datetime.datetime.now()
    current_month_first_day = datetime.date.today().replace(day=1)
    before_month_first_day = current_month_first_day + relativedelta(months=-1)

    should_pay = BuDan.objects.filter(
        doctor_id__in=doctor_ids,
        create_time__gte=current_month_first_day,
        create_time__lte=now,
        status__in=(BUDAN_STATUS.CREATED, BUDAN_STATUS.APPEAL),
    ).exclude(
        budan_type=BUDAN_TYPE.TRANSFER_BUDAN
    ).aggregate(total=Sum('payment'))['total'] or 0
    should_pay *= 100

    alread_pay = Order.objects.filter(status__in=(ORDER_STATUS.USED, ORDER_STATUS.SETTLED),
                                      user_id__in=user_ids,
                                      validate_time__gte=current_month_first_day,
                                      validate_time__lte=now, ).aggregate(total=Sum('discount'))['total'] or 0
    # 目前直接录入的补单
    luru_amount = BuDanLuRu.objects.filter(
        doctor_id__in=doctor_ids,
        created_time__gte=current_month_first_day,
        created_time__lte=now,
        status=BUDAN_LURU_STATUS.ENTERED
    ).aggregate(total=Sum('amount'))['total'] or 0
    alread_pay = (alread_pay + luru_amount) * 100

    # 上个月的结余
    try:
        balance = BuDanSettlement.objects.get(doctor=doctor, month_at=before_month_first_day).balance
        balance *= 100
    except BuDanSettlement.DoesNotExist:
        balance = 0

    if balance > 0:
        alread_pay += balance
    elif balance < 0:
        should_pay += -balance

    need_pay = should_pay - alread_pay

    return {
        'should_pay': round(should_pay / 100.0, 1),
        'alread_pay': round(alread_pay / 100.0, 1),
        'need_pay': round(need_pay / 100.0, 1) if need_pay > 0 else 0,
    }


@bind_context('doctor/budan/paidan_detail')
def paidan_detail(ctx, settlement_id, doctor_id=None, from_select=False, begin=0, offset=10,
                  budan_type=0):
    if doctor_id is None:
        doctor = get_doctor_from_context(ctx)
    else:
        doctor = Doctor.objects.get(id=doctor_id)
    if not doctor:
        return gen(CODES.DOCTOR_NOT_FOUND)

    budan = BuDanSettlement.objects.get(id=settlement_id)
    # assert budan.doctor_id == doctor.id, "补单结算单对应医生不是请求用户"

    doctor_ids, user_ids = get_all_doctors(doctor, from_select)

    orders_data = budan.get_orders(user_ids)

    luru_data = budan.get_luru(doctor_ids)
    data = dict()
    data['total_discount'] = orders_data['total_discount'] + luru_data['luru_amounts']
    data['month'] = str(budan.month_at)[:7]
    if not from_select:
        data['balance'] = budan.balance
    if budan_type == 0:
        orders = orders_data['orders'][begin:begin + offset]
        for order in orders:
            order['doctor'] = order.pop('user__doctor__name')
            order['validate_time'] = order['validate_time'].strftime('%Y-%m-%d %H:%M:%S')
        data['orders'] = list(orders)
        data['total'] = orders_data['orders'].count()
    if budan_type == 1:
        lurus = [
            {
                'amount': item.amount,
                'desc': item.desc,
                'created_time': item.created_time.strftime('%Y-%m-%d  %H:%M:%S')
            }
            for item in luru_data['luru'][begin:begin + offset]]
        data['lurus'] = lurus
        data['total'] = luru_data['luru'].count()

    return data


@bind_context('doctor/budan/history')
def history_budan(ctx, year, begin=0, offset=10):
    """

    :param ctx:
    :param year: int
    :param begin:
    :param offset:
    :return:
    """
    doctor = get_doctor_from_context(ctx)
    if not doctor:
        return gen(CODES.DOCTOR_NOT_FOUND)

    budan_settlments = BuDanSettlement.objects.filter(doctor_id=doctor.id, month_at__year=year)

    data = dict()
    data['total'] = budan_settlments.count()
    budan_settlments = budan_settlments.order_by('-month_at')[begin:begin + offset]
    settlements = []
    for item in budan_settlments:
        row = {
            'should_pay': item.payment,
            'already_pay': item.alread_payment,
            'balance': item.balance,
            'month': str(item.month_at)[:7],
            'id': item.id
        }
        settlements.append(row)
    data['settlements'] = settlements
    return data
