# coding=utf-8'
import time
import json
from datetime import datetime
from celery import shared_task
from django.conf import settings
from gm_jmessage import common
from dingtalkchatbot.chatbot import DingtalkChatbot
from api.models.message import SPECIAL_MSG_TYPE
from gm_types.gaia import MESSAGE_TYPE
from gm_types.msg import CONVERSATION_TYPE, MessageTraceEventType
from gm_upload.utils.image_utils import Picture
from urlparse import urljoin
from wechat_sdk.wx import WechatPubAPI

from api.models import User, Message, Conversation, get_doctor_by_user_id, ConversationUserStatus
from hippo.tool.chain_hospital_tools import get_master_merchant
from hippo.utils import get_merchant
from message import JIM_USER_NOT_FOUND
from message.utils.common import get_jim_login_info, register_jim
from message.utils.push_service import UserPushService, DoctorPushService
from rpc.cache import wechatpub_cache
from rpc.tool.log_tool import info_logger, conversation_logger
from rpc.tool.log_tool import log_message_trace, trace_context
from services.unread.noti_unread import noti_poll
from rpc.cache import conversation_cache


@shared_task
def send_wechatpub_message(target_user_id, msg_type, content):
    wechat_api = WechatPubAPI(settings.WECHATPUB_APPID, settings.WECHATPUB_APPSECRET)

    at = wechatpub_cache.get('access_token')
    if not at:
        # get wx access token
        at_json = wechat_api.get_access_token()
        at = at_json['access_token']
        expires_in = at_json['expires_in'] - 120
        wechatpub_cache.setex('access_token', expires_in, at)

    user = User.objects.get(id=target_user_id)
    wechat_openid = user.username[6:]  # get openid

    if msg_type == MESSAGE_TYPE.TEXT:
        error_code = wechat_api.send_kefu_text_msg(at, wechat_openid, content['text'])
        info_logger.info(
            u'send to wechatpub: status: %s, openid: %s, content: %s',
            error_code, wechat_openid, content['text']
        )

    elif msg_type == MESSAGE_TYPE.SERVICE:
        service_id = content['service_id']
        link = urljoin(settings.M_HOST, '/promotion/%s/' % service_id)
        error_code = wechat_api.send_kefu_text_msg(at, wechat_openid, u'美购: %s' % link)
        info_logger.info(
            u'send to wechatpub: status: %s, openid: %s, content: %s',
            error_code, wechat_openid, link
        )

    elif msg_type == MESSAGE_TYPE.IMAGE:
        link = content['image']
        link = Picture.get_full_path(link, '-w')
        error_code = wechat_api.send_kefu_text_msg(at, wechat_openid, u'图片: %s' % link)
        info_logger.info(
            u'send to wechatpub: status: %s, openid: %s, content: %s',
            error_code, wechat_openid, link
        )

    elif msg_type == MESSAGE_TYPE.DIARY:
        link = urljoin(settings.M_HOST, '/diary_book/%s/' % content['diary_id'])
        error_code = wechat_api.send_kefu_text_msg(at, wechat_openid, u'案例: %s' % link)
        info_logger.info(
            u'send to wechatpub: status: %s, openid: %s, content: %s',
            error_code, wechat_openid, link
        )


@shared_task
def send_jim_message_v1(trace_id, receiver_uid, extras, msg_type='text', text=''):
    """ JIM 推送集成链路日志trace 版本 """
    with trace_context(trace_id):
        task_params = {
            "trace_id": trace_id,
            "receiver_uid": receiver_uid,
            "extras": extras,
            "msg_type": msg_type,
            "text": text
        }
        log_message_trace(MessageTraceEventType.TRACE_GAIA_MESSAGE_JIM_TASK_RECIVED, task_params)
        send_jim_message(receiver_uid, extras, msg_type, text)


@shared_task
def send_jim_message(receiver_uid, extras, msg_type='text', text=''):
    """Send message to jim server.
    reference doc:
        https://docs.jiguang.cn/jmessage/server/rest_api_im/#_17
    """
    from rpc.tool.log_tool import info_logger
    info_logger.info({"send_jim_message": True, "receiver_uid": receiver_uid})
    # currenttly, msg_type just supports text
    assert msg_type in ('text',)
    username, password = get_jim_login_info(receiver_uid)
    jmessage = common.JMessage(settings.JIM_APPKEY, settings.JIM_SECRET)
    jmsg_obj = jmessage.create_messages()

    msg = jmsg_obj.build_message(1, 'single', 'admin', msg_type,
                                 username, settings.JIM_ADMIN, text, extras=extras)

    msg['no_notification'] = True
    # 如果序列化不成功， 大部分情况是极光服务器出了问题
    body = jmsg_obj.send_messages(msg).json()

    # handle with user not exist case.
    if 'error' in body and body['error']['code'] == JIM_USER_NOT_FOUND:
        register_jim(username, password)
        # try send message again, we retry only once.
        body = jmsg_obj.send_messages(msg).json()
    log_message_trace(
        MessageTraceEventType.TRACE_GAIA_MESSAGE_JIM_TO_USER,
        {"body": body, "username": username, "password": password}
    )
    return body


@shared_task
def change_message_is_read_status_and_notify(msg_ids, read_user_id):
    """修改对应message的已读状态， 并且通知对应用户
    对于医生web版 通过poll服务推送消息
    对于用户版 通过JPush Message进行推送消息
    其余的暂时不支持
    :param msg_ids List: 已读消息ID
    :param read_user_id:
    :return:
    """
    # 每次的msg_id 要属于同一个conversation_id, 并且发送人不是自己
    messages = Message.objects.filter(id__in=msg_ids)
    conversation_ids = list(set([item.conversation_id for item in messages]))
    user_ids = [item.user_id for item in messages]
    if len(conversation_ids) != 1:
        conversation_logger.info('MessageRead: msg_ids: {} not the same conversation'.format(msg_ids))
        return
    if read_user_id in user_ids:
        conversation_logger.info('MessageRead: read_user_id: {} is the sender of the message, message_send_user_ids: {}'.
                                 format(read_user_id, user_ids))
        return

    conversation_id = conversation_ids[0]
    conversation_object = Conversation.objects.filter(id=conversation_id).first()
    if not conversation_object:
        conversation_logger.info('MessageRead: conversation_id: {} not exist'.format(conversation_id))
        return

    # 如果不是自己的对话 也不能设置
    conversation__user_ids = conversation_object.user_ids()
    if read_user_id not in conversation__user_ids:
        conversation_logger.info('MessageRead: read_user_id: {} does not belong in this conversation_id: {}'.
                                 format(read_user_id, conversation_id))
        return

    # 更新部分
    need_update_msg_ids = [item.id for item in messages if item.is_read is False]
    if not need_update_msg_ids:
        conversation_logger.info('MessageRead: No read messages need to be updated')
        return
    update_rows = Message.objects.filter(id__in=need_update_msg_ids).update(is_read=True)
    assert update_rows > 0

    # 发送通知部分
    target_user_id = list(set(conversation__user_ids) - set([read_user_id]))[0]
    doctor = get_doctor_by_user_id(target_user_id)

    user_key = '_'.join(map(str, sorted(conversation__user_ids)))
    push_data = {'msg_ids': msg_ids, 'user_key': user_key}

    if doctor:
        noti_poll(doctor.id, 'message', 'read', push_data)

    from message.utils.push_service import UserPushService
    UserPushService.send_msg_read_message_push(user_ids=[target_user_id], data=push_data)
    conversation_logger.info('MessageRead: msg_ids: {} update read status successful'.format(need_update_msg_ids))

@shared_task
def update_conversation_refer(user_id, conversation_id, from_user_refer=None, business_id=None):
    """

    :param from_user_refer:
    :param business_id:
    :return:
    """
    # update refer
    # TODO: 如果来源是美购详情页的话， 要判断当前business_id 是否属于该医生， 不然就会错乱, 没时间改， 留给后人了
    cus = ConversationUserStatus.objects.filter(conversation_id=conversation_id, user_id=user_id).first()
    if not cus:
        return
    if cus.refer != from_user_refer or cus.business_id != business_id:
        cus.refer = from_user_refer
        cus.business_id = business_id
        cus.save(update_fields=['refer', 'business_id'])

@shared_task
def send_new_message_push_v1(trace_id, target_user_id, msg_id, alert, silent):
    """ 集成日志链路追踪 """
    with trace_context(trace_id):
        task_params = {
            "trace_id": trace_id,
            "target_user_id": target_user_id,
            "msg_id": msg_id,
            "alert": alert,
            "silent": silent
        }
        log_message_trace(MessageTraceEventType.TRACE_GAIA_MESSAGE_JPUSH_TASK_RECIVED, task_params)
        send_new_message_push(target_user_id, msg_id, alert, silent)


@shared_task
def send_new_message_push(target_user_id, msg_id, alert, silent):
    # 如果message_id 不存在 就报错， 或者打点记录
    message = Message.objects.get(id=msg_id)
    UserPushService.send_new_msg_push(user_ids=[target_user_id], alert=alert, data=message, silent=silent)
    log_message_trace(MessageTraceEventType.TRACE_GAIA_MESSAGE_JPUSH_TO_USER, {"user_ids": [target_user_id]})
    # 医生推送 Jpush 医生版暂时还没有接入极光JMessage(JIM)
    recv_doctor = get_doctor_by_user_id(target_user_id)
    if recv_doctor:
        doctor_uids = [target_user_id]
        # 给多个用户发私信
        merchant_doctor = get_merchant(recv_doctor.id)
        master_merchant = None
        if merchant_doctor:
            master_merchant = get_master_merchant(merchant_doctor.id)
        if merchant_doctor and recv_doctor != merchant_doctor.doctor:
            doctor_uids.append(merchant_doctor.doctor.user.id)
        if master_merchant and master_merchant.doctor not in [recv_doctor, merchant_doctor.doctor]:
            doctor_uids.append(master_merchant.doctor.user.id)
        DoctorPushService.send_new_msg_push(user_ids=doctor_uids, alert=alert, data=message)
        log_message_trace(MessageTraceEventType.TRACE_GAIA_MESSAGE_JPUSH_TO_DOCTOR, {"user_ids": doctor_uids})


@shared_task
def auto_reply_message(conversation_id, target_user_id, send_user_id):
    """ 目前支持用户首次发送消息给医生时，自动回复消息

    相关链接: http://wiki.wanmeizhensuo.com/pages/viewpage.action?pageId=28921473
    """
    from rpc.tool.log_tool import info_logger
    info_logger.info({"auto_reply_message": conversation_id, "target_user_id": target_user_id, "send_user_id": send_user_id})
    from message.views.message import internal_message_send
    from message.utils.message import get_last_send_time
    if not get_last_send_time(target_user_id, send_user_id):
        res = internal_message_send(
            sender_user_id=target_user_id,
            target_user_id=send_user_id,
            conversation_type=CONVERSATION_TYPE.MESSAGE,
            msg_type=MESSAGE_TYPE.TEXT,
            content=settings.AUTO_REPLY_MESSAGE,
            push_stat_labels={'event_type': 'push', 'event': 'received_private_msg'},
            is_system=True,
        )


@shared_task
def async_write_message_monitor_log(msg_id, conversation_id, send_uid, target_uid, doctor_id, target_type, msg_type):
    """ 异步写 私信监控日志

    :param: msg_id: 私信消息ID
    :param: conversation_id: 会话ID
    :param: send_uid: 发送者ID
    :param: target_uid: 接收者ID
    :param: target_type: 接收者类型

    TODO 目前由于 gaia 在 APPConfig 中初始化 gm_logging,
        导致 Celery Fork 模式无法在子进程中重复装载 gm_logging (目前暂时同步调用)
    """

    # 读取缓存判断是否第一次消息
    first_message_id = conversation_cache.get(
        "message:conversation_first_message:{}:{}".format(conversation_id, send_uid)
    )

    if not first_message_id:  # 读取数据库判断是否 建立会话首次发送文本消息

        first_message = Message.objects.filter(
            conversation_id=conversation_id,
            user_id=send_uid,
            type=MESSAGE_TYPE.TEXT,
        ).first()

        if not first_message:
            return

        first_message_id = first_message.id

        conversation_cache.set(
            "message:conversation_first_message:{}:{}".format(conversation_id, send_uid),
            first_message_id,
            ex=60 * 60 * 24 * 3  # 3 day
        )

    if str(msg_id) == str(first_message_id):
        is_first = 1
    else:
        is_first = 0

    log_dict = {
        "msg_id": msg_id,
        "send_uid": str(send_uid),
        "target_uid": target_uid,
        "doctor_id": doctor_id,
        "time": int(time.time() * 1000),
        "is_first": is_first,
        "target_type": target_type,
        "msg_type": msg_type
    }
    from gm_logging.general import log_maidian
    log_maidian(log_dict, 'message_monitor')


@shared_task
def async_dingtask_monitor_message(msg_id):
    """ 异步钉钉私信监控 (防恶意私信) """

    key = "message:conversation_message_monitor"
    current_time = int(time.time())

    bufffered = conversation_cache.get(key)

    output_msg_ids = []

    if not bufffered:

        data = {
            "start_time": current_time,
            "msg_ids": [msg_id]
        }

    else:
        data = json.loads(bufffered)

        last_start_time = data["start_time"]

        if current_time - last_start_time >= settings.MESSAGE_MONITOR_SCHEDULE:  # 超过 60 s 则批量输出一次

            output_msg_ids = data["msg_ids"]

            data = {
                "start_time": current_time,
                "msg_ids": [msg_id]
            }

        else:
            data["msg_ids"].append(msg_id)

    conversation_cache.set(key, json.dumps(data))

    if output_msg_ids:  # 处理需要输出的 消息ID

        if settings.MESSAGE_MONITOR_DINGTALK_ACCESS_TOKEN:
            webhook = 'https://oapi.dingtalk.com/robot/send?access_token={}'.format(settings.MESSAGE_MONITOR_DINGTALK_ACCESS_TOKEN)
            robot = DingtalkChatbot(webhook)
            at_mobiles = list(settings.MESSAGE_MONITOR_DINGTALK_MOBILES)

            msg  = "##【私信】{0} \n\n".format(datetime.now().strftime('%Y-%m-%d %H:%M:%S'))

            msg += """ | 消息ID |  会话ID | 发送时间 | 发送用户 | 接收用户 |发送内容 |\n"""

            messages = Message.objects.filter(id__in=output_msg_ids)
            for message in messages:

                if message.type in SPECIAL_MSG_TYPE:
                    continue
                    #content = Message.get_special_msg_content(message.type, message.body)
                else:
                    content = Message.get_general_msg_content(message.type, message.content)

                cs = ConversationUserStatus.objects.filter(
                    conversation_id=message.conversation_id
                ).exclude(user_id=message.user_id).first()
                if not cs:
                    continue
                target_uid = cs.user_id

                msg += "  - |  {}  |  {} | {}  | {}   | {}  | {}  |\n".format(
                    message.id,
                    message.conversation_id,
                    message.send_time.strftime('%Y-%m-%d %H:%M:%S'),
                    message.user_id,
                    target_uid,
                    content["text"].decode("utf8")
                )

            robot.send_markdown(title="最近的私信的消息", text=msg, at_mobiles=at_mobiles)
