# -*- coding:utf-8 -*-
#   Author  :   zhangxiaolin
#   E-mail  :   petelin1120@gmail.com
#   Date    :   17/6/27 18:26
#   Desc    :   补单相关
import operator

import itertools

import math
import json
import datetime
from dateutil.relativedelta import relativedelta
from django.db import models, transaction
from django.db.models import Sum
from django.db.utils import IntegrityError
from gm_types.doctor import BUDAN_LURU_OPERATE
from gm_types.doctor import BUDAN_LURU_STATUS, BUDAN_LURU_TYPE
from collections import defaultdict
from api.tool.user_tool import filter_user_nick_name
from api.models import Doctor, User, Order, Service, logging_exception, Person, Merchant, Tag, ServiceItem
from rpc.tool.dict_mixin import DictMixin
from rpc.tool.error_code import CODES, gen

from gm_types.gaia import BUDAN_STATUS, BUDAN_OPERATOR, BUDAN_SETTLEMENT_STATUS, BUDAN_OPERATE, ORDER_STATUS, BUDAN_TYPE

from pay.tool import random_tool


class BuDanSettlement(models.Model):
    class Meta:
        verbose_name = u'补单结算单信息'
        db_table = 'api_budansettlement'
        app_label = 'api'

    id = models.CharField(max_length=12, primary_key=True, verbose_name=u'结算单号')
    doctor = models.ForeignKey(Doctor, verbose_name=u'需要补单的医生')
    extra_consume = models.FloatField(default=0, verbose_name=u'额外消费金额_主动补单', help_text=u'用于记录用户额外消费了多少(元)')
    payment = models.FloatField(default=0, verbose_name=u'应补佣金')
    alread_payment = models.FloatField(default=0, verbose_name=u'已补佣金')
    fine = models.FloatField(default=0, verbose_name=u'应补罚款')
    balance = models.FloatField(default=0, verbose_name=u'结余(可正可负)')
    status = models.SmallIntegerField(
        verbose_name=u'补单状态',
        choices=BUDAN_SETTLEMENT_STATUS,
        default=BUDAN_SETTLEMENT_STATUS.PAY_SUCCESS,
    )
    month_at = models.DateField(null=False, verbose_name=u'那个月的结算单')
    created_at = models.DateTimeField(auto_now_add=True, verbose_name=u'创建时间')

    def save(self, *args, **kwargs):
        if not self.pk:
            self.id = random_tool.generate_id(id_length=12)
        while True:
            try:
                super(BuDanSettlement, self).save(*args, **kwargs)
                break
            except IntegrityError as e:
                if "PRIMARY" in e[1]:  # 主键重复
                    self.id = random_tool.generate_id(id_length=12)
                else:
                    raise

    def get_orders(self, user_ids=[]):
        """
        获取补单结算单周期内的订单列表
        :type user_ids: 订单作为补单的用户ID
        :return:
        """
        if not user_ids:
            user_ids = [self.doctor.user.id, ]
        base_query = Order.objects.filter(
            user_id__in=user_ids,
            status__in=(ORDER_STATUS.USED, ORDER_STATUS.SETTLED),
            validate_time__gte=self.month_at,
            validate_time__lt=self.month_at + relativedelta(months=+1),
        )
        orders = base_query.select_related('service').values(
            'id', 'user__doctor__name', 'service__name', 'service_price', 'phone', 'discount', 'validate_time'
        )

        total_discount = base_query.aggregate(Sum('discount'))['discount__sum'] or 0
        return {
            'orders': orders,
            'total_discount': total_discount,
        }

    def get_luru(self, doctor_ids):
        """

        :param doctor_ids: 订单作为补单的医生ID
        :return:
        """
        if not doctor_ids:
            doctor_ids = [self.doctor.id, ]

        lurus_base_query = BuDanLuRu.objects.filter(
            doctor_id__in=doctor_ids,
            status=BUDAN_LURU_STATUS.ENTERED,
            created_time__gte=self.month_at,
            created_time__lt=self.month_at + relativedelta(months=+1)
        )
        luru_amounts = lurus_base_query.aggregate(Sum('amount'))['amount__sum'] or 0

        return {
            'luru': lurus_base_query,
            'luru_amounts': luru_amounts
        }


class BuDan(models.Model):
    class Meta:
        db_table = 'api_budan'
        app_label = 'api'

    id = models.CharField(max_length=12, primary_key=True, verbose_name=u'补单号')
    doctor = models.ForeignKey(Doctor, verbose_name=u'需要补单的医生')
    user = models.ForeignKey(User, null=True, verbose_name=u'关联用户', related_name=u'budans')
    service = models.ForeignKey(Service, null=True, verbose_name='关联美购')
    order = models.OneToOneField(Order, null=True, verbose_name=u'关联的订单')

    settlement = models.ForeignKey(BuDanSettlement, null=True, verbose_name='关联的结算单', related_name='items')

    user_phone = models.CharField(default='', max_length=20, verbose_name=u'用户手机号')
    # 不要直接赋值该属性，调用方法 set_dev_projects
    dev_projects = models.CharField(max_length=5000,
                                    verbose_name=u'医生开发项目(json) [{"name": x, "money": 1, "commission_rate": 80}]')
    extra_consume = models.FloatField(verbose_name=u'额外消费金额', default=0, help_text=u'用于记录用户额外消费了多少(元)')
    payment = models.FloatField(verbose_name=u'抽成', default=0, help_text=u'医生需要支付的补单额(元)')
    status = models.SmallIntegerField(choices=BUDAN_STATUS, default=BUDAN_STATUS.CREATED,
                                      verbose_name=u'补单状态')
    platform = models.SmallIntegerField(choices=BUDAN_OPERATOR, default=BUDAN_OPERATOR.HERA,
                                        verbose_name=u'来源于那个平台')
    create_user = models.ForeignKey(User, verbose_name=u'创建者', related_name='created_budans')
    comment = models.CharField(default='', max_length=500, verbose_name='备注')
    happen_time = models.DateTimeField(null=True, verbose_name=u'飞单发生时间')
    create_time = models.DateTimeField(auto_now_add=True, verbose_name=u'补单创建时间')

    budan_type = models.SmallIntegerField(verbose_name='补单类型', choices=BUDAN_TYPE, default=BUDAN_TYPE.GENERAL_BUDAN)
    is_monthly_revocation = models.BooleanField(verbose_name=u'是否跨月撤销', default=False)
    revocation_time = models.DateTimeField(verbose_name=u'撤销时间', null=True)

    def save(self, *args, **kwargs):
        if str(self.budan_type) == str(BUDAN_TYPE.GENERAL_BUDAN):
            self.payment = self.extra_consume * 0.1
        if not self.pk:
            # 抽成 10%
            self.id = random_tool.generate_id(id_length=12)
        while True:
            try:
                super(BuDan, self).save(*args, **kwargs)
                break
            except IntegrityError as e:
                if "PRIMARY" in e[1]:  # 主键重复
                    self.id = random_tool.generate_id(id_length=12)
                else:
                    raise

    @staticmethod
    def create_budan(doctor_id, dev_projects, create_user, happen_time, budan_type, platform=BUDAN_OPERATOR.HERA,
                     user_id=None, user_phone=None, service_id=None, order_id=None, comment="",
                     total_gengmei_price=0, additional_amount=0):
        from api.models import BDTransferMonth, BDTransferMonthToBudan
        if user_phone is None:
            if user_id is not None:
                user_phone = User.objects.get(id=user_id).person.phone or ''
            elif order_id is not None:
                user_phone = Order.objects.get(id=order_id).phone or ''
            else:
                return gen(CODES.PARAMS_INVALID)

        with transaction.atomic():
            if order_id:
                exist = BuDan.objects.filter(
                    order__id=order_id
                ).exclude(
                    status=BUDAN_STATUS.CANCEL
                ).select_for_update().count()

                if exist > 0:
                    return gen(CODES.BUDAN_STATUS_ERROR)
            try:
                budan = BuDan.objects.create(user_phone=user_phone,
                                             doctor_id=doctor_id,
                                             service_id=service_id,
                                             order_id=order_id,
                                             user_id=user_id,
                                             comment=comment,
                                             happen_time=happen_time,
                                             platform=platform,
                                             create_user=create_user,
                                             budan_type=budan_type,)
                budan.set_dev_projects(dev_projects, tpy=budan_type)
                budan.extra_consume = budan.extra_consume+total_gengmei_price
                budan.payment = budan.payment+additional_amount
                budan.save()
                if str(budan.budan_type) == str(BUDAN_TYPE.TRANSFER_BUDAN):
                    month = "{year}-{month}-01".format(year=datetime.datetime.now().year, month=datetime.datetime.now().month)
                    bd_month, created = BDTransferMonth.objects.get_or_create(
                        month_at=month,
                        doctor_id=budan.doctor_id,
                        defaults={'is_finished': False}
                    )
                    bd_month.total_amount += budan.extra_consume
                    bd_month.should_pay += budan.payment
                    bd_month.save()

                    if bd_month.should_pay != bd_month.already_pay:
                        bd_month.is_finished = False
                        bd_month.save()

                    BDTransferMonthToBudan.objects.get_or_create(
                        budan_id=budan.id,
                        bdtransfermonth=bd_month
                    )

            except IntegrityError as e:
                logging_exception()
                return gen(CODES.BUDAN_STATUS_ERROR)  # 重复
            BuDanRecord.record(create_user, budan, role=platform, operate=BUDAN_OPERATE.CREATE, message="")
            return budan

    @transaction.atomic
    def cancel(self, user, role, message):
        """撤销补单"""
        f = BuDan.objects.filter(id=self.id, status__in=[BUDAN_STATUS.CREATED, BUDAN_STATUS.APPEAL, BUDAN_STATUS.CREATE_SETTLEMENT])
        update = f.update(status=BUDAN_STATUS.CANCEL)
        if update != 1:
            return gen(CODES.BUDAN_STATUS_ERROR)
        BuDanRecord.record(user, self, role=role, operate=BUDAN_OPERATE.CANCEL, message=message)

    @transaction.atomic
    def reject(self, user, message):
        """运营驳回"""
        f = BuDan.objects.filter(id=self.id, status=BUDAN_STATUS.APPEAL)
        update = f.update(status=BUDAN_STATUS.CREATED)
        if update != 1:
            return gen(CODES.BUDAN_STATUS_ERROR)
        BuDanRecord.record(user, self, role=BUDAN_OPERATOR.HERA, operate=BUDAN_OPERATE.REJECT,
                           message=message)

    def set_dev_projects(self, projects, tpy):
        try:
            project_list = json.loads(projects)
        except:
            project_list = None
        # 关联订单 和项目二选一
        # if not project_list:
        #     gen(CODES.BUDAN_NOT_PROJECTS)
        data = []
        consume = 0
        payments = 0
        if str(tpy) == str(BUDAN_TYPE.GENERAL_BUDAN):
            for item in project_list:
                pro = {
                    'name': item.get('name'),
                    'money': int(item.get('money', 0)),
                    'time': item.get('time', ''),
                    'second_tag_id': item.get('second_tag_id', ''),
                    'third_tag_id': item.get('third_tag_id', ''),
                    'service_id': item.get('service_id', ''),
                    'sku_id': item.get('sku_id', ''),
                }
                if pro['name']:
                    data.append(pro)
                    consume += pro['money']
        else:
            for item in project_list:
                pro = {
                    'name': item.get('name'),
                    'money': int(item.get('money', 0)),
                    'rate': int(item.get('rate', 0)),
                }
                if pro['name']:
                    data.append(pro)
                    consume += pro['money']
                    payments += float(pro['money'] * pro['rate']) / 100
            self.payment = payments
        # 中文方式存 and 减少空格
        self.dev_projects = json.dumps(data, ensure_ascii=False, separators=(',', ':'))
        self.extra_consume = consume


    @property
    def dev_projects_json(self):
        return json.loads(self.dev_projects)

    @property
    def dev_projects_format(self):
        return "\n".join([u"项目%d: %s %s" % (i + 1, item['name'], item['money'])
                          for i, item in enumerate(self.dev_projects_json)])

    def data(self):
        def format_data(sub):
            try:
                second_tag = Tag.objects.get(id=sub['second_tag_id'])
                third_tag = Tag.objects.get(id=sub['third_tag_id'])
                sub['project_type'] = u'{}/{}'.format(second_tag.name, third_tag.name)
            except:
                sub['project_type'] = u''
            try:
                service = Service.objects.get(id=sub['service_id'])
                service_item = ServiceItem.objects.get(id= sub['sku_id'])
                sub['related_project'] = u'{}/{}'.format(service.name, ''.join(service_item.items_name))
            except:
                sub['related_project'] = u''
            if not sub.get('time', None):
                sub['time'] = u''
            return sub
        dev_projects = map(format_data, json.loads(self.dev_projects))

        info = {
            'id': self.id,
            'doctor': self.doctor.name,
            'service_id': self.service_id,
            'service_name': self.service.name if self.service else None,
            'order_id': self.order_id,
            'user_id': self.user_id if self.user else None,
            'username': filter_user_nick_name(self.user) if self.user else None,
            'create_time': self.create_time.strftime('%Y-%m-%d %H:%M'),
            'user_phone': self.user_phone,
            'dev_projects': dev_projects,
            'status': self.status,
            'status_desc': BUDAN_STATUS.getDesc(self.status),
            'extra_consume': self.extra_consume,
            'payment': self.payment,
            'platform': self.platform,
            'comment': self.comment,
            'bd_type_desc': BUDAN_TYPE.getDesc(self.budan_type),
            'bd_type': self.budan_type,
        }
        return info

    def total_project_amount(self):
        consume = 0
        for item in self.dev_projects_json:
            pro = {
                'name': item.get('name'),
                'money': int(item.get('money', 0)),
                'rate': int(item.get('rate', 0)),
            }
            if pro['name']:
                consume += float(pro['money'])
        return consume


class BuDanRecord(models.Model):
    class Meta:
        db_table = 'api_budanrecord'
        app_label = 'api'

    budan = models.ForeignKey(BuDan, null=False, verbose_name=u'关联的补单', related_name='records')
    user = models.ForeignKey(User, null=False)
    message = models.TextField(max_length=500, default='', verbose_name=u'信息')
    role = models.SmallIntegerField(choices=BUDAN_OPERATOR, default=BUDAN_OPERATOR.DOCTOR, verbose_name=u'医生申诉,运营驳回')
    create_time = models.DateTimeField(auto_now_add=True, verbose_name='记录时间')
    operate = models.SmallIntegerField(choices=BUDAN_OPERATE,
                                       default=BUDAN_OPERATE.CREATE)

    @staticmethod
    def record(user, budan, role, operate, message):
        assert user is not None and role in BUDAN_OPERATOR and operate in BUDAN_OPERATE

        return BuDanRecord.objects.create(budan=budan, user=user, role=role,
                                          operate=operate, message=message)


class BuDanLuRu(models.Model):
    class Meta:
        verbose_name = u'补单录入表'
        db_table = 'api_budanluru'
        app_label = 'api'

    doctor = models.ForeignKey(Doctor, verbose_name=u'医生ID')
    month = models.IntegerField(verbose_name=u'补单月份')  # 201702
    amount = models.FloatField(verbose_name=u'补单金额')  # 补单金额
    desc = models.CharField(verbose_name=u'描述', max_length=1000)
    created_time = models.DateTimeField(verbose_name=u'创建时间', auto_now_add=True)
    status = models.IntegerField(verbose_name=u'补单状态', choices=BUDAN_LURU_STATUS,
                                 default=BUDAN_LURU_STATUS.ENTERED)
    is_settle = models.BooleanField(verbose_name=u'是否已结算', default=False)  # 已结算的不能再被撤销
    type = models.IntegerField(verbose_name='补单类型',choices=BUDAN_LURU_TYPE,null=True,blank=True,default=0)

    @property
    def can_undo(self):
        """
        补单录入是否可以撤销
        :return:
        """
        data_now = datetime.datetime.now()
        month_now = data_now.strftime("%Y%m")
        if month_now == str(self.month) and self.status == BUDAN_LURU_STATUS.ENTERED and not self.is_settle:
            return True
        else:
            return False

    @property
    def operatesrecord(self):
        """
        操作记录数据
        :return:
        """
        data = [{
                    'user_name': item.user.user.username,
                    'operate': BUDAN_LURU_OPERATE.getDesc(item.operate),
                    'created_time': item.created_time.strftime('%Y-%m-%d  %H:%M:%S'),
                } for item in self.operates.all()]
        return data

    def get_data(self):
        data = {'doctor_id': self.doctor.id, 'month': self.month,
                'month_str': '{}-{}'.format(str(self.month)[0:4], str(self.month)[-2:]), 'amount': self.amount,
                'desc': self.desc,
                'doctor_name': self.doctor.name,
                'operate': self.operatesrecord, 'can_undo': self.can_undo,
                'id': self.id
                }

        return data


class BuDanLuRuOperate(models.Model):
    class Meta:
        verbose_name = u'补单录入操作表'
        db_table = 'api_budanluruoperate'
        app_label = 'api'

    budanluru = models.ForeignKey(BuDanLuRu, verbose_name=u'补单录入', related_name='operates')
    user = models.ForeignKey(Person, verbose_name=u'操作人')
    operate = models.IntegerField(u'操作类型', choices=BUDAN_LURU_OPERATE)
    created_time = models.DateTimeField(u'操作时间', auto_now_add=True)


class BuDanRealtedOrder(models.Model, DictMixin):
    class Meta:
        verbose_name = u'补单相关订单'
        db_table = 'api_budan_ref_order'
        app_label = 'api'

    order_id = models.CharField(u'订单ID', max_length=12)
    budan_id = models.CharField(u'补单ID', max_length=12)
    service_id = models.IntegerField(u'美购ID')
    service_name = models.CharField(u'美购名称', max_length=100)
    created_time = models.DateTimeField(u'操作时间', default=datetime.datetime.now(), blank=True)
    gengmei_price = models.FloatField(u'更美价格')
    commission_rate = models.FloatField(u'佣金比例')
    commission_amount = models.FloatField(u'更美抽成')
    additional_amount = models.FloatField(u'订单应补款')
    is_delete = models.BooleanField(u'是否删除')

    def to_dict(self, fields=None, excludes=None, expands=None):
        if excludes is None:
            excludes = ['id']
        return super(BuDanRealtedOrder, self).to_dict(fields, excludes, expands)

    @classmethod
    def batch_get_to_dict_data_by_order_ids(cls, order_ids, budan_id=None):
        if budan_id:
            return [item.to_dict() for item in cls.objects.filter(order_id__in=order_ids, budan_id=budan_id)]
        return [item.to_dict() for item in cls.objects.filter(order_id__in=order_ids)]

    @classmethod
    def batch_get_order_ids_by_budan_ids(cls, budan_ids):
        qs = cls.objects.filter(budan_id__in=budan_ids)
        grouped = itertools.groupby(
            qs.values_list('budan_id', 'order_id'),
            operator.itemgetter(0),
        )

        result = {
            k: [e[1] for e in v]
            for k, v in grouped
        }
        return result

    @classmethod
    def get_order_ids_by_budan_id(cls, budan_id):
        return cls.batch_get_order_ids_by_budan_ids([budan_id]).get(budan_id)
