#!/usr/bin/env python
# coding=utf-8
#
#   Author  :   zhangxiaolin
#   E-mail  :   petelin1120@gmail.com
#   Date    :   16/10/25 19:00
#   Desc    :   秒杀活动相关,对应model special,对应ascle,activity

from datetime import datetime

from django.db.models import Q
from rpc.decorators import bind, bind_context
from rpc.decorators import list_interface
from rpc.tool.error_code import gen, CODES
from rpc.tool.dict_mixin import to_dict
from api.tool.datetime_tool import get_timestamp, get_timestamp_epoch
from api.models.doctor import Doctor
from hippo.models.merchant import MerchantRelevance
from api.tool.user_tool import get_doctor_from_context_or_exception
from api.models.special import SpecialSeckillButton
from api.models.special import DoctorSeckillApply
from api.models.special import DoctorSeckillApplyRecord 

from api.models.service import ServiceItem, Service
from gm_types.gaia import DOCTOR_SECKILL_APPLY_STATUS, DOCTOR_SECKILL_APPLY_TYPE, DOCTOR_TYPE, RANGE_TYPE


@bind_context('doctor/special/seckill/list', login_required=True)
def special_seckill_list(ctx):
    # TODO Deprecated 2017-05-03 下次上线可以删除
    now = datetime.now()
    ss = SpecialSeckillButton.objects.filter(start_show_time__lte=now, end_show_time__gte=now).order_by(
        "-start_show_time")
    return [{
        "id": s.id,
        "name": s.button_document
    } for s in ss]


@bind_context('doctor/special/seckill/list_v2', login_required=True)
def special_seckill_list_v2(ctx, doctor_id, activity_id=None, activity_name=None, activity_start_at=None, 
                            activity_end_at=None, activity_type=-1, signin_start_at=None, 
                            signin_end_at=None, signin_status=-1, service_id=None, start_num=0, count=10):

    search_dict = {}

    now = datetime.now()
    search_dict["start_show_time__lte"] = now
    search_dict["end_show_time__gte"] = now

    if activity_id:
        search_dict["id"] = activity_id

    if activity_name:
        search_dict["button_document__contains"] = activity_name

    if activity_type != -1:
        search_dict["activity_type"] = activity_type

    ss = SpecialSeckillButton.objects.filter(**search_dict)

    if signin_start_at and signin_end_at:
        ss = ss.exclude(Q(end_show_time__lte=signin_start_at)|Q(start_show_time__gte=signin_end_at)) 

    if activity_start_at and activity_end_at:
        ss = ss.exclude(Q(end_sell_time__lte=activity_start_at)|Q(start_sell_time__gte=activity_end_at)) 

    ss = ss.order_by("-start_show_time")

    doctor = Doctor.objects.get(id=doctor_id)
    city = doctor.hospital.city

    #展示部分商户可以报名的活动以及今天可以报名的活动

    ssb_ids = []
    parent_doctor = doctor.merchant_doctor

    for s in ss:
        if s.range_type == RANGE_TYPE.PARTIAL_CITY:
            if city in s.cities.all():
                ssb_ids.append(s.id)

        elif s.range_type == RANGE_TYPE.PARTIAL_MERCHANT:
            if parent_doctor in s.doctors.filter(is_merchant=True):
                ssb_ids.append(s.id)

        else:
            ssb_ids.append(s.id)

    ss = ss.filter(id__in=ssb_ids)

    #如果有service_id的筛选
    if service_id:
        service = Service.objects.get(id=service_id)
        if service.tags.all():
            can_apply_ids = []
            for ssb in ss:
                ssb_tags = SpecialSeckillButton.objects.get(id=ssb.id).tags.all()
                tags = []
                if ssb_tags:
                    for ssb_tag in ssb_tags:
                        tags.append(ssb_tag.id)
                        for s2 in ssb_tag.child_tags():
                            tags.append(s2.id)
                            for s3 in s2.child_tags():
                                tags.append(s3.id)
                    s_tags = service.tags.all().values_list('id', flat=True)
                    same_tags = set(s_tags) & set(tags)
                    if list(same_tags):
                        can_apply_ids.append(ssb.id)
                else:
                    can_apply_ids.append(ssb.id)
            ss = ss.filter(id__in=can_apply_ids)

    data = {
        'total': ss.count(),
    }
    FMT = '%Y-%m-%d %H:%M:%S'

    signin_data = []
    nosign_data = []
    total_data = []

    if doctor.doctor_type==DOCTOR_TYPE.OFFICER and doctor.is_merchant:  
        doctor_ids = [doctor.id for doctor in doctor.merchant_doctors()] 
    else:                                
        doctor_ids = [doctor.id]

    #查看这个医生所报名的活动
    doctor_signin_num = 0
    for special in ss:
        doctor_signin_activity= DoctorSeckillApply.objects.filter(
                                        service_item__service__doctor__in=doctor_ids, 
                                        seckill_button=special.id)
        if doctor_signin_activity:
            doctor_signin_num = doctor_signin_num + 1

    doctor_nosign_num = ss.count() - doctor_signin_num 

    for special in ss:
        examine_status = {}
        special_filter = DoctorSeckillApply.objects.filter(seckill_button=special.id)
        officer_filter = special_filter.filter(service_item__service__doctor__in=doctor_ids)

        pass_num = officer_filter.filter(status=DOCTOR_SECKILL_APPLY_STATUS.PASS).count()
        again_num = officer_filter.filter(status=DOCTOR_SECKILL_APPLY_STATUS.RE_APPLY).count()
        nodeal_num = officer_filter.filter(status=DOCTOR_SECKILL_APPLY_STATUS.DEFAULT).count()
        refuse_num = officer_filter.filter(status=DOCTOR_SECKILL_APPLY_STATUS.REJECT).count()
        signin_num = pass_num + again_num + nodeal_num + refuse_num

        examine_status["pass_num"] = pass_num
        examine_status["again_num"] = again_num
        examine_status["nodeal_num"] = nodeal_num
        examine_status["refuse_num"] = refuse_num
        examine_status["signin_num"] = signin_num

        if officer_filter:
            doctor_signin_status = 1 
        else:
            doctor_signin_status = 0

        activity_data = {
            "activity_id": special.id,
            "activity_name": special.button_document,
            "activity_type": special.activity_type,
            "activity_start_at": special.start_sell_time.strftime(FMT) if special.start_sell_time else '',
            "activity_end_at": special.end_sell_time.strftime(FMT) if special.end_sell_time else '',
            "signin_start_at": special.start_show_time.strftime(FMT) if special.start_show_time else '',
            "signin_end_at": special.end_show_time.strftime(FMT) if special.end_show_time else '',
            "doctor_signin_status": doctor_signin_status,
            "examine_status": examine_status
        }

        if doctor_signin_status == 0:
            total_data.append(activity_data)
            nosign_data.append(activity_data)

        if doctor_signin_status == 1:
            total_data.append(activity_data)
            signin_data.append(activity_data)
        
    data["total_signin_num"] = doctor_signin_num 
    data["total_nosign_num"] = doctor_nosign_num

    if signin_status == -1:
        data["specials"] = total_data[start_num: start_num + count] 

    elif signin_status == 0:
        data['specials'] = nosign_data[start_num: start_num + count]

    elif signin_status == 1:
        data["specials"] = signin_data[start_num: start_num + count]

    else:
        data["specials"] = []
    
    return data


def can_show_activity(doctor_id):
    now = datetime.now()
    city = Doctor.objects.get(id=doctor_id).hospital.city
    ss = SpecialSeckillButton.objects.filter(
        start_show_time__lte=now, end_show_time__gte=now
    ).order_by("-start_show_time")
    ssb_ids = []

    for s in ss:
        if s.cities.all():
            if city in s.cities.all():
                if city in s.cities.all():
                    ssb_ids.append(s.id)
        else:
            ssb_ids.append(s.id)
    ss = ss.filter(id__in=ssb_ids)
    if ss.count():
        return True
    else:
        return False


def _get_online_items(doctor):
    """
    获取医生所有在线美购属性
    """
    services = Service.objects.filter(doctor=doctor, is_online=True).prefetch_related(
        "items")

    result = []
    for service in services:
        items = []
        for item in service.items.all():
            if item.is_delete:
                continue
            items.append(
                {
                    "id": item.id,
                    "name": "".join(item.items_name),
                    # "gengmei_price": item.gengmei_price
                    "gengmei_price": item.get_default_price_info().get('gengmei_price', 0),
                }
            )
        result.append({
            "service_id": service.id,
            "service_name": service.name,
            "items": items
        })

    return result


def _gen_special_seckill_button(button):
    return {
        "id": button.id,
        "name": button.button_document,
        "start_time": get_timestamp_epoch(button.start_sell_time),
        "special_end_time": get_timestamp_epoch(button.end_sell_time),
        "describe": button.content,
        "stock_limit": button.stock_limit,
        "activity_type": button.activity_type,
        "discount_rate": button.discount_rate,
        "fixed_price": button.fixed_price,
    }


def _gen_seckill_apply(apply):
    price_info = apply.service_item.get_default_price_info()
    default_gengmei_price = price_info.get('gengmei_price', 0)
    data = {
        "activity_id": apply.seckill_button.id,
        "apply_id": apply.id,
        "item_id": apply.service_item.id,
        "service_id": apply.service_item.service.id,
        "service_name": apply.service_item.service.name,
        "doctor_name": apply.service_item.service.doctor.name,
        "doctor_id": apply.service_item.service.doctor_id,
        "items_name": "".join(apply.service_item.items_name),
        "activity_name": apply.seckill_button.button_document,
        "submit_time": get_timestamp(apply.latest_apply_time),
        "gengmei_price": default_gengmei_price,
        "seckill_price": apply.seckill_price,
        "stock": apply.stock,
        "status": apply.status,
        "end_time": get_timestamp_epoch(apply.service_item.service.end_time),
    }
    data.update(service_type=apply.service_item.service.service_type)
    if data.get('service_type', None) == 3:
        data.update(city_name=apply.service_item.city.name)
    return data


@bind_context('doctor/special/seckill/apply_list', login_required=True)
@list_interface(offset_name='offset', limit_name='count', element_model=DoctorSeckillApply)
def special_seckill_doctor_applylist(ctx, service_id=None, service_name=None, item_id=None, item_name=None,
                                    activity_id=None, activity_name=None, activity_type=-1, activity_start_at=None,
                                    activity_end_at=None, signin_start_at=None, signin_end_at=None,
                                    check_status=-1, offset=0, count=10):
    """signin_statusstatus in [-1, *DOCTOR_SECKILL_APPLY_STATUS], -1 for 代表需要全部的数据"""
    doctor = get_doctor_from_context_or_exception(ctx)
    search_dict = {}

    if doctor.doctor_type==DOCTOR_TYPE.OFFICER and doctor.is_merchant:  #如果为机构管理者
        search_dict["service_item__service__doctor__in"] = [doctor.id for doctor in doctor.merchant_doctors()] 
    else:
        search_dict["service_item__service__doctor"] = doctor.id

    if activity_type != -1:
        search_dict["seckill_button__activity_type"] = activity_type

    if activity_id:
        search_dict["seckill_button__id"] = activity_id

    if activity_name:
        search_dict["seckill_button__button_document__contains"] = activity_name

    if service_id:
        search_dict["service_item__service"] = service_id

    if service_name:
        search_dict["service_item__service__name__contains"] = service_name

    if item_id:
        search_dict["service_item"] = item_id

    if item_name:
        search_dict["service_item__key__contains"] = item_name

    search_filter = DoctorSeckillApply.objects.filter(**search_dict)

    if signin_start_at and signin_end_at:
        search_filter = search_filter.exclude(
            Q(seckill_button__end_show_time__lte=signin_start_at)|Q(seckill_button__start_show_time__gte=signin_end_at))

    if activity_start_at and activity_end_at:
        search_filter = search_filter.exclude(
            Q(seckill_button__end_sell_time__lte=activity_start_at)|Q(seckill_button__start_sell_time__gte=activity_end_at))

    if check_status == -1:
        activity_applys = search_filter.order_by("-latest_apply_time")
    else:
        activity_applys = search_filter.filter(status=check_status).order_by("-latest_apply_time")

    nodeal_num = search_filter.filter(status=DOCTOR_SECKILL_APPLY_STATUS.DEFAULT).count()
    refuse_num = search_filter.filter(status=DOCTOR_SECKILL_APPLY_STATUS.REJECT).count()
    again_num = search_filter.filter(status=DOCTOR_SECKILL_APPLY_STATUS.RE_APPLY).count()
    pass_num = search_filter.filter(status=DOCTOR_SECKILL_APPLY_STATUS.PASS).count()

    FMT = '%Y-%m-%d %H:%M:%S'
    data = {"specials": [], 
            "total":search_filter.count(),
            "nodeal_num": nodeal_num,
            "refuse_num": refuse_num,
            "again_num": again_num,
            "pass_num": pass_num
            }

    for activity_apply in activity_applys[offset: offset+count]:
        activity = activity_apply.seckill_button    #获取活动对象
        apply_record = DoctorSeckillApplyRecord.objects.filter(
                        doctorseckillapply=activity_apply.id).order_by("-created_time").first()
        
        if apply_record and "其他。" in apply_record.comment:
            comment = apply_record.comment.replace("其他。", "")
        elif apply_record: 
            comment = apply_record.comment
        else:
            comment = ""

        service_item = ServiceItem.objects.get(id=activity_apply.service_item.id)
        service = service_item.service
        
        my_data = { 
            "activity_id": activity.id,
            "activity_name": activity.button_document,
            "activity_type": activity.activity_type,
            "service_id": service.id,
            "service_name": service.name,
            "image_url": service.image_header,
            "item_id": service_item.id,
            "item_name": "".join(service_item.items_name),
            "signin_at": activity_apply.created_time.strftime(FMT),
            "item_price": service_item.gengmei_price,
            "item_advance_payment": service_item.pre_payment_price,
            "activity_price": activity_apply.seckill_price,
            "activity_advance_payment": activity_apply.pre_payment_price,
            "activity_stock": activity_apply.stock,
            "check_status": activity_apply.status,
            "apply_id": activity_apply.id,
            "refuse_reason": comment
        }
        my_data.update(service_type=service_item.service.service_type)
        if my_data.get('service_type', None) == 3:
            my_data.update(city_name=service_item.city.name)
        data["specials"].append(my_data)

    return data

@bind_context('doctor/special/seckill/detail', login_required=True)
def special_seckill_detail(ctx, seckill_id):
    doctor = get_doctor_from_context_or_exception(ctx)
    try:
        if doctor.is_merchant:
            doctor_ids = MerchantRelevance.objects.get(doctor_id=doctor.id)\
                .merchant.merchant_doctors.all().values_list('doctor_id', flat=True)
        else:
            doctor_ids = [doctor.id]
    except:
        doctor_ids = [doctor.id]

    button = SpecialSeckillButton.objects.get(id=seckill_id)

    doctor_applys = DoctorSeckillApply.objects.filter(
        seckill_button=button,
        service_item__service__doctor_id__in=doctor_ids
    ).select_related("service_item__service")
    result = {
        "submited_applys": [_gen_seckill_apply(apply) for apply in doctor_applys],
        "online_items": _get_online_items_use_tags(doctor, ssb_id=button.id),
    }
    result.update(_gen_special_seckill_button(button))
    return result



def _get_online_items_use_tags(doctor, ssb_id):
    """
    获取医生所有tag和活动相符的在线美购
    """
    try:
        if doctor.is_merchant:
            doctor_ids = MerchantRelevance.objects.get(doctor_id=doctor.id)\
                .merchant.merchant_doctors.all().values_list('doctor_id', flat=True)
        else:
            doctor_ids = [doctor.id]
    except:
        doctor_ids = [doctor.id]
    ssb_tags = SpecialSeckillButton.objects.get(id=ssb_id).tags.all()
    tags = []
    if ssb_tags:
        for ssb_tag in ssb_tags:
            tags.append(ssb_tag.id)
            for s2 in ssb_tag.child_tags():
                tags.append(s2.id)
                for s3 in s2.child_tags():
                    tags.append(s3.id)
        services_before = Service.objects.filter(doctor_id__in=doctor_ids, is_online=True)
        s_ids = []
        for s_item in services_before:
            if s_item.tags.all():
                s_tags = s_item.tags.all().values_list('id', flat=True)
                same_tags = set(s_tags) & set(tags)
                if list(same_tags):
                    s_ids.append(s_item.id)
            else:
                s_ids.append(s_item.id)
        services = services_before.filter(id__in=s_ids).prefetch_related("items")
    else:
        services = Service.objects.filter(doctor_id__in=doctor_ids, is_online=True)\
            .prefetch_related("items")

    result = []
    for service in services:
        items = []
        for item in service.items.filter(parent_id=0):
            if item.is_delete:
                continue
            data = {
                    "id": item.id,
                    "name": "".join(item.items_name),
                    # "gengmei_price": item.gengmei_price
                    "gengmei_price": item.get_default_price_info().get('gengmei_price', 0),
                }
            if service.service_type == 3:
                data['city_name'] = item.city.name if item.city else ''
            items.append(data)
        result.append({
            "service_id": service.id,
            "service_name": service.name,
            "items": items,
            "end_time": get_timestamp_epoch(service.end_time),
            'service_type': service.service_type
        })

    return result


def activity_limit_check(actit, sku, price, stock):
    """
    :param actit: `SpecialSeckillButton` object
    :param sku: `ServiceItem` object
    :param price: doctor apply price for this activity
    :param stock: doctor apply stock for this activity

    :return: if pass return True, else raise exception
    """
    # 不允许库存小于等于0
    if stock <= 0:
        return gen(CODES.PARAMS_INVALID)

    # 不允许提报的价格大于默认更美价
    sku_default_price_info = sku.get_default_price_info()
    if not sku_default_price_info or price > sku_default_price_info['gengmei_price']:
        return gen(CODES.ACTIVITY_PRICE_OVER)

    # 如果活动有库存数量限制，不允许提报的库存大于活动的限制
    if actit.stock_limit is not None and stock > actit.stock_limit:
        return gen(CODES.ACTIVITY_SKU_LIMIT)

    return True


@bind_context('doctor/special/seckill/create', login_required=True)
def special_seckill_create(ctx, seckill_id, service_item_id, stock, seckill_price):
    doctor = get_doctor_from_context_or_exception(ctx)

    now_t = datetime.now()
    buttons = SpecialSeckillButton.objects.filter(id=seckill_id, end_show_time__gte=now_t)
    if not buttons:
        return gen(CODES.ACTIVITY_CLOSED)
    button = buttons[0]
    service_item = ServiceItem.objects.get(id=service_item_id, is_delete=False)

    if DoctorSeckillApply.objects.filter(seckill_button=button, service_item=service_item).exists():
        return gen(CODES.ACTIVITY_PARTICIPATE_LIMIT)

    activity_limit_check(button, service_item, seckill_price, stock)

    apply = DoctorSeckillApply(service_item=service_item, seckill_price=seckill_price, stock=stock,
                               seckill_button=button)
    apply.save()
    return {"apply_id": apply.id}


@bind_context('doctor/special/seckill/edit', login_required=True)
def special_seckill_edit(ctx, apply_id, stock, seckill_price, doctor_id):
    """只有驳回之后才能编辑"""
    doctor = Doctor.objects.get(id=doctor_id)

    apply = DoctorSeckillApply.objects.get(id=apply_id, service_item__service__doctor=doctor)
    if apply.status != DOCTOR_SECKILL_APPLY_STATUS.REJECT:
        return gen(CODES.PARAMS_INVALID)

    activity_limit_check(apply.seckill_button, apply.service_item, seckill_price, stock)

    apply.seckill_price = seckill_price
    apply.stock = stock
    apply.status = DOCTOR_SECKILL_APPLY_STATUS.RE_APPLY
    apply.latest_apply_time = datetime.now()
    apply.save()
    return {"apply_id": apply.id}


@bind_context('doctor/special/seckill/view', login_required=True)
def special_seckill_view(ctx, seckill_id, apply_id):
    doctor = get_doctor_from_context_or_exception(ctx)

    button = SpecialSeckillButton.objects.get(id=seckill_id)
    seckill_apply = DoctorSeckillApply.objects.select_related(
        "service_item__service").prefetch_related("records").get(id=apply_id)

    result = _gen_special_seckill_button(button)
    result.update(_gen_seckill_apply(seckill_apply))
    result["reject_records"] = [{
                                    "id": record.id,
                                    "comment": record.comment,
                                    "created_time": get_timestamp(record.created_time)
                                } for record in seckill_apply.records.filter(is_pass=False).all()]
    return result
