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

import datetime
import jpush
import pytz
from abc import ABCMeta, abstractmethod
from django.conf import settings
from gm_types.gaia import PUSH_TYPE
from gm_types.gaia import TAG_TYPE, DOCTOR_TYPE
from gm_types.push import PUSH_INFO_TYPE, AUTOMATED_PUSH, PUSH_URGENCY, J_TYPE
from gm_upload.utils.image_utils import Picture
from helios import RPCFaultException, RPCSystemException

from api.models import Tag, Message, get_user_by_id
from api.tool.image_utils import get_full_path, get_thumb_path
from api.tool.log_tool import logging_exception
from api.tool.user_tool import filter_user_nick_name
from rpc.context import get_rpc_remote_invoker
from rpc.tool.protocol import gmdoctor_protocol, gm_protocol
from services.unread.stat import UserUnread

rpc_invoker = get_rpc_remote_invoker()

def generate_extra(push_type=0, user_ids=()):
    if push_type == PUSH_TYPE.MY_CONVERSATION:
        extra = {
            'type': 2,
            'msgType': 5,
            'pushUrl': 'gengmei://message_list',
            'is_activity_push': False,
        }
    elif push_type == PUSH_TYPE.DOCTOR_PRI_MSG_LIST:
        extra = {
            'type': 2,
            'msgType': 5,
            'pushUrl': 'gmdoctor://message_list',
            'is_activity_push': False,
        }

    if user_ids:
        extra['user_key'] = u'_'.join([unicode(u) for u in sorted(user_ids)])

    return extra


def build_user_push_extra(msg_id, conversation_type, user_ids):
    """生成私信推送
    """
    extra = {
        'type':PUSH_INFO_TYPE.MESSAGE,
        'msg':{
            'msg_id':msg_id,
            'conversation_type':conversation_type,
            'user_key': u'_'.join([unicode(uid) for uid in sorted(user_ids)])
        },
        'user_key': u'_'.join([unicode(uid) for uid in sorted(user_ids)]),  # compatibility for version before 6.0
        'pushUrl': 'gengmei://message_list',  # compatibility for version before 6.0
    }
    return extra


def build_conversation_push_extra(conversation_id, msg_id, conversation_type, user_ids, send_user, text, last_reply_time):
    """这块以后不要再使用了。 也不要维护了。
    """
    if isinstance(last_reply_time, datetime.datetime):
        last_reply_time = last_reply_time.strftime('%Y-%m-%d  %H:%M:%S')

    doctor = getattr(send_user, 'doctor', None)
    if doctor:
        if doctor.doctor_type == DOCTOR_TYPE.DOCTOR:
            portrait = get_full_path(doctor.portrait or settings.DOCTOR_DEFAULT_PORTRAIT_KEY)
            nickname = "{} 医生".format(doctor.name)
        else:
            portrait = get_full_path(doctor.portrait or settings.HOSPITAL_DEFAULT_PORTRAIT_KEY)
            nickname = doctor.name
    else:
        portrait = get_full_path(send_user.userextra.portrait) or get_full_path(u'img%2Fuser_portrait.png')
        nickname = filter_user_nick_name(send_user)
    portrait = Picture.get_thumb_path(portrait)
    extra = {
        'type': PUSH_INFO_TYPE.MESSAGE,
        'msg': {
            'msg_id': msg_id,
            'conversation_type': conversation_type,
            'user_key': u'_'.join([unicode(uid) for uid in sorted(user_ids)]),
        },
        # 将消息内容推送给客户端新字段
        'message': {
            'id': conversation_id,
            'msg_id': msg_id,
            'conversation_type': conversation_type,
            'user_key': u'_'.join([unicode(uid) for uid in sorted(user_ids)]),
            'text': text,
            'portrait': portrait,
            'nickname': nickname,
            'last_reply_time': last_reply_time,
            'is_new': True,
            'is_deleted': False
        },
        'user_key': u'_'.join([unicode(uid) for uid in sorted(user_ids)]),  # compatibility for version before 6.0
        'pushUrl': 'gengmei://message_list',  # compatibility for version before 6.0
    }
    return extra


def _eta_to_duration(eta):
    """
    定时的日期转换成本地时间
    """
    eta = datetime.datetime.strptime(eta, '%Y-%m-%d %H:%M:%S')
    local_timezone = pytz.timezone(settings.TIME_ZONE)
    local_eta = local_timezone.localize(eta)
    now = datetime.datetime.now(local_timezone)
    duration = (local_eta - now).total_seconds()
    return duration


def tzlc(dt, truncate_to_sec=True):
    if dt is None:
        return None
    if truncate_to_sec:
        dt = dt.replace(microsecond=0)
    return pytz.timezone(settings.TIME_ZONE).localize(dt)


def eta_2_push_time(eta):
    if eta:
        eta = datetime.datetime.strptime(eta, '%Y-%m-%d %H:%M:%S')
        eta = tzlc(eta)
        return int((eta-datetime.datetime.fromtimestamp(0,pytz.timezone("UTC"))).total_seconds())
    else:
        push_time = None
    return push_time


def push_message_user_multi(platform=None, user_ids=None, extra=None, alert='', msg_content='', eta=None,
                            push_stat_labels=None, silent=False):
    """
    把推送任务发送到Celery中
    :param jpush_fmt: jpush使用notification还是message
    :param platform: 推送的平台
    :param extra: 推送的附加消息
    :param alert: 推送消息文本
    :param msg_content: message推送的内容
    :param eta: 定时执行
    :return:
    """
    rpc_invoker = get_rpc_remote_invoker()
    try:
        rpc_invoker['push2/push/user/notification/uids'](
            user_ids=user_ids, platform=platform,
            alert=alert,
            extra=extra,
            push_time=eta_2_push_time(eta),
            labels=push_stat_labels
        ).unwrap()
    except Exception as e:
        logging_exception()


_jpush = jpush.JPush(settings.USER_JPUSH_APP_KEY, settings.USER_JPUSH_MASTER_SECRET)


def jpush_city_binding(registration_id, city_tag_id):
    def build_jpush_tag(tag_id):
        return '_'+str(tag_id)

    def parse_jpush_tag(jpush_tag):
        if jpush_tag.startswith('_'):
            return int(jpush_tag[1:])
        else:
            return None

    device = _jpush.create_device()
    device_info = device.get_deviceinfo(registration_id=registration_id).payload

    if 'tags' in device_info and device_info['tags']:
        tag_ids = filter(None, [parse_jpush_tag(t) for t in device_info['tags']])
        old_city_jpush_tags = [build_jpush_tag(tag.id) for tag in
                               Tag.objects.filter(pk__in=tag_ids, tag_type=TAG_TYPE.CITY)]
    else:
        old_city_jpush_tags = []

    entity = jpush.device_tag(jpush.add(build_jpush_tag(city_tag_id)),
                              jpush.remove(*old_city_jpush_tags))
    device.set_deviceinfo(registration_id=registration_id,
                          entity=entity)


class PushService:
    __metaclass__ = ABCMeta
    rpc_endpoint = ''

    @classmethod
    def _gen_user_related_info(cls, msg):
        send_user = get_user_by_id(msg.user_id)
        doctor = getattr(send_user, 'doctor', None)
        if doctor:
            if doctor.doctor_type == DOCTOR_TYPE.DOCTOR:
                portrait = get_thumb_path(doctor.portrait or settings.DOCTOR_DEFAULT_PORTRAIT_KEY)
                nickname = "{} 医生".format(doctor.name)
            else:
                portrait = get_thumb_path(doctor.portrait or settings.HOSPITAL_DEFAULT_PORTRAIT_KEY)
                nickname = doctor.name
        else:
            portrait = get_thumb_path(send_user.userextra.portrait) or settings.DEFAULT_PORTRAIT
            nickname = filter_user_nick_name(send_user)
        return {
            'portrait': portrait,
            'nickname': nickname,
            'user': send_user,
        }

    @classmethod
    @abstractmethod
    def _build_new_msg_extra(cls, msg):
        pass

    @classmethod
    def _build_push_message_extra(cls, data, push_info_type=PUSH_INFO_TYPE.GM_PROTOCOL, push_url="",
                                  activate_notification=False):
        """自定义消息做到医生端和用户端统一格式"""
        return {
            "type": push_info_type,
            "data": data,
            "push_url": push_url,
            "activate_notification": activate_notification,
        }

    @classmethod
    def _build_push_notification_extra(cls, data, push_info_type=PUSH_INFO_TYPE.GM_PROTOCOL, push_url=""):
        return {
            'type': push_info_type,
            'data': data,
            'pushUrl': push_url,
        }

    @classmethod
    def send_msg_read_message_push(cls, user_ids, data):
        extra = cls._build_push_message_extra(data=data, push_info_type=PUSH_INFO_TYPE.MESSAGE_READ)
        cls._send(user_ids, alert='', extra=extra, platform=None, push_time=None, silent=None,
                  j_type=J_TYPE.MESSAGE, push_type=AUTOMATED_PUSH.MESSAGE_HAS_BEEN_READ)

    @classmethod
    @abstractmethod
    def send_new_msg_push(cls, user_ids, alert, data, platform=None, push_time=None, silent=None):
        pass

    @classmethod
    @abstractmethod
    def _send(cls, user_ids, alert, extra, platform, push_time, silent, j_type=None, **kwargs):
        pass

    @classmethod
    @abstractmethod
    def _build_new_msg_notification_extra(cls, data):
        pass

    @classmethod
    @abstractmethod
    def _build_new_msg_message_extra(cls, data):
        pass

class DoctorPushService(PushService):
    api_endpoint = 'push2/push/doctor/notification/uids'

    @classmethod
    def _build_new_msg_extra(cls, data):
        user_ids = data.conversation.user_ids()
        user_key = '_'.join(map(str, user_ids))
        return {
            'type': PUSH_INFO_TYPE.MESSAGE,
            'pushUrl': gmdoctor_protocol.get_message_chat(user_key),
            'is_activity_push': False,
            'user_key': user_key,
        }

    @classmethod
    def send_new_msg_push(cls, user_ids, alert, data, platform=None, push_time=None, silent=None):
        if not isinstance(data, Message):
            raise ValueError('Parameter data must be the Message object')
        extra = cls._build_new_msg_extra(data)
        cls._send(user_ids, alert, extra, platform, push_time, silent)

    @classmethod
    def _send(cls, user_ids, alert, extra, platform, push_time, silent, j_type=None, **kwargs):
        try:
            rpc_invoker[cls.api_endpoint](
                user_ids=user_ids, platform=platform, extra=extra, alert=alert,
                push_time=push_time, silent=silent, urgency=PUSH_URGENCY.URGENT
            ).unwrap()
        except (RPCFaultException, RPCSystemException):
            logging_exception()

class UserPushService(PushService):

    api_endpoint = 'push2/push/user/automated_push/uids'

    @classmethod
    def _build_new_msg_extra(cls, msg):
        # 这块主要是兼容旧版客户端
        user_related_info = cls._gen_user_related_info(msg)
        user_ids = msg.conversation.user_ids()
        user_key = '_'.join(map(str, user_ids))
        target_user_id = msg.conversation.user_status_set.exclude(user_id=msg.user_id).first().user_id
        return {
            'type': PUSH_INFO_TYPE.MESSAGE,
            'msg': {
                'msg_id': msg.id,
                'conversation_type': msg.conversation.conversation_type,
                'user_key': user_key,
            }, # 兼容7720 版本之前的数据
            # 将消息内容推送给客户端新字段
            'message': {
                'id': msg.conversation_id,
                'msg_id': msg.id,
                'conversation_type': msg.conversation.conversation_type,
                'user_key': user_key,
                'text': Message.content_display(type=msg.type, content=msg.content, user=user_related_info['user']),
                'portrait': user_related_info['portrait'],
                'nickname': user_related_info['nickname'],
                'last_reply_time': msg.send_time.strftime('%Y-%m-%d  %H:%M:%S'),
                'is_new': True,
                'is_deleted': False
            },  # 兼容 7755版本之前的数据
            'data': {
                'conversation_id': msg.conversation_id,
                'conversation_type': msg.conversation.conversation_type,
                'user_key': user_key,
                'message_id': msg.id,
                'text': Message.content_display(type=msg.type, content=msg.content, user=user_related_info['user']),
                'portrait': user_related_info['portrait'],
                'nickname': user_related_info['nickname'],
                'last_reply_time': msg.send_time.strftime('%Y-%m-%d  %H:%M:%S'),
                'is_new': True,
                'is_deleted': False,
                # push在更新未读数之前，所以在取出未读数时需要+1校准数据
                'unread_num': UserUnread(target_user_id).get_conversation_unread(msg.conversation_id, default=0) + 1,
            },  # 7750添加  给自定义消息使用
            'user_key': user_key,
            'pushUrl': gm_protocol.message_chat(user_key),  # 历史版本
            "push_url": gm_protocol.message_chat(user_key),
            "activate_notification": True,
        }

    @classmethod
    def send_new_msg_push(cls, user_ids, alert, data, platform=None, push_time=None, silent=None):
        if not isinstance(data, Message):
            raise ValueError('Parameter data must be the Message object')
        extra = cls._build_new_msg_extra(data)
        cls._send(user_ids, alert, extra, platform, push_time, silent,
                  j_type=J_TYPE.NOTIFICATION_AND_MESSAGE, push_type=AUTOMATED_PUSH.RECEIVED_A_PRIVATE_MESSAGE)

    @classmethod
    def _send(cls, user_ids, alert, extra, platform, push_time, silent, j_type=None, **kwargs):
        push_type = kwargs['push_type']
        try:
            rpc_invoker[cls.api_endpoint](
                user_ids=user_ids, push_type=push_type, platform=platform,
                alert=alert, extra=extra,
                silent=silent,
                push_time=push_time, urgency=PUSH_URGENCY.URGENT,
                j_type=j_type
            ).unwrap()
        except (RPCFaultException, RPCSystemException):
            logging_exception()
