# -*- coding: utf-8 -*-
from datetime import datetime, timedelta, date

from django.contrib.auth.models import User
from django.db import transaction
from gm_types.artemis import ACCREDIT_TYPE, ACCREDIT_STATUS, ACCREDIT_TIMES_TYPE
from gm_types.gaia import DOCTOR_TYPE

from api.models import VoiceAlertRecord, VoiceAlertSetting
from api.models.cptwhitelist import CPTWhiteList
from api.tasks.voice_alert_task import voice_alert_publish, \
    VOICE_ALERT_PUBLISH_TASK_ID_FORMAT
from hippo.models import Doctor, Merchant, SlaveMerchant, MerchantRelevance
from rpc.tool.log_tool import info_logger
from talos.cache.base import voice_alert_cache

VOICE_ALERT_LAST_ALERT_TIME_FORMAT = 'last_alert_time:{merchant_id}'
VOICE_ALERT_SURPLUS_TIMES_FORMAT = 'surplus:{white_list_id}:{accredit_times_type}:{key}'

ALERT_INTERVAL = timedelta(minutes=5)


def generate_surplus_key(white_list_id, accredit_times_type):
    key = ''
    if accredit_times_type == ACCREDIT_TIMES_TYPE.LIMIT_PER_DAY:
        key = date.today().strftime('%Y%m%d')
    key = VOICE_ALERT_SURPLUS_TIMES_FORMAT.format(white_list_id=white_list_id,
                                                  accredit_times_type=accredit_times_type,
                                                  key=key)

    return key


def get_surplus_times(white_list_id, accredit_times_type):
    key = generate_surplus_key(white_list_id, accredit_times_type)
    return voice_alert_cache.get(key)


def set_surplus_times(white_list_id, accredit_times_type, value):
    key = generate_surplus_key(white_list_id, accredit_times_type)
    if accredit_times_type == ACCREDIT_TIMES_TYPE.LIMIT_PER_DAY:
        voice_alert_cache.set(key, value, ex=86400)
    else:
        voice_alert_cache.set(key, value)


def decr_surplus_times(white_list_id, accredit_times_type):
    key = generate_surplus_key(white_list_id, accredit_times_type)
    voice_alert_cache.decr(key)


def check_voice_alert_permission(merchant_id_list):
    merchant_qs = Merchant.objects.filter(id__in=merchant_id_list).values('id', 'doctor_id')
    doctor_dict = {merchant.get('doctor_id'): merchant.get('id') for merchant in merchant_qs}
    now = datetime.now()
    # 是否在服务器内
    whiltelist_qs = CPTWhiteList.objects.filter(doctor_id__in=list(doctor_dict.keys()),
                                enable=True,
                                accredit_type=ACCREDIT_TYPE.MESSAGE_VOICE_ALERT,
                                start_time__lte=now,
                                end_time__gt=now)
    result = {}
    # 检查次数限制
    for whitelist in whiltelist_qs:
        status = whitelist.get_status()
        merchant_id = doctor_dict.get(whitelist.doctor_id)
        if status == ACCREDIT_STATUS.ING: # 生效中
            beginning_of_today = datetime.today().replace(hour=0, minute=0, second=0, microsecond=0)
            accredit_times_type = whitelist.accredit_times_type

            if accredit_times_type == ACCREDIT_TIMES_TYPE.NO_LIMIT:
                result.update({merchant_id:whitelist.id})
                continue

            surplus_times = get_surplus_times(whitelist.id, accredit_times_type)
            if surplus_times is not None:
                if int(surplus_times) > 0:
                    result.update({merchant_id:whitelist.id})
                else:
                    info_logger.info('check surplus_times failed, merchan_id: %r, '
                                     'accredit_times_type: %r, accredit_times: %r, cache: %r',
                                     merchant_id, accredit_times_type,
                                     whitelist.accredit_times, surplus_times)

            elif accredit_times_type == ACCREDIT_TIMES_TYPE.LIMIT_PER_DAY:
                with transaction.atomic():
                    today_count = VoiceAlertRecord.objects.select_for_update().\
                        filter(white_list_id=whitelist.id,
                               merchant_id=merchant_id,
                               alert_time__gt=beginning_of_today)\
                        .count()
                    if whitelist.accredit_times > today_count:
                        result.update({merchant_id:whitelist.id})
                    else:
                        info_logger.info('check surplus_times failed, merchan_id: %r, '
                                         'accredit_times_type: %r, accredit_times: %r, today_use: %r',
                                         merchant_id, accredit_times_type, whitelist.accredit_times, today_count)

            elif accredit_times_type == ACCREDIT_TIMES_TYPE.LIMIT_TOTAL:
                with transaction.atomic():
                    total_count = VoiceAlertRecord.objects.select_for_update()\
                        .filter(white_list_id=whitelist.id,
                                merchant_id=merchant_id,
                                alert_time__gt=whitelist.start_time)\
                        .count()
                    if whitelist.accredit_times > total_count:
                        result.update({merchant_id:whitelist.id})
                    else:
                        info_logger.info('check surplus_times failed, merchan_id: %r, '
                                         'accredit_times_type: %r, accredit_times: %r, total_use: %r',
                                         merchant_id, accredit_times_type,
                                         whitelist.accredit_times, total_count)

    return result


def get_last_alert_time(merchant_id_list):
    prefix = object.__getattribute__(voice_alert_cache, 'prefix')
    cached_values = voice_alert_cache.mget([(prefix+':'+VOICE_ALERT_LAST_ALERT_TIME_FORMAT)
                                           .format(merchant_id=i) for i in merchant_id_list])
    for index, merchant_id in enumerate(merchant_id_list):
        cached_value = cached_values[index]
        if not cached_value:
            # query DB
            last_record = VoiceAlertRecord.objects.filter(merchant_id=merchant_id)\
                .order_by('-alert_time').first()
            if last_record:
                cached_values[index] = last_record.alert_time
        else:
            cached_values[index] = datetime.fromtimestamp(float(cached_value))
    return cached_values


def find_message_need_alert_merchant(message, target_user):
    if not target_user:
        target_user = message.conversation.get_target_user(User.objects.get(id=message.user_id))
    if not target_user:
        return None, None
    doctor = Doctor.objects.filter(user=target_user).first()
    if not doctor:
        return None, None
    # if doctor.doctor_type == DOCTOR_TYPE.OFFICER:
    #     merchant = Merchant.objects.filter(doctor=doctor).first()
    #     if merchant:
    #         return [merchant.id]
    #     return
    # doctor = Doctor.objects.prefetch_related('merchant').filter(hospital=doctor.hospital,
    #                                                    doctor_type=DOCTOR_TYPE.OFFICER).first()
    # return [doctor.merchant.id] if doctor.merchant else None
    # current_merchant = Merchant.objects.filter(doctor=doctor).first()
    merchant_relevance = MerchantRelevance.objects.filter(doctor=doctor).last()
    if merchant_relevance and merchant_relevance.merchant:
        current_merchant = merchant_relevance.merchant
    else:
        return None, None
    # 向上查找私信关联的所有医生对应的商户 + 医生本人对应的商户，返回商户id
    relation = SlaveMerchant.objects.filter(slavemerchant=current_merchant).first()
    if relation:
        master_merchant = relation.mastermerchant.mastermerchant
        if master_merchant:
            return [master_merchant.id, current_merchant.id], target_user
    return [current_merchant.id], target_user


def dispatch_voice_alert_task(message, target_user):
    merchant_id_list, target_user = find_message_need_alert_merchant(message, target_user)
    if not merchant_id_list or not target_user:
        info_logger.info('dispatch_voice_alert_task not found merchant or target_user, message: %r',
                         message.id)
        return
    permission_result = check_voice_alert_permission(merchant_id_list)
    merchant_id_list = list(permission_result.keys())

    if not permission_result:
        info_logger.info(
            'dispatch_voice_alert_task permission error, message: %r',
            message.id)
        return

    setting_qs = VoiceAlertSetting.objects.filter(merchant_id__in=merchant_id_list,
                                                  alert_interval__gt=0) \
        .values('merchant_id', 'alert_interval')
    setting_dict = {setting.get('merchant_id'): setting.get('alert_interval')
                    for setting in setting_qs}

    last_alert_time_dict = {merchant_id: last_alert_time for merchant_id, last_alert_time
                            in
                            zip(merchant_id_list, get_last_alert_time(merchant_id_list))}

    now = datetime.now()
    for merchant_id in merchant_id_list:
        last_alert_time = last_alert_time_dict.get(merchant_id)
        if last_alert_time and (now - last_alert_time) < ALERT_INTERVAL:
            info_logger.info(
                'dispatch_voice_alert_task too frequently, message: %r, merchant_id: %r, '
                'last_alert_time: %s', message.id, merchant_id, last_alert_time)
            continue
        countdown = setting_dict.get(merchant_id)
        white_list_id = permission_result.get(merchant_id)
        voice_alert_publish.apply_async(args=(message.id, merchant_id, white_list_id, target_user.id),
                                        # eta=message.send_time+timedelta(seconds=countdown),
                                        countdown=countdown,
                                        task_id=VOICE_ALERT_PUBLISH_TASK_ID_FORMAT.format(
                                            message_id=message.id))