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

import json
import random
import datetime
from datetime import timedelta

from django.conf import settings
from django.db import models
from django.db.models import Q, F
from django.db import transaction
from django.utils import timezone

from gm_types.trade import COUPON_TIME_TYPE
from gm_types.gaia import COUPON_STATUS, COUPON_DISTRIBUTION_STATUS, COUPON_TYPES, COUPON_GIFT_TYPES
from gm_types.gaia import BENEFIT_TYPE, DOCTOR_USE_COUPON_TYPE
from gm_types.doctor import COUPON_FILTER_STATUS

from rpc.tool.generate_random_code import code_generator
from rpc.cache import coupon_cache
from api.tool.log_tool import logging_exception, cache_logger
from api.util.cache_util import cache_wrap

from gm_tracer import func_span, traced_function


class CouponManager(models.Manager):
    def create_platform_coupon(self, coupon, user, relation):
        pass

    def update_platform_coupon(self, coupon, user, relation):
        pass

    def create_doctor_coupon(self, doctor, coupon_info):
        """
            医生创建的美券
            coupon_info 字段说明 详见 http://wiki.gengmei.cc/pages/viewpage.action?pageId=4134320
        """
        from api.tool.coupon_tool import gen_doctor_coupon_info, gen_doctor_gift_info, gen_doctor_channelgift
        from api.models.coupon import CouponSKURestrict, CouponGift, GiftToCoupon, ChannelGift
        from api.models.doctor import MerchantRelevance, Doctor
        with transaction.atomic():
            # 创建 美券
            coupon_dict = gen_doctor_coupon_info(coupon_info)
            doctor_coupon_use_type = coupon_info['doctor_coupon_type']
            coupon_dict.update({
                'doctor_id': doctor.id,
                'coupon_type': COUPON_TYPES.DOCTOR,
                'benefit_type': BENEFIT_TYPE.MANJIAN if coupon_dict['has_threshold'] else BENEFIT_TYPE.ZHIJIAN,
                'doctor_coupon_use_type': doctor_coupon_use_type,
            })
            coupon = self.create(**coupon_dict)

            skus = []
            # 医生通用
            if doctor_coupon_use_type == DOCTOR_USE_COUPON_TYPE.DOCTOR_GENERAL:
                skus = doctor.get_can_sell_sku_ids(doctor_id=doctor.id)
            elif doctor_coupon_use_type == DOCTOR_USE_COUPON_TYPE.PART_GENERAL:
                # 部分美购使用
                skus = [item.get("id") for item in coupon_info.get('skus', [])]
            elif doctor_coupon_use_type == DOCTOR_USE_COUPON_TYPE.FULL_PLATFORM_GENERAL:
                # 全机构通用 机构下所有医生所有美购都能使用
                skus = []
                doctors = list(MerchantRelevance.objects.filter(
                    merchant_id=doctor.d_merchant.id).values_list("doctor_id", flat=True))
                for doctor_id in doctors:
                    skus.extend(Doctor.objects.get(pk=doctor_id).get_can_sell_sku_ids())
            # 美券关联sku
            cskr_obj_list = [CouponSKURestrict(coupon_id=coupon.id, sku_id=sku) for sku in skus]
            CouponSKURestrict.objects.bulk_create(cskr_obj_list)

            # 创建大礼包, 礼包name/value=美券的
            gift_info = gen_doctor_gift_info(coupon_info)
            gift_info.update({
                'gift_type': COUPON_GIFT_TYPES.DOCTOR,
            })
            coupon_gift = CouponGift.objects.create(**gift_info)
            # 礼包关联美券，医生礼包中的医生美券数固定为1
            GiftToCoupon.objects.create(coupon=coupon, coupon_gift=coupon_gift, number=1)
            # 礼包关联渠道
            channelgift_info = gen_doctor_channelgift(coupon_info)
            channelgift_info.update({
                'gift': coupon_gift,
                'business_channel_id': settings.DOCTOR_BUSINESS_CHANNAL_ID,
            })
            channel_gift = ChannelGift.objects.create(**channelgift_info)

            from api.models.coupon import ChannelGiftStatistics
            ChannelGiftStatistics.objects.get_or_create(
                channel_gift_id=channel_gift.id
            )

            if coupon_info.get('distribution_status') == COUPON_DISTRIBUTION_STATUS.OPEN:
                # 生效美券
                self.active_doctor_coupon(coupon)
            return coupon

    def update_doctor_coupon(self, coupon, doctor, coupon_info):
        from api.tool.coupon_tool import gen_doctor_coupon_info, gen_doctor_gift_info, gen_doctor_channelgift
        from api.models.coupon import CouponSKURestrict, CouponGift, ChannelGift
        from api.models.doctor import MerchantRelevance, Doctor
        with transaction.atomic():
            # 更新美券
            coupon_dict = gen_doctor_coupon_info(coupon_info)
            doctor_coupon_use_type = coupon_info['doctor_coupon_type']
            coupon_dict.update({
                'doctor_coupon_use_type': doctor_coupon_use_type,
            })

            self.filter(pk=coupon.id).update(**coupon_dict)
            # 更新关联的skus
            skus = [], []
            old_res_skus = coupon.restric_skus.values_list("sku_id", flat=True)
            # 医生通用
            if doctor_coupon_use_type == DOCTOR_USE_COUPON_TYPE.DOCTOR_GENERAL:
                skus = doctor.get_can_sell_sku_ids(doctor_id=doctor.id)
            elif doctor_coupon_use_type == DOCTOR_USE_COUPON_TYPE.PART_GENERAL:
                # 部分美购使用
                skus = [item.get("id") for item in coupon_info.get('skus', [])]
            elif doctor_coupon_use_type == DOCTOR_USE_COUPON_TYPE.FULL_PLATFORM_GENERAL:
                # 机构下所有医生都能使用
                skus = []
                doctors = list(MerchantRelevance.objects.filter(
                    merchant_id=doctor.d_merchant.id).values_list("doctor_id", flat=True))
                for doctor_id in doctors:
                    skus.extend(Doctor.objects.get(pk=doctor_id).get_can_sell_sku_ids())

            wait_delete_sku_ids = set(old_res_skus) - set(skus)
            wait_create_sku_ids = set(skus) - set(old_res_skus)

            # 添加新创建
            cskr_obj_list = [CouponSKURestrict(coupon_id=coupon.id, sku_id=sku_id) for sku_id in wait_create_sku_ids]
            CouponSKURestrict.objects.bulk_create(cskr_obj_list)

            # 删除历史
            CouponSKURestrict.objects.filter(sku_id__in=wait_delete_sku_ids).delete()
            # 更新大礼包
            coupon_gift = coupon.doctor_coupon_gift()
            gift_info = gen_doctor_gift_info(coupon_info)
            CouponGift.objects.filter(pk=coupon_gift.id).update(**gift_info)
            # 更新关联渠道的信息
            channelgift = coupon_gift.doctor_channelgift()
            channelgift_info = gen_doctor_channelgift(coupon_info)
            ChannelGift.objects.filter(pk=channelgift.id).update(**channelgift_info)

            if coupon_info.get('distribution_status') == COUPON_DISTRIBUTION_STATUS.OPEN:
                # 生效美券
                self.active_doctor_coupon(coupon)



    def active_platform_coupon(self):
        pass

    def active_doctor_coupon(self, coupon):
        """
            生效美券
        """
        from api.models.coupon import GiftToCoupon
        activated_time = datetime.datetime.now()
        # 券生效
        with transaction.atomic():
            coupon.activated_time = activated_time
            coupon.distribution_status = COUPON_DISTRIBUTION_STATUS.OPEN
            coupon.save()
            # 处理礼包中美券生效
            GiftToCoupon.objects.filter(coupon_id=coupon.id).update(coupon_enable=True)
            # 处理 大礼包在渠道中生效
            coupon_gift = coupon.doctor_coupon_gift()
            # 若是 领取时间小于 生效时间，不合法
            if coupon_gift.end_time < activated_time:
                raise Exception
            channelgift = coupon_gift.doctor_channelgift()
            channelgift.activated_time = activated_time
            channelgift.gift_enable = True
            channelgift.displayable = True
            channelgift.save()

    def close_doctor_coupon(self, coupon):
        """
            下线美券
        """
        from api.models.coupon import GiftToCoupon
        with transaction.atomic():
            coupon.distribution_status = COUPON_DISTRIBUTION_STATUS.CLOSED
            coupon.save()
            # 处理礼包中美券失效
            GiftToCoupon.objects.filter(coupon_id=coupon.id).update(coupon_enable=False)
            # 处理 大礼包在渠道中失效
            coupon_gift = coupon.doctor_coupon_gift()
            channelgift = coupon_gift.doctor_channelgift()
            channelgift.gift_enable = False
            channelgift.displayable = False
            channelgift.save()

    def doctor_coupon_list(self, doctor_ids, filter_status=COUPON_FILTER_STATUS.ALL, doctor_id=None, coupon_name=None):
        from api.models import Coupon
        if doctor_id:
            query = Q(doctor_id=doctor_id, coupon_type=COUPON_TYPES.DOCTOR)
        else:
            query = Q(doctor_id__in=doctor_ids, coupon_type=COUPON_TYPES.DOCTOR)
        if coupon_name:
            query &= Q(name__contains=coupon_name)
        if filter_status == COUPON_FILTER_STATUS.ALL:
            pass
        elif filter_status == COUPON_FILTER_STATUS.DRAFT:
            query &= Q(distribution_status=COUPON_DISTRIBUTION_STATUS.DRAFT)
        elif filter_status == COUPON_FILTER_STATUS.CLOSED:
            query &= Q(distribution_status=COUPON_DISTRIBUTION_STATUS.CLOSED)
        else:
            query &= Q(distribution_status=COUPON_DISTRIBUTION_STATUS.OPEN, coupon_gift__gift_type=COUPON_GIFT_TYPES.DOCTOR)
            if filter_status == COUPON_FILTER_STATUS.ACTIVATED:
                query &= Q(coupon_gift__start_time__gt=timezone.now())
            elif filter_status == COUPON_FILTER_STATUS.OPEN:
                query &= Q(coupon_gift__start_time__lte=timezone.now(), coupon_gift__end_time__gt=timezone.now())
            elif filter_status == COUPON_FILTER_STATUS.EXPIRED:
                query &= Q(coupon_gift__end_time__lte=timezone.now())

        coupons = Coupon.objects.filter(query).order_by('-id')
        return coupons


class CouponInfoManager(models.Manager):
    def create_coupon(self, coupon, user, relation):
        claimed_time = datetime.datetime.now()

        if coupon.time_type == COUPON_TIME_TYPE.START_END:
            start_time = coupon.start_time
            end_time = coupon.end_time
        else:
            start_time = claimed_time
            end_time = claimed_time + timedelta(days=coupon.countdown)

        return self.create(
            coupon=coupon,
            user=user,
            claimed_time=claimed_time,
            status=COUPON_STATUS.CLAIMED,
            start_time=start_time,
            end_time=end_time,
            relation=relation
        )


class ChannelGiftUserRelationManager(models.Manager):
    def create_without_code(self, user, channel_gift):
        now = datetime.datetime.now()
        self.__add_channelgift_claimed_count(channel_gift.id)
        return self.create(
            claimed_time=now,
            created_time=now,
            user=user,
            channel_gift=channel_gift,
        )

    def create_with_code(self, channel_gift, launch=None):
        self.__add_channelgift_claimed_count(channel_gift.id)
        return self.create(
            code=code_generator(size=16),
            channel_gift=channel_gift,
            launch=launch,
        )

    def __add_channelgift_claimed_count(self, channel_gift_id):
        from api.models.coupon import ChannelGiftStatistics
        ChannelGiftStatistics.objects.filter(channel_gift_id=channel_gift_id).update(claimed_count=F('claimed_count') + 1)


class ChannelGiftManager(models.Manager):
    def update_show_gift_on_service_detail_page(self, gitf_id, business_channel_id, is_show):
        pass


def _get_public_can_get_coupon_gift_id_and_type(service_id):
    from api.tool.coupon_tool import get_coupon_gift_info_for_service_detail

    cache_key = "getpublicgift00az_v2:{}".format(service_id)
    raw_data = coupon_cache.get(cache_key)
    if raw_data:
        result = json.loads(raw_data)
        return result
    else:
        cache_time = random.randint(40, 60)

        result, _ = get_coupon_gift_info_for_service_detail(None, service_id, 0, 100)
        coupon_cache.setex(cache_key, cache_time, json.dumps(result))

    return result


def get_show_coupon_dict_for_service_detail(service_id, show_count):
    '''
    美购详情页显示外露的优惠券信息
    :return:
        {   # 优惠券种类：          优惠券详细信息,  总共可领取的券的种类, 总过可领取的券的张数,
            'prepaid_coupon_info': {'detail': [], 'total_coupon_piece': 3, 'total_coupon_count': 30},
            'voucher_coupon_info': {'detail': [], 'total_coupon_piece': 3, 'total_coupon_count': 30},

        }
    '''

    def _build_format_info(gift_infos):
        load_gift_limit = 10

        total_coupon_piece = sum(map(lambda x: x[2], gift_infos))
        total_coupon_count = sum(map(lambda x: x[3], gift_infos))
        gift_ids = map(lambda x: x[0], gift_infos[:load_gift_limit])
        gift_infos = gift_ids_to_gift_dtos(gift_ids, [])

        # 7780 添加
        details = []
        gift_infos.sort(key=lambda x: (-x['benefit_type'], x['discount_rate']))
        for coupon_detail in gift_infos[:show_count]:
            benefit_type = coupon_detail['benefit_type']
            _coupon_detail = {
                'gift_type': coupon_detail['gift_type'],
                'expense': coupon_detail['expense'],
                'id': coupon_detail['gift_id'],
                'doctor_coupon_use_desc': coupon_detail['doctor_coupon_use_desc']
            }
            gift_desc_format = "尾款{}" if coupon_detail['gift_type'] == COUPON_GIFT_TYPES.DOCTOR else "预付{}"
            gift_desc = ""

            if benefit_type == BENEFIT_TYPE.ZHIJIAN:
                _coupon_detail['desc'] = '直减{}'.format(coupon_detail['coupon_value'])
                gift_desc = gift_desc_format.format('减{}'.format(coupon_detail['coupon_value']))

            elif benefit_type == BENEFIT_TYPE.MANJIAN:
                _coupon_detail['desc'] = '{}减{}'.format(coupon_detail['threshold'], coupon_detail['coupon_value'])
                gift_desc = gift_desc_format.format(
                    '{}-{}'.format(coupon_detail['threshold'], coupon_detail['coupon_value']))

            elif benefit_type == BENEFIT_TYPE.DISCOUNT:
                discount_rate = coupon_detail['discount_rate']
                discount_str = '0'

                if discount_rate == 0:
                    desc = '免单券'
                else:
                    discount_str = fmt_discount_rate_show(discount_rate)
                    desc = '{}折券'.format(discount_str)
                _coupon_detail['desc'] = desc

                threshold = coupon_detail.get('threshold', 0)
                if threshold > 0:
                    gift_desc = gift_desc_format.format('{}打{}折'.format(threshold, discount_str))
                else:
                    gift_desc = gift_desc_format.format('打{}折'.format(discount_str))

            _coupon_detail['gift_desc'] = gift_desc
            details.append(_coupon_detail)

        # 7780以下
        # details = [
        #     {
        #         'gift_type': d['gift_type'],
        #         'desc': '{}减{}'.format(d['threshold'], d['coupon_value']) if d['has_threshold'] is True else '直减{}'.format(d['coupon_value'])
        #     } for d in gift_infos
        # ]
        return {
            'details': details,
            'total_coupon_piece': total_coupon_piece,
            'total_coupon_count': total_coupon_count,
        }

    can_get_gift_infos = _get_public_can_get_coupon_gift_id_and_type(service_id)
    prepaid_gift_infos = filter(lambda x: x[1] == COUPON_TYPES.PLATFORM, can_get_gift_infos)
    voucher_gift_infos = filter(lambda x: x[1] == COUPON_TYPES.DOCTOR, can_get_gift_infos)

    prepaid_coupon_info = _build_format_info(prepaid_gift_infos)
    voucher_coupon_info = _build_format_info(voucher_gift_infos)

    # 针对尾款券进行排序: 先优惠金额, 后ID
    if voucher_coupon_info["details"]:
        voucher_coupon_info["details"].sort(key=lambda x: (-x['expense'], -x['id']))

    return {
        'prepaid_info': prepaid_coupon_info,
        'voucher_info': voucher_coupon_info
    }


def get_top_coupon_gift_infos(service_id):
    """美购列表展示礼包信息"""

    if service_id is None:
        return []

    def _get_desc(dto):
        desc = ""
        benefit_type = dto['benefit_type']
        discount_rate = dto['discount_rate']
        if benefit_type == BENEFIT_TYPE.ZHIJIAN:
            desc = '直减{}'.format(dto['coupon_value'])
        elif benefit_type == BENEFIT_TYPE.MANJIAN:
            desc = '{}减{}'.format(dto['threshold'], dto['coupon_value'])
        elif benefit_type == BENEFIT_TYPE.DISCOUNT:
            divisor, remainder = divmod(discount_rate, 10)
            desc = '{}折'.format(dto['discount_rate'] if remainder else divisor)
        return desc

    can_get_gift_infos = _get_public_can_get_coupon_gift_id_and_type(service_id)

    top2_platform_gift_ids = [gi[0] for gi in can_get_gift_infos if gi[1] == COUPON_TYPES.PLATFORM][:2]
    top2_doctor_gift_ids = [gi[0] for gi in can_get_gift_infos if gi[1] == COUPON_TYPES.DOCTOR][:2]

    if len(top2_platform_gift_ids) >= 1 and len(top2_doctor_gift_ids) >= 1:
        result_gift_ids = [top2_platform_gift_ids[0], top2_doctor_gift_ids[0]]
    else:
        result_gift_ids = top2_platform_gift_ids + top2_doctor_gift_ids

    dtos = gift_ids_to_gift_dtos(result_gift_ids, [])

    result = [{
                  "gift_type": d["gift_type"],
                  "desc": _get_desc(d),
              } for d in dtos]

    return result


def _get_best_coupon_gift_info_for_service_card(service_ids):
    '''
    计算美购列表最优券
    :param service_ids:
    :return:
    '''
    if not service_ids:
        return {}

    service_ids_to_base_gift_ids = {}
    all_gift_set = set()

    for sid in service_ids:
        can_get_gift_infos = _get_public_can_get_coupon_gift_id_and_type(sid)

        top_platform_gift_ids = [gi[0] for gi in can_get_gift_infos if gi[1] == COUPON_TYPES.PLATFORM][:1]
        top_doctor_gift_ids = [gi[0] for gi in can_get_gift_infos if gi[1] == COUPON_TYPES.DOCTOR]

        all_gift_ids = top_platform_gift_ids + top_doctor_gift_ids
        service_ids_to_base_gift_ids[sid] = all_gift_ids
        if all_gift_ids:
            for gid in all_gift_ids:
                all_gift_set.add(gid)

    all_gift_id_to_gift_dto = {dto["gift_id"]: dto for dto in gift_ids_to_gift_dtos(list(all_gift_set), [])}

    service_ids_to_best_coupon_gift_info = {}
    for sid in service_ids:
        result = []
        if sid in service_ids_to_base_gift_ids:
            selected_doctor_gift_dto, selected_platform_gift_dto = _get_best_gift_dto(service_ids_to_base_gift_ids, sid, all_gift_id_to_gift_dto)

            result = [{
                "gift_id": selected_doctor_gift_dto["gift_id"],
                "gift_type": selected_doctor_gift_dto["gift_type"],
                "coupon_value": selected_doctor_gift_dto["coupon_value"],
                "coupons_info": selected_doctor_gift_dto.get('coupons_info', []),
                'has_discount_coupon': selected_doctor_gift_dto['has_discount_coupon'],
                'best_discount_rate': selected_doctor_gift_dto['best_discount_rate'],
            }] if selected_doctor_gift_dto else []

            if selected_platform_gift_dto:
                result.append({
                    "gift_id": selected_platform_gift_dto["gift_id"],
                    "gift_type": selected_platform_gift_dto["gift_type"],
                    "coupon_value": selected_platform_gift_dto["coupon_value"],
                    "coupons_info": selected_platform_gift_dto.get('coupons_info', []),
                    'has_discount_coupon': selected_platform_gift_dto['has_discount_coupon'],
                    'best_discount_rate': selected_platform_gift_dto['best_discount_rate'],
                })

        service_ids_to_best_coupon_gift_info[sid] = result

    return service_ids_to_best_coupon_gift_info


def _get_best_gift_dto(service_ids_to_base_gift_ids, service_id, all_gift_id_to_gift_dto):
    '''
    筛选最优礼包信息
    '''
    all_gift_ids = service_ids_to_base_gift_ids[service_id]
    dtos = []
    for gid in all_gift_ids:
        if gid in all_gift_id_to_gift_dto:
            dtos.append(all_gift_id_to_gift_dto[gid])

    all_doctor_gift_dtos = [d for d in dtos if d["gift_type"] == COUPON_GIFT_TYPES.DOCTOR]
    top_platform_gift_ids = [d for d in dtos if d["gift_type"] == COUPON_GIFT_TYPES.PLATFORM][:1]

    selected_doctor_gift_dto = None
    for dg_dto in all_doctor_gift_dtos:
        if dg_dto["has_threshold"] is True and dg_dto["threshold"] > 0:
            if selected_doctor_gift_dto is None:
                selected_doctor_gift_dto = dg_dto
            else:
                #  优惠比例=抵扣金额/门槛金额
                c_rate = float(selected_doctor_gift_dto["coupon_value"]) / selected_doctor_gift_dto["threshold"]
                n_rate = float(dg_dto["coupon_value"]) / dg_dto["threshold"]
                if n_rate > c_rate:
                    selected_doctor_gift_dto = dg_dto
                elif n_rate == c_rate and dg_dto["coupon_value"] > selected_doctor_gift_dto["coupon_value"]:
                    selected_doctor_gift_dto = dg_dto

    if selected_doctor_gift_dto is None:
        for dg_dto in all_doctor_gift_dtos:
            if dg_dto["has_threshold"] is False:
                if selected_doctor_gift_dto is None:
                    selected_doctor_gift_dto = dg_dto
                else:
                    #  如果没有尾款满减券，找尾款立减券，立减券取立减金额最高的露出；
                    if dg_dto["coupon_value"] > selected_doctor_gift_dto["coupon_value"]:
                        selected_doctor_gift_dto = dg_dto

    if selected_doctor_gift_dto is None:
        #  优先露出尾款券；没有尾款露平台券
        if len(top_platform_gift_ids) > 0:
            for dg_dto in dtos:
                if dg_dto["gift_type"] == COUPON_GIFT_TYPES.PLATFORM:
                    selected_doctor_gift_dto = dg_dto

    selected_platform_gift_dto = None
    if len(top_platform_gift_ids) > 0 and selected_doctor_gift_dto and selected_doctor_gift_dto["gift_type"] != COUPON_GIFT_TYPES.PLATFORM:
        for dg_dto in dtos:
            if dg_dto["gift_type"] == COUPON_GIFT_TYPES.PLATFORM:
                selected_platform_gift_dto = dg_dto
    return selected_doctor_gift_dto, selected_platform_gift_dto


coupon_cache_wrap = cache_wrap(coupon_cache, pre_fix='coupon_info_v2:@service_id', expire=60, time_range=60)


@traced_function(name="get_best_coupon_gift_info")
@coupon_cache_wrap.batch
def get_best_coupon_gift_info_for_service_card(service_ids, **kwargs):

    """
    获取美购最优券, 单个缓存
    :param service_ids: [1,2,3]
    :return: { 1:[], 2:[], 3:[] }
    """
    result = {}
    if not service_ids:
        return result
    cache_logger.info(json.dumps({'CACHE_SERVICE_COUPON': '[CACHE_SERVICE_COUPON] service_coupon entry for count: %s, service_ids(%s)' % (len(service_ids), service_ids)}))
    for sid in service_ids:
        result[sid] = []

    data_info = _get_best_coupon_gift_info_for_service_card(service_ids)
    if data_info:
        for sid, sc in data_info.items():
            result[sid] = sc
    return result


def get_coupon_gift_info_for_service_detail_with_type(service_id, user=None, count=1, start_num=0, gift_type=None):
    '''
    从美购详情页进入美券列表, 支持券类型过滤
    :params gift_type: 预付款券-1 | 尾款券-2, 如果为None, 则返回全部
    '''
    from api.tool.coupon_tool import get_coupon_gift_info_for_service_detail
    if service_id is None or start_num < 0 or count <= 0:
        return []

    if not gift_type:
        can_get_gift_infos, already_get_gift_infos = get_coupon_gift_info_for_service_detail(user, service_id, start_num, count)
        can_get_gift_ids = [cg_info[0] for cg_info in can_get_gift_infos]
        already_get_gift_ids = [ag_info[0] for ag_info in already_get_gift_infos]

        total_ids = can_get_gift_ids + already_get_gift_ids
        already_get_gift_id_set = set(already_get_gift_ids)
    else:
        pre_count = start_num + count + 500
        # 这里领取到的为不分类型的券
        can_get_gift_infos, already_get_gift_infos = get_coupon_gift_info_for_service_detail(user, service_id, 0, pre_count)
        # 获取对应类型的券
        pre_can_get_gift_ids = [info[0] for info in can_get_gift_infos if info[1] == int(gift_type)]
        pre_already_get_gift_ids = [info[0] for info in already_get_gift_infos if info[1] == int(gift_type)]

        total_ids = (pre_can_get_gift_ids + pre_already_get_gift_ids)[start_num: start_num + count]
        already_get_gift_id_set = set(pre_already_get_gift_ids)

    can_get_gift_ids = [gid for gid in total_ids if gid not in already_get_gift_id_set]
    already_get_gift_ids = [gid for gid in total_ids if gid in already_get_gift_id_set]

    res = gift_ids_to_gift_dtos(can_get_gift_ids, already_get_gift_ids)
    return res


def get_coupon_gift_info_list_for_service_detail(service_id, user=None, count=1, start_num=0):
    """美购详情页礼包信息"""
    # 注: 该函数日后可使用get_coupon_gift_info_for_service_detail_with_type替代, 建议不要再用, 会逐步清理
    from api.tool.coupon_tool import get_coupon_gift_info_for_service_detail
    if service_id is None or start_num < 0 or count <= 0:
        return []

    can_get_gift_infos, already_get_gift_infos = get_coupon_gift_info_for_service_detail(user, service_id, start_num, count)

    can_get_gift_ids = [info[0] for info in can_get_gift_infos]
    already_get_gift_ids = [info[0] for info in already_get_gift_infos]

    res = gift_ids_to_gift_dtos(can_get_gift_ids, already_get_gift_ids)
    return res


def get_coupongift_by_gift_ids(gift_ids, user=None, filter_displayable=True):
    """ 直播间 美券礼包列表 """
    from api.tool import coupon_tool

    can_get_gift_ids, already_get_gift_ids, gift_id_to_left_count = coupon_tool._get_user_gift_ids(
        gift_ids, user, filter_displayable=filter_displayable)

    res = gift_ids_to_gift_dtos(can_get_gift_ids, already_get_gift_ids)

    return res


def _old_gift_ids_to_gift_dtos(can_get_gift_ids, already_get_gift_ids):
    '''
    根据礼包id, 获取礼包的格式化输出信息
    :param can_get_gift_ids:
    :param already_get_gift_ids:
    :return:
    '''
    from api.models import CouponGift, Doctor, GiftToCoupon
    gift_ids = can_get_gift_ids + already_get_gift_ids

    can_get_gift_id_set = set(can_get_gift_ids)

    if not len(gift_ids) > 0:
        return []

    # cgs_dict = {x.id: x for x in CouponGift.objects.filter(id__in=gift_ids)}
    # cgs = [cgs_dict[gift_id] for gift_id in gift_ids if gift_id in cgs_dict]
    #
    # # http://wiki.gengmei.cc/pages/viewpage.action?pageId=4129058
    # # 因为现阶段的医生礼包只有一张医生美券，所以输出会麻烦很多...
    #
    # cg_info = [g.info_data for g in cgs]

    gtcs = [gt for gt in GiftToCoupon.objects.select_related('coupon').filter(
        coupon_gift_id__in=gift_ids, coupon_enable=True)]

    gid_to_info = {cg.id: (cg.name, cg.gift_type) for cg in CouponGift.objects.filter(id__in=gift_ids)}

    cgid_to_info = {}
    for gtc in gtcs:
        if gtc.coupon_gift_id not in cgid_to_info:
            name = ""
            gift_type = COUPON_GIFT_TYPES.PLATFORM
            if gtc.coupon_gift_id in gid_to_info:
                name, gift_type = gid_to_info[gtc.coupon_gift_id]

            cgid_to_info[gtc.coupon_gift_id] = {
                "id": gtc.coupon_gift_id,
                "name": name,
                "value": 0,
                "coupon_number": 0,
                "coupons": [],
                "gift_type": gift_type,

            }

        info = cgid_to_info[gtc.coupon_gift_id]
        # todo zhekou 折扣券怎么算价值
        if gtc.coupon.benefit_type == BENEFIT_TYPE.DISCOUNT:
            info['has_discount_coupon'] = True
            info['best_discount_rate'] = min(info.get('best_discount_rate', 100), gtc.coupon.discount_rate)
            info['value'] += (gtc.coupon.upper_limit - (gtc.coupon.upper_limit * gtc.coupon.discount_rate // 100)) * gtc.number
        else:
            info["value"] += gtc.coupon.value * gtc.number
            if info.get('has_discount_coupon') is None:
                info['has_discount_coupon'] = False
                info['best_discount_rate'] = 100
        info["coupon_number"] += gtc.number
        info["coupons"].append((gtc.coupon.data(), gtc.number))

    cg_info = [cgid_to_info[gift_id] for gift_id in gift_ids if gift_id in cgid_to_info]

    doctor_gift_ids = [dg['id'] for dg in cg_info if dg['gift_type'] == COUPON_GIFT_TYPES.DOCTOR]

    dgc_infos = {info[0]: info for info in CouponGift.objects.filter(id__in=doctor_gift_ids).values_list(
        'id', 'coupon__has_threshold', 'coupon__prepay_threshold', 'coupon__value', 'coupon__time_type',
        'coupon__start_time', 'coupon__end_time', 'coupon__countdown', 'coupon__doctor_id', 'coupon__benefit_type',
        'coupon__doctor_coupon_use_type')}

    all_doctor_ids = {dgc[8] for _, dgc in dgc_infos.items()}

    doctor_id_to_data = {d[0]: d for d in
                         Doctor.objects.filter(id__in=all_doctor_ids).values_list('id', 'name', 'doctor_type')}

    result = []

    for cgi in cg_info:
        cgi['coupons'].sort(key=lambda x:(-x[0]['benefit_type'], x[0]['discount_rate']))
        gid = cgi['id']
        gtype = cgi['gift_type']

        expense = cgi['value']
        desc = ''
        valid_period = ''

        has_threshold = False
        prepay_threshold = 0
        coupon_value = 0

        benefit_type = 0
        discount_rate = 100
        upper_limit = 0

        doctor_id = None
        doctor_name = None
        doctor_type = None

        doctor_coupon_type = DOCTOR_USE_COUPON_TYPE.NONE_FOR_DOCTOR
        doctor_coupon_use_desc = ''

        if gtype == COUPON_GIFT_TYPES.DOCTOR:
            if gid in dgc_infos:
                _, has_threshold, prepay_threshold, coupon_value, time_type, start_time, end_time, countdown, doctor_id, benefit_type, \
                doctor_coupon_type = \
                dgc_infos[gid]

                _, doctor_name, doctor_type = doctor_id_to_data.get(doctor_id, (None, None, None))

                # desc = "满{}元减{}元".format(
                #     prepay_threshold, expense
                # ) if has_threshold else "直减{}元".format(expense)

                if benefit_type == BENEFIT_TYPE.ZHIJIAN:
                    desc = '直减{}元'.format(expense)
                elif benefit_type == BENEFIT_TYPE.MANJIAN:
                    desc = '满{}元减{}元'.format(prepay_threshold, expense)
                elif benefit_type == BENEFIT_TYPE.DISCOUNT:  # 尾款券目前没有折扣类型, 暂时不处理
                    pass

                if time_type == COUPON_TIME_TYPE.START_END:
                    ed = end_time.strftime('%Y.%m.%d') if end_time else ''
                    valid_period = "有效期至{}".format(ed)
                else:
                    valid_period = "领取后{}天内有效".format(countdown)

                doctor_coupon_use_desc = {
                    DOCTOR_USE_COUPON_TYPE.PART_GENERAL: '部分美购可用',
                    DOCTOR_USE_COUPON_TYPE.DOCTOR_GENERAL: '医生通用券',
                    DOCTOR_USE_COUPON_TYPE.FULL_PLATFORM_GENERAL: '全店通用',
                }[doctor_coupon_type]

        else:
            vt = expense
            if expense is not None and expense >= 10000:
                v = expense / 1000
                vt = str(v / 10 if v % 10 == 0 else float(v) / 10) + "万"

            desc = "共{}元（预付款抵扣）".format(vt)

            max_coupon, number = max(cgi['coupons'], key=lambda c: (-c[0]['discount_rate'], c[0]['value'], c[0]['id']))
            has_threshold = max_coupon['has_threshold']
            prepay_threshold = max_coupon['prepay_threshold']
            coupon_value = max_coupon['value']

            # 7780添加
            benefit_type = max_coupon['benefit_type']
            discount_rate = max_coupon['discount_rate']
            upper_limit = max_coupon['upper_limit']

        r = {'gift_id': gid,
             'gift_name': cgi['name'],
             'gift_type': gtype,
             'coupon_count': cgi['coupon_number'],
             'expense': expense,
             'desc': desc,
             'valid_period': valid_period,
             'has_gift_left': cgi['id'] in can_get_gift_id_set,

             'has_threshold': has_threshold,
             'threshold': prepay_threshold,
             'coupon_value': coupon_value,

             'doctor_id': doctor_id,  # DOCTOR专用字段可能为None， PLATFORM 返回None
             'doctor_name': doctor_name,  # DOCTOR专用字段可能为None， PLATFORM 返回None
             'doctor_type': doctor_type,  # DOCTOR专用字段可能为None， PLATFORM 返回None
             'coupons_info': cgi['coupons'], # 美购信息
             'has_discount_coupon': cgi['has_discount_coupon'],
             'best_discount_rate': cgi['best_discount_rate'],

             # 7780添加
             'benefit_type': benefit_type,
             'discount_rate': discount_rate,
             'upper_limit': upper_limit,
             'doctor_coupon_type': doctor_coupon_type,
             'doctor_coupon_use_desc': doctor_coupon_use_desc,
             }

        result.append(r)

    return result


gift_cache_wrap = cache_wrap(coupon_cache, pre_fix='gift_info_v1:@gift_id', expire=60, time_range=60)


@gift_cache_wrap.batch
def _gift_ids_to_gift_dtos(gift_ids):

    from api.models import CouponGift, Doctor, GiftToCoupon

    if not len(gift_ids) > 0:
        return []

    # cgs_dict = {x.id: x for x in CouponGift.objects.filter(id__in=gift_ids)}
    # cgs = [cgs_dict[gift_id] for gift_id in gift_ids if gift_id in cgs_dict]
    #
    # # http://wiki.gengmei.cc/pages/viewpage.action?pageId=4129058
    # # 因为现阶段的医生礼包只有一张医生美券，所以输出会麻烦很多...
    #
    # cg_info = [g.info_data for g in cgs]

    gtcs = [gt for gt in GiftToCoupon.objects.select_related('coupon').filter(
        coupon_gift_id__in=gift_ids, coupon_enable=True)]

    gid_to_info = {cg.id: (cg.name, cg.gift_type) for cg in CouponGift.objects.filter(id__in=gift_ids)}

    cgid_to_info = {}
    for gtc in gtcs:
        if gtc.coupon_gift_id not in cgid_to_info:
            name = ""
            gift_type = COUPON_GIFT_TYPES.PLATFORM
            if gtc.coupon_gift_id in gid_to_info:
                name, gift_type = gid_to_info[gtc.coupon_gift_id]

            cgid_to_info[gtc.coupon_gift_id] = {
                "id": gtc.coupon_gift_id,
                "name": name,
                "value": 0,
                "coupon_number": 0,
                "coupons": [],
                "gift_type": gift_type,

            }

        info = cgid_to_info[gtc.coupon_gift_id]
        # todo zhekou 折扣券怎么算价值
        if gtc.coupon.benefit_type == BENEFIT_TYPE.DISCOUNT:
            info['has_discount_coupon'] = True
            info['best_discount_rate'] = min(info.get('best_discount_rate', 100), gtc.coupon.discount_rate)
            info['value'] += (gtc.coupon.upper_limit - (gtc.coupon.upper_limit * gtc.coupon.discount_rate // 100)) * gtc.number
        else:
            info["value"] += gtc.coupon.value * gtc.number
            if info.get('has_discount_coupon') is None:
                info['has_discount_coupon'] = False
                info['best_discount_rate'] = 100
        info["coupon_number"] += gtc.number
        info["coupons"].append((gtc.coupon.data(), gtc.number))

    cg_info = [cgid_to_info[gift_id] for gift_id in gift_ids if gift_id in cgid_to_info]

    doctor_gift_ids = [dg['id'] for dg in cg_info if dg['gift_type'] == COUPON_GIFT_TYPES.DOCTOR]

    dgc_infos = {info[0]: info for info in CouponGift.objects.filter(id__in=doctor_gift_ids).values_list(
        'id', 'coupon__has_threshold', 'coupon__prepay_threshold', 'coupon__value', 'coupon__time_type',
        'coupon__start_time', 'coupon__end_time', 'coupon__countdown', 'coupon__doctor_id', 'coupon__benefit_type',
        'coupon__doctor_coupon_use_type')}

    all_doctor_ids = {dgc[8] for _, dgc in dgc_infos.items()}

    doctor_id_to_data = {d[0]: d for d in
                         Doctor.objects.filter(id__in=all_doctor_ids).values_list('id', 'name', 'doctor_type')}

    result = {}

    for cgi in cg_info:
        cgi['coupons'].sort(key=lambda x:(-x[0]['benefit_type'], x[0]['discount_rate']))
        gid = cgi['id']
        gtype = cgi['gift_type']

        expense = cgi['value']
        desc = ''
        valid_period = ''

        has_threshold = False
        prepay_threshold = 0
        coupon_value = 0

        benefit_type = 0
        discount_rate = 100
        upper_limit = 0

        doctor_id = None
        doctor_name = None
        doctor_type = None

        doctor_coupon_type = DOCTOR_USE_COUPON_TYPE.NONE_FOR_DOCTOR
        doctor_coupon_use_desc = ''

        if gtype == COUPON_GIFT_TYPES.DOCTOR:
            if gid in dgc_infos:
                _, has_threshold, prepay_threshold, coupon_value, time_type, start_time, end_time, countdown, doctor_id, benefit_type, \
                doctor_coupon_type = \
                dgc_infos[gid]

                _, doctor_name, doctor_type = doctor_id_to_data.get(doctor_id, (None, None, None))

                # desc = "满{}元减{}元".format(
                #     prepay_threshold, expense
                # ) if has_threshold else "直减{}元".format(expense)

                if benefit_type == BENEFIT_TYPE.ZHIJIAN:
                    desc = '直减{}元'.format(expense)
                elif benefit_type == BENEFIT_TYPE.MANJIAN:
                    desc = '满{}元减{}元'.format(prepay_threshold, expense)
                elif benefit_type == BENEFIT_TYPE.DISCOUNT:  # 尾款券目前没有折扣类型, 暂时不处理
                    pass

                if time_type == COUPON_TIME_TYPE.START_END:
                    ed = end_time.strftime('%Y.%m.%d') if end_time else ''
                    valid_period = "有效期至{}".format(ed)
                else:
                    valid_period = "领取后{}天内有效".format(countdown)

                doctor_coupon_use_desc = {
                    DOCTOR_USE_COUPON_TYPE.PART_GENERAL: '部分美购可用',
                    DOCTOR_USE_COUPON_TYPE.DOCTOR_GENERAL: '医生通用券',
                    DOCTOR_USE_COUPON_TYPE.FULL_PLATFORM_GENERAL: '全店通用',
                }[doctor_coupon_type]

        else:
            vt = expense
            if expense is not None and expense >= 10000:
                v = expense / 1000
                vt = str(v / 10 if v % 10 == 0 else float(v) / 10) + "万"

            desc = "共{}元（预付款抵扣）".format(vt)

            max_coupon, number = max(cgi['coupons'], key=lambda c: (-c[0]['discount_rate'], c[0]['value'], c[0]['id']))
            has_threshold = max_coupon['has_threshold']
            prepay_threshold = max_coupon['prepay_threshold']
            coupon_value = max_coupon['value']

            # 7780添加
            benefit_type = max_coupon['benefit_type']
            discount_rate = max_coupon['discount_rate']
            upper_limit = max_coupon['upper_limit']

        r = {'gift_id': gid, # gid = cgi['id']
             'gift_name': cgi['name'],
             'gift_type': gtype,
             'coupon_count': cgi['coupon_number'],
             'expense': expense,
             'desc': desc,
             'valid_period': valid_period,
             'has_gift_left': False,

             'has_threshold': has_threshold,
             'threshold': prepay_threshold,
             'coupon_value': coupon_value,

             'doctor_id': doctor_id,  # DOCTOR专用字段可能为None， PLATFORM 返回None
             'doctor_name': doctor_name,  # DOCTOR专用字段可能为None， PLATFORM 返回None
             'doctor_type': doctor_type,  # DOCTOR专用字段可能为None， PLATFORM 返回None
             'coupons_info': cgi['coupons'], # 美购信息
             'has_discount_coupon': cgi['has_discount_coupon'],
             'best_discount_rate': cgi['best_discount_rate'],

             # 7780添加
             'benefit_type': benefit_type,
             'discount_rate': discount_rate,
             'upper_limit': upper_limit,
             'doctor_coupon_type': doctor_coupon_type,
             'doctor_coupon_use_desc': doctor_coupon_use_desc,
             }

        result[gid] = r
    return result


def gift_ids_to_gift_dtos(can_get_gift_ids, already_get_gift_ids):
    '''
    根据礼包id, 获取礼包的格式化输出信息
    :param can_get_gift_ids:
    :param already_get_gift_ids:
    :return:
    '''

    can_get_gift_id_set = set(can_get_gift_ids)

    gift_ids = can_get_gift_ids + already_get_gift_ids

    gift_id_to_dto = _gift_ids_to_gift_dtos(gift_ids)

    result = []
    for gid in gift_ids:
        if gid in gift_id_to_dto:
            r = gift_id_to_dto[gid]
            r['has_gift_left'] = gid in can_get_gift_id_set
            result.append(r)

    return result


def fmt_discount_rate_show(discount_rate):
    '''
    格式化折扣券的外露折扣
    规则:
    66      60     6
    6.6折   6折    0.6折
    :param discount_rate:   1-99
    :return:  str_int
    '''
    divisor, remainder = divmod(discount_rate, 10)
    if remainder == 0:
        discount_str = str(divisor)
    else:
        discount_str = str(discount_rate / 10.0)
    return discount_str