# -*- coding: utf-8 -*-
import time
from datetime import datetime

from celery import shared_task
from django.conf import settings
from django.contrib.auth.models import User
from django.db import transaction
from gm_types.artemis import ACCREDIT_TYPE, ACCREDIT_TIMES_TYPE, ACCREDIT_STATUS
from qcloudsms_py import TtsVoiceSender
from qcloudsms_py.httpclient import HTTPError

from api.models import Message, VoiceAlertRecord, VoiceAlertStaff, \
    VoiceAlertStaffDutyTable, Doctor, ConversationUserStatus
from api.models.cptwhitelist import CPTWhiteList
from hippo.models import Merchant
from hippo.models.chain_hospital import MasterMerchant
from rpc.tool.log_tool import info_logger, exception_logger
from services.unread.stat import UserUnread
from talos.cache.base import voice_alert_cache

VOICE_ALERT_PUBLISH_TASK_ID_FORMAT = 'gm-voice-alert-{message_id}'


def send_voice_alert(message_id, merchant_id, phone, unread_count):
    vvcsender = TtsVoiceSender(settings.TENCENT_VMS_APP_ID,
                               settings.TENCENT_VMS_APP_KEY)
    try:
        result = vvcsender.send(settings.TENCENT_VMS_VOICE_ALERT_TEMPLATE_ID,
                       [str(unread_count)],
                       phone)
    except HTTPError as e:
        exception_logger.exception(str(e))
    except Exception as e:
        exception_logger.exception(str(e))
    # {'result': 0, 'errmsg': 'OK', 'callid': 'da701c44-8a8d-11ea-9a6e-5254004815a7', 'ext': ''}
    if isinstance(result, dict) and result.get('result') == 0:
        return result.get('callid')

    info_logger.info('send_voice_alert failed, message_id: %r, merchant_id: %r, phone: %r, vms_result: %r',
                     message_id, merchant_id, phone, result)


def get_receiver_staff(now, merchant_id):
    weekday = now.weekday()
    today_delta = (now - now.replace(hour=0, minute=0, second=0, microsecond=0)).seconds
    qs = VoiceAlertStaffDutyTable.objects.prefetch_related('staff')\
        .filter(setting__merchant_id=merchant_id,
                weekday=weekday,
                start_time__lte=today_delta,
                end_time__gte=today_delta)
    return [table_item.staff for table_item in qs]


def handle_new_voice_alert(merchant_id, white_list_obj, staff, message, unread_count):
    from api.tool.voice_alert import VOICE_ALERT_LAST_ALERT_TIME_FORMAT, get_surplus_times, \
        set_surplus_times, decr_surplus_times

    callid = send_voice_alert(message.id, merchant_id, staff.phone, unread_count)
    if not callid:
        info_logger.info(
            'handle_new_voice_alert call send_voice_alert failed, message_id: %r, merchand_id: %r, '
            'unread_count: %r, staff_id: %r, staff_name: %r', message.id, merchant_id, unread_count,
            staff.id, staff.name)
        return

    target_user = message.conversation.get_target_user(
        User.objects.get(id=message.user_id))
    now = datetime.now()
    VoiceAlertRecord.objects.create(merchant_id=merchant_id,
                                    white_list_id=white_list_obj.id,
                                    message_receiver_user_id=target_user.id,
                                    message=message,
                                    alert_time=now,
                                    receive_staff=staff,
                                    vms_callid=callid)


    # 减少剩余次数，更新上次发送的时间缓存
    accredit_times_type = white_list_obj.accredit_times_type
    cached_surplus_times = get_surplus_times(white_list_obj.id, accredit_times_type)

    if cached_surplus_times is None:
        if accredit_times_type == ACCREDIT_TIMES_TYPE.LIMIT_PER_DAY:
            beginning_of_today = datetime.today().replace(hour=0, minute=0, second=0,
                                                          microsecond=0)

            with transaction.atomic():
                today_count = VoiceAlertRecord.objects.filter(white_list_id=white_list_obj.id,
                                                              merchant_id=merchant_id,
                                                              alert_time__gt=beginning_of_today) \
                    .count()
                set_surplus_times(white_list_obj.id, accredit_times_type, white_list_obj.accredit_times - today_count)

        elif accredit_times_type == ACCREDIT_TIMES_TYPE.LIMIT_TOTAL:
            with transaction.atomic():
                total_count = VoiceAlertRecord.objects.filter(white_list_id=white_list_obj.id,
                                                              merchant_id=merchant_id,
                                                              alert_time__gt=white_list_obj.start_time) \
                    .count()
                set_surplus_times(white_list_obj.id, accredit_times_type, white_list_obj.accredit_times - total_count)
    else:
        decr_surplus_times(white_list_obj.id, accredit_times_type)

    # 更新上次发送报警的时间
    voice_alert_cache.set(VOICE_ALERT_LAST_ALERT_TIME_FORMAT.format(merchant_id=merchant_id),
                          str(time.mktime(now.timetuple())))

    # 已完成状态处理
    white_list_obj = CPTWhiteList.objects.filter(id=white_list_obj.id).first()
    tmp_status = white_list_obj.get_status()
    if tmp_status == ACCREDIT_STATUS.CANCEL:
        return
    if tmp_status == ACCREDIT_STATUS.ING:
        if white_list_obj.accredit_times_type == ACCREDIT_TIMES_TYPE.LIMIT_PER_DAY:
            beginning_of_today = datetime.today().replace(hour=0, minute=0, second=0,
                                                          microsecond=0)
            today_count = VoiceAlertRecord.objects.filter(
                white_list_id=white_list_obj.id,
                merchant_id=merchant_id,
                alert_time__gt=beginning_of_today).count()
            surplus_times = white_list_obj.accredit_times - today_count
            if surplus_times <= 0 and beginning_of_today.date() >= white_list_obj.end_time.date():
                tmp_status = ACCREDIT_STATUS.DONE
        elif white_list_obj.accredit_times_type == ACCREDIT_TIMES_TYPE.LIMIT_TOTAL:
            start_time = white_list_obj.start_time
            total_count = VoiceAlertRecord.objects.filter(
                white_list_id=white_list_obj.id,
                merchant_id=merchant_id,
                alert_time__gt=start_time).count()
            surplus_times = int(white_list_obj.accredit_times) - total_count
            if surplus_times <= 0:
                tmp_status = ACCREDIT_STATUS.DONE
        white_list_obj.status = tmp_status
        white_list_obj.save()


@shared_task
def voice_alert_publish(message_id, merchant_id, white_list_id, target_user_id):
    '''
    @param message_id: 触发通知的message_id
    @param merchant_id: 需要发送给开通了服务的哪个商户
    @param target_user_id: 私信目标用户的user_id
    @return:
    '''
    # 检查上次发送的时间
    from api.tool.voice_alert import get_last_alert_time, ALERT_INTERVAL, check_voice_alert_permission
    last_alert_time = get_last_alert_time([merchant_id])[0]
    now = datetime.now()
    if last_alert_time and (now - last_alert_time) < ALERT_INTERVAL:
        info_logger.info(
            'voice-alert check interval failed, message_id: %r, merchand_id: %r, target_user_id: %r',
            message_id, merchant_id, target_user_id)
        return

    # 检查权限和剩余次数
    if not white_list_id:
        return
    white_list = CPTWhiteList.objects.filter(id=white_list_id).first()
    if not white_list or white_list.status != ACCREDIT_STATUS.ING:
        info_logger.info('voice-alert check permission failed, message_id: %r, merchand_id: %r, white_list_id: %r, '
                         'target_user_id: %r',
                         message_id, merchant_id, white_list_id, target_user_id)
        return

    # 找到触发的私信
    try:
        message = Message.objects.get(id=message_id)
    except Message.DoesNotExist:
        info_logger.info(
            'voice-alert message doesnot exists, message_id: %r, merchand_id: %r, target_user_id: %r',
            message_id, merchant_id, target_user_id)
        return

    # 查看私信是否为已读, 如果已读，则触发提醒的为下一个未读
    if message.is_read:
        info_logger.info(
            'voice-alert message has been read, message_id: %r, merchand_id: %r',
            message_id, merchant_id)
        return

    # 找到私信的target_doctor
    target_doctor = Doctor.objects.filter(user_id=target_user_id).first()
    if not target_doctor:
        info_logger.info(
            'voice-alert message doesnot exists, message_id: %r, merchand_id: %r, target_user_id: %r',
            message_id, merchant_id, target_user_id)
        return

    # 在窗口期内, 查找merchant和 slave merchant的未读消息
    # 1.找到私信关联的slave merchant
    target_merchant = Merchant.objects.filter(doctor_id=target_doctor.id).first()
    need_check_not_read_user_list = [target_user_id]
    if target_merchant:
        mastermerchant_relation = MasterMerchant.objects.filter(
            mastermerchant_id=target_merchant.id, is_message_related=True).first()
        if mastermerchant_relation:
            for item in mastermerchant_relation.slavemerchants.all():
                need_check_not_read_user_list.append(item.slavemerchant.doctor.user.id)

    # 2.查询自私信发送出后的未读数量
    # send_time = message.send_time
    status_id_list = ConversationUserStatus.objects.filter(user_id__in=need_check_not_read_user_list,
                                                           read_status=True,
                                                           # last_reply_time__lt=send_time
                                                           )\
        .values_list('id', flat=True)
    conversation_user_status = ConversationUserStatus.objects.prefetch_related(
        'conversation'). \
        filter(id__in=status_id_list).order_by('-last_reply_time')
    conversation_info_list = [cs.conversation.conversation_info_v3(cs, need_check_not_read_user_list) for cs
                              in conversation_user_status]
    conversation_id_to_user_id = {cs.conversation_id: cs.user_id for cs in
                                  conversation_user_status}
    unread_count = 0
    for c in conversation_info_list:
        default = 1 if c['is_new'] else 0
        unread_count += UserUnread(conversation_id_to_user_id[c['id']])\
            .get_conversation_unread(c['id'], default=default)

    # 发送消息，增加记录
    now = datetime.now()
    staff_list = get_receiver_staff(now, merchant_id)
    info_logger.info(
        'voice-alert will send voice alert, message_id: %r, merchand_id: %r, staff_id_list: %r, '
        'unread_count: %r, need_check_not_read_user_list: %r',
        message_id, merchant_id,  map(lambda x: '{}:{}'.format(x.id, x.name), staff_list),
        unread_count, need_check_not_read_user_list)

    for staff in staff_list:
        handle_new_voice_alert(merchant_id, white_list, staff, message, unread_count)


@shared_task
def check_cpt_white_list_status():
    def update_with_calculated_status(white_list, calculated_status):
        if white_list.status != calculated_status:
            white_list.status = calculated_status
            white_list.save()

    cpt_white_list_qs = CPTWhiteList.objects.filter(accredit_type__in=[ACCREDIT_TYPE.CPT,
                                                     ACCREDIT_TYPE.CONVERSATION,
                                                     ACCREDIT_TYPE.BUDAN_BILL,
                                                     ACCREDIT_TYPE.BDTRANSER_DISPATCH])
    for white_list in cpt_white_list_qs:
        calculated_status = white_list.get_status()
        update_with_calculated_status(white_list, calculated_status)


    info_logger.info('check_cpt_white_list_status not voice_alert success')

    cpt_white_list_qs = CPTWhiteList.objects.filter(accredit_type=ACCREDIT_TYPE.MESSAGE_VOICE_ALERT)
    # temp_ing_status_id_list = []
    for white_list in cpt_white_list_qs:
        if not white_list.enable:
            update_with_calculated_status(white_list, ACCREDIT_STATUS.CANCEL)
            continue

        now = datetime.now()
        if now < white_list.start_time:
            update_with_calculated_status(white_list, ACCREDIT_STATUS.PRE)
            continue

        if now >= white_list.end_time:
            update_with_calculated_status(white_list, ACCREDIT_STATUS.DONE)
            continue

        # ACCREDIT_STATUS.ING
        merchant = Merchant.objects.filter(doctor_id=white_list.doctor_id).first()
        if not merchant:
            update_with_calculated_status(white_list, ACCREDIT_STATUS.CANCEL)
            continue

        accredit_status = ACCREDIT_STATUS.ING
        if white_list.accredit_times_type == ACCREDIT_TIMES_TYPE.LIMIT_PER_DAY:
            beginning_of_today = datetime.today().replace(hour=0, minute=0, second=0,
                                                          microsecond=0)
            today_count = VoiceAlertRecord.objects.filter(white_list_id=white_list.id,
                                                          merchant_id=merchant.id,
                                                          alert_time__gt=beginning_of_today).count()
            surplus_times = white_list.accredit_times - today_count
            if surplus_times <= 0 and beginning_of_today.date() >= white_list.end_time.date():
                accredit_status = ACCREDIT_STATUS.DONE
        elif white_list.accredit_times_type == ACCREDIT_TIMES_TYPE.LIMIT_TOTAL:
            start_time = white_list.start_time
            end_time = white_list.end_time

            total_count = VoiceAlertRecord.objects.filter(white_list_id=white_list.id,
                                                          merchant_id=merchant.id,
                                                          alert_time__gt=start_time).count()
            surplus_times = int(white_list.accredit_times) - total_count
            if surplus_times <= 0:
                accredit_status = ACCREDIT_STATUS.DONE

        update_with_calculated_status(white_list, accredit_status)

    info_logger.info('check_cpt_white_list_status voice_alert success')