#-*- coding: utf-8 -*-
import json
import re
from collections import OrderedDict
from datetime import datetime

from django.contrib.auth.models import User
from django.db.models import Q
from gm_types.artemis import ACCREDIT_STATUS, ACCREDIT_TIMES_TYPE, ACCREDIT_TYPE
from gm_types.error import ERROR as CODES
from gm_types.gaia import VERIFY_CODE_TYPE

from api.models import VoiceAlertStaff, VoiceAlertRecord, VoiceAlertSetting, \
    VoiceAlertStaffDutyTable, Message, VoiceAlertConnectStatus
from api.models.cptwhitelist import CPTWhiteList
from api.tool.sms_tool import send_person_code
from api.tool.user_tool import get_user_from_context
from api.tool.voice_alert import get_surplus_times
from hippo.models import Doctor, Merchant
from rpc.cache import code_cache
from rpc.context import get_rpc_remote_invoker
from rpc.decorators import bind_context
from rpc.exceptions import GaiaRPCFaultException
from rpc.tool.error_code import gen
from rpc.tool.log_tool import info_logger


@bind_context("api/voice_alert/send_phone_code", login_required=True)
def send_phone_code(ctx, phone):
    # TODO: 检查当前登录用户的商户权限
    send_person_code(phone, None, VERIFY_CODE_TYPE.BIND, exists_user=False)
    return gen(CODES.SUCCESS)


@bind_context("api/voice_alert/create_staff", login_required=True)
def create_staff(ctx, code, phone, staff_name):
    if not all([code, phone, staff_name]):
        raise GaiaRPCFaultException(
            error=CODES.PARAMS_INVALID,
            message=u'验证码，手机号，员工姓名不能为空',
            data=None,
        )

    # TODO: 检查当前登录用户的商户权限
    user = get_user_from_context(ctx)
    try:
        doctor = Doctor.objects.get(user=user)
        merchant = Merchant.objects.get(doctor=doctor)
    except (Doctor.DoesNotExist, Merchant.DoesNotExist):
        return gen(CODES.NO_PERMISSION)

    # 检查验证码
    key = "phone:%d:%s:%s" % (VERIFY_CODE_TYPE.BIND, None, phone)
    verified_code = code_cache.get(key)
    if verified_code is None or verified_code != code:
        raise GaiaRPCFaultException(
            error=CODES.INVALID_CODE,
            message=u'请填写正确的验证码',
            data=None,
        )

    merchant_id = merchant.id
    try:
        VoiceAlertStaff.objects.get(merchant_id=merchant_id, phone=phone)
    except VoiceAlertStaff.DoesNotExist:
        staff = VoiceAlertStaff.objects.create(merchant_id=merchant_id,
                                       phone=phone,
                                       name=staff_name)
        return {'staff_id': staff.id}
    else:
        # 已经存在
        raise GaiaRPCFaultException(
            error=CODES.PARAMS_INVALID,
            message=u'该手机号已经添加，请勿重复添加',
            data=None,
        )

    return gen(CODES.SUCCESS)


@bind_context("api/voice_alert/delete_staff", login_required=True)
def delete_staff(ctx, staff_id):
    if not staff_id:
        return gen(CODES.PARAMS_INVALID)

    # TODO: 检查当前登录用户的商户权限
    user = get_user_from_context(ctx)
    try:
        doctor = Doctor.objects.get(user=user)
        merchant = Merchant.objects.get(doctor=doctor)
    except (Doctor.DoesNotExist, Merchant.DoesNotExist):
        return gen(CODES.NO_PERMISSION)

    merchant_id = merchant.id
    VoiceAlertStaff.objects.filter(merchant_id=merchant_id, id=staff_id).delete()
    return {}
    # raise GaiaRPCFaultException(
    #     error=CODES.PARAMS_INVALID,
    #     message=u'该商户下未找到指定的员工',
    #     data=None,
    # )


@bind_context("api/voice_alert/staff_list", login_required=True)
def staff_list(ctx, page=1, size=10):
    # TODO: 检查当前登录用户的商户权限
    user = get_user_from_context(ctx)
    try:
        doctor = Doctor.objects.get(user=user)
        merchant = Merchant.objects.get(doctor=doctor)
    except (Doctor.DoesNotExist, Merchant.DoesNotExist):
        return gen(CODES.NO_PERMISSION)

    merchant_id = merchant.id
    qs = VoiceAlertStaff.objects.filter(merchant_id=merchant_id).order_by('-id')
    total = qs.count()
    return {
        'page': page,
        'size': size,
        'total': total,
        'staff_list': [{
            'staff_name': staff.name,
            'phone': staff.phone,
            'id': staff.id
        } for staff in qs[(page-1)*size: page*size]]
    }


@bind_context("api/voice_alert/accredit_status", login_required=True)
def accredit_status(ctx):
    user = get_user_from_context(ctx)
    try:
        doctor = Doctor.objects.get(user=user)
        merchant = Merchant.objects.get(doctor=doctor)
    except (Doctor.DoesNotExist, Merchant.DoesNotExist):
        return gen(CODES.NO_PERMISSION)

    now = datetime.now()
    cpt_white_list = CPTWhiteList.objects.filter(doctor_id=doctor.id,
                                                 accredit_type=ACCREDIT_TYPE.MESSAGE_VOICE_ALERT)\
        .order_by('-id').first()

    if not cpt_white_list:
        return {
            'accredit_status': ACCREDIT_STATUS.PRE,
            'accredit_times_type': None,
            'accredit_start_time': None,
            'accredit_end_time': None,
            'accredit_times': None
        }

    surplus_times = None
    # accredit_status = cpt_white_list.status
    accredit_status = cpt_white_list.get_status()

    if accredit_status == ACCREDIT_STATUS.ING: # 生效中
        if cpt_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=cpt_white_list.id,
                                                          merchant_id=merchant.id,
                                                          alert_time__gt=beginning_of_today).count()
            surplus_times = cpt_white_list.accredit_times - today_count
            if surplus_times <= 0 and beginning_of_today.date() >= cpt_white_list.end_time.date():
                accredit_status = ACCREDIT_STATUS.DONE
        elif cpt_white_list.accredit_times_type == ACCREDIT_TIMES_TYPE.LIMIT_TOTAL:
            start_time = cpt_white_list.start_time
            end_time = cpt_white_list.end_time

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

    return {
        'accredit_status': accredit_status,
        'accredit_times_type': cpt_white_list.accredit_times_type,
        'accredit_start_time': cpt_white_list.start_time.strftime('%Y-%m-%d %H:%M'),
        'accredit_end_time': cpt_white_list.end_time.strftime('%Y-%m-%d %H:%M'),
        'accredit_times': cpt_white_list.accredit_times,
        'surplus_times': surplus_times
    }


def format_time_delta(time_delta):
    hour_minute, second = divmod(int(time_delta), 60)
    hour, minute = divmod(hour_minute, 60)
    return '{:02d}:{:02d}:{:02d}'.format(hour, minute, second)


def transform_time_str_to_time_delta(time_str):
    time_str = time_str.split(':')
    if not time_str or len(time_str) != 3:
        return None
    hour, minute, second = time_str
    return int(hour) * 3600 + int(minute) * 60 + int(second)


@bind_context("api/voice_alert/get_settings", login_required=True)
def voice_alert_get_settings(ctx):
    user = get_user_from_context(ctx)
    try:
        doctor = Doctor.objects.get(user=user)
        merchant = Merchant.objects.get(doctor=doctor)
    except (Doctor.DoesNotExist, Merchant.DoesNotExist):
        return gen(CODES.NO_PERMISSION)

    setting_obj = VoiceAlertSetting.objects.filter(merchant_id=merchant.id).first()
    if not setting_obj:
        setting_obj = VoiceAlertSetting.objects.create(merchant_id=merchant.id,
                                                        alert_interval=0)
        return {
            'setting_id': setting_obj.id,
            'alert_interval': 0,
            'duty_table': [None]*7
        }

    alert_interval = setting_obj.alert_interval // 60
    duty_table = VoiceAlertStaffDutyTable.objects.prefetch_related('staff')\
        .filter(setting=setting_obj)\
        .order_by('weekday', 'start_time')\
        .values('id', 'weekday', 'start_time', 'end_time', 'staff_id', 'staff__name', 'staff__phone')
    duty_dict = OrderedDict([(i, None)for i in range(7)])
    for duty in duty_table:
        weekday = duty.get('weekday')
        if duty_dict[weekday] is None:
            duty_dict[weekday] = []
        duty['staff_name'] = duty.pop('staff__name')
        duty['staff_phone'] = duty.pop('staff__phone')
        duty['start_time'] = format_time_delta(duty.pop('start_time'))
        duty['end_time'] = format_time_delta(duty.pop('end_time'))
        duty_dict[weekday].append(duty)

    return {
        'setting_id': setting_obj.id,
        'alert_interval': alert_interval,
        'duty_table': list(duty_dict.values())
    }


@bind_context("api/voice_alert/update_settings", login_required=True)
def voice_alert_update_settings(ctx, settings_id, alert_interval, duty_table):
    user = get_user_from_context(ctx)
    try:
        doctor = Doctor.objects.get(user=user)
        merchant = Merchant.objects.get(doctor=doctor)
    except (Doctor.DoesNotExist, Merchant.DoesNotExist):
        return gen(CODES.NO_PERMISSION)

    if not alert_interval:
        raise GaiaRPCFaultException(
            error=CODES.PARAMS_INVALID,
            message=u'缺少alert_interval参数',
            data=None,
        )

    alert_interval = int(alert_interval) * 60
    record_count = VoiceAlertSetting.objects.filter(merchant_id=merchant.id,
                                                   id=settings_id)\
        .update(alert_interval=alert_interval)
    update_setting = bool(record_count)

    # 更新duty_table
    if not duty_table:
        return {
            'update_setting': update_setting,
            'update_duty_table': False
        }

    # 检查duty_table数据
    for weekday, weekday_list in enumerate(duty_table):
        if not weekday_list:
            continue
        for item in weekday_list:
            if item.get('id') and not any([item.get('start_time'),
                                           item.get('end_time'),
                                           item.get('staff_id')]):
                continue
            try:
                item['start_time'] = transform_time_str_to_time_delta(item.get('start_time'))
                item['end_time'] = transform_time_str_to_time_delta(item.get('end_time'))
                item['staff_id'] = item.get('staff_id')
            except Exception as e:
                return gen(CODES.PARAMS_INVALID)

    for weekday, weekday_list in enumerate(duty_table):
        if not weekday_list:
            continue
        for item in weekday_list:
            item_id = item.get('id')
            if item_id:
                if not any([item.get('start_time'),
                            item.get('end_time'),
                            item.get('staff_id')]):
                    VoiceAlertStaffDutyTable.objects.filter(setting=settings_id,
                                                            id=item_id).delete()
                    continue
                VoiceAlertStaffDutyTable.objects.filter(setting_id=settings_id,
                                                        weekday=weekday,
                                                        id=item_id)\
                    .update(start_time=item.get('start_time'),
                            end_time=item.get('end_time'),
                            staff_id=item.get('staff_id'))
            else:
                VoiceAlertStaffDutyTable.objects.create(setting_id=settings_id,
                                                        weekday=weekday,
                                                        start_time=item.get('start_time'),
                                                        end_time=item.get('end_time'),
                                                        staff_id=item.get('staff_id'))
    return {
        'update_setting': update_setting,
        'update_duty_table': True
    }


@bind_context("api/voice_alert/duty_item/update_or_create", login_required=True)
def voice_alert_duty_item_update_or_create(ctx, settings_id, start_time, end_time,
                                           weekday, staff_id, is_create=False, item_id=None):
    if not is_create and not item_id:
        raise GaiaRPCFaultException(
            error=CODES.PARAMS_INVALID,
            message='item_id不能为空',
            data=None,
        )

    user = get_user_from_context(ctx)
    try:
        doctor = Doctor.objects.get(user=user)
        merchant = Merchant.objects.get(doctor=doctor)
    except (Doctor.DoesNotExist, Merchant.DoesNotExist):
        return gen(CODES.NO_PERMISSION)

    setting_obj = VoiceAlertSetting.objects.filter(merchant_id=merchant.id,
                                                   id=settings_id).first()
    if not setting_obj:
        return gen(CODES.PARAMS_INVALID)

    start_time = transform_time_str_to_time_delta(start_time)
    end_time = transform_time_str_to_time_delta(end_time)

    if not start_time or not end_time:
        return gen(CODES.PARAMS_INVALID)

    staff = VoiceAlertStaff.objects.filter(id=staff_id).first()
    if not staff:
        return gen(CODES.PARAMS_INVALID)

    if is_create:
        duty_item = VoiceAlertStaffDutyTable.objects.create(setting_id=setting_obj.id,
                                                    weekday=weekday,
                                                    start_time=start_time,
                                                    end_time=end_time,
                                                    staff=staff)
        return {
            'id': duty_item.id
        }
    else:
        record_count = VoiceAlertStaffDutyTable.objects.filter(setting_id=setting_obj.id,
                                                id=item_id)\
            .update(weekday=weekday,
                    start_time=start_time,
                    end_time=end_time,
                    staff=staff)
        if record_count:
            return {
                'id': item_id
            }
        else:
            return gen(CODES.PARAMS_INVALID)


@bind_context("api/voice_alert/duty_item/delete", login_required=True)
def voice_alert_duty_item_delete(ctx, settings_id, item_id):
    user = get_user_from_context(ctx)
    try:
        doctor = Doctor.objects.get(user=user)
        merchant = Merchant.objects.get(doctor=doctor)
    except (Doctor.DoesNotExist, Merchant.DoesNotExist):
        return gen(CODES.NO_PERMISSION)

    setting_obj = VoiceAlertSetting.objects.filter(merchant_id=merchant.id,
                                                   id=settings_id).first()
    if not setting_obj:
        raise GaiaRPCFaultException(
            error=CODES.PARAMS_INVALID,
            message='找不到指定的setting',
            data=None,
        )

    VoiceAlertStaffDutyTable.objects.filter(setting=setting_obj,
                                            id=item_id).delete()
    return {}



@bind_context("api/voice_alert/record_list", login_required=True)
def voice_alert_record_list(ctx, page=1, size=10, start_time=None, end_time=None):
    user = get_user_from_context(ctx)
    try:
        doctor = Doctor.objects.get(user=user)
        merchant = Merchant.objects.get(doctor=doctor)
    except (Doctor.DoesNotExist, Merchant.DoesNotExist):
        return gen(CODES.NO_PERMISSION)

    query_conditions = Q()
    if start_time:
        try:
            start_time = datetime.fromtimestamp(start_time)
        except:
            raise GaiaRPCFaultException(
                error=CODES.PARAMS_INVALID,
                message='时间格式不正确',
                data=None,
            )
        query_conditions &= Q(alert_time__gte=start_time)

    if end_time:
        try:
            end_time = datetime.fromtimestamp(end_time)
        except:
            raise GaiaRPCFaultException(
                error=CODES.PARAMS_INVALID,
                message='时间格式不正确',
                data=None,
            )
        query_conditions &= Q(alert_time__lte=end_time)

    if start_time and end_time and start_time >= end_time:
        raise GaiaRPCFaultException(
            error=CODES.PARAMS_INVALID,
            message='开始时间不能大于结束时间',
            data=None,
        )

    if size >= 50:
        size = 50

    query_conditions &= Q(merchant_id=merchant.id)
    record_qs = VoiceAlertRecord.objects.prefetch_related('receive_staff').filter(query_conditions).order_by('-id')
    total = record_qs.count()
    record_qs = record_qs[(page-1)*size:page*size]\
        .values('message_receiver_user_id', 'message_id', 'alert_time', 'receive_staff__name',
                'receive_staff__phone', 'accept_time', 'end_calltime', 'start_calltime')

    result = []
    message_id_set = set()
    message_user_id_set = set()
    for record in record_qs:
        start_calltime = record.pop('start_calltime')
        accept_time = record.pop('accept_time')
        end_calltime = record.pop('end_calltime')

        connect_status = VoiceAlertRecord.get_connect_status(accept_time)
        if connect_status == VoiceAlertConnectStatus.NOT_CONNECTED:
            call_duration = VoiceAlertRecord.get_call_duration(start_calltime, end_calltime)
        else:
            call_duration = VoiceAlertRecord.get_call_duration(start_calltime, accept_time)

        record['connect_status'] = VoiceAlertConnectStatus.getDesc(connect_status)
        record['call_duration'] = '{}s'.format(call_duration)
        record['connect_duration'] = '{}s'.format(VoiceAlertRecord.get_connect_duration(accept_time, end_calltime))
        record['receive_staff_name'] = record.pop('receive_staff__name')
        record['receive_phone'] = record.pop('receive_staff__phone')
        record['alert_time'] = record['alert_time'].strftime('%Y-%m-%d %H:%M')
        record['user_key'] = ""
        message_id_set.add(record['message_id'])
        message_user_id_set.add(record['message_receiver_user_id'])
        result.append(record)

    message_qs = Message.objects.prefetch_related('conversation')\
        .filter(id__in=list(message_id_set))

    message_dict = {}
    for message in message_qs:
        message_dict[message.id] = message
        message_user_id_set.add(message.user_id)

    user_qs = User.objects.filter(id__in=list(message_user_id_set))
    user_dict = {user.id:user for user in user_qs}

    for record in result:
        message = message_dict.get(record['message_id'])
        record['message_content'] = message.content
        record['user_key'] = '_'.join(map(str, message.conversation.user_ids()))
        record['user_name'] = user_dict.get(message.user_id).last_name
        record['doctor_user_id'] = user.id

    return {
        'total': total,
        'record_list': result,
        'page': page,
        'size': size
    }


def get_datetime_from_ts_str(timestamp_str):
    if not timestamp_str:
        return None
    return datetime.fromtimestamp(float(timestamp_str))


@bind_context("api/voice_alert/vms_callback")
def voice_alert_vms_callback(ctx, callback_event, value):
    info_logger.info(
        'voice_alert_vms_callback, callback_event: %r, value: %r',
        callback_event, json.dumps(value))
    # 语音验证码状态通知
    if callback_event == 'voicecode_callback':
        pass

    # 语音通知状态通知
    elif callback_event == 'voiceprompt_callback':
        callid = value.get('callid')
        accept_time = value.get('accept_time') or None
        if accept_time:
            if str(accept_time) == "0": # 未接通
                accept_time = None
            else:
                accept_time = get_datetime_from_ts_str(accept_time)

        if callid:
            VoiceAlertRecord.objects.filter(vms_callid=callid).update(
                result=int(value.get('result')),
                mobile=value.get('mobile'),
                call_from=value.get('call_from'),
                fee=value.get('fee'),
                start_calltime=get_datetime_from_ts_str(value.get('start_calltime')),
                accept_time=accept_time,
                end_calltime=get_datetime_from_ts_str(value.get('end_calltime'))
            )


    # 语音通知按键通知
    elif callback_event == 'voicekey_callback':
        pass

    # 语音送达失败原因通知
    elif callback_event == 'voice_failure_callback':
        callid = value.get('callid')
        if callid:
            VoiceAlertRecord.objects.filter(vms_callid=callid).update(
                call_from=value.get('call_from'),
                failure_code=value.get('failure_code'),
                mobile=value.get('mobile'),
            )
