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

import jpush
from celery import shared_task
from django.db.models import Q
from django.conf import settings
from django.core.paginator import Paginator
from django.utils.http import urlquote
from gm_types.push import PUSH_INFO_TYPE
from gm_types.gaia import PUSHCOUPON_STATUS, PUSHCOUPON_USER_STATUS, GROUPBUY_STATUS
from gm_types.push import HERA_TASK_TYPE
from gm_types.push import AUTOMATED_PUSH, PUSH_URGENCY
from gm_protocol import GmProtocol

from api.models import ServiceReserve, transaction, GroupBuyTeamOrder, ServiceItem, GroupBuyTeam
from api.models import Order
from api.models import User
from api.models import ChannelGift
from api.models import PushCoupon, PushCouponUser
from api.models import PushUserTag, PushTask
from api.models import UserExtraWechatInfo
from api.tool import notification_tool
from api.tool.notification_tool import send_notification
from api.tool.coupon_tool import _try_get_gift
from api.models.others import UserCheckinLog
from statistic.models import Device
from utils.dwz import DWZ
from rpc.tool.log_tool import info_logger
from rpc.tool.protocol import gm_protocol
from sms.utils.smsfactory import send_sms
from datetime import datetime
from pytz import timezone
import random
from rpc.context import get_rpc_remote_invoker
from api.tool.log_tool import logging_exception
from message.utils.push_service import eta_2_push_time, push_message_user_multi
from api.util.wechat_util import wechat_template_push

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


class PushLogInfo(object):
    def __init__(self, action, **kwargs):
        self._data = {
            'SYS': {
                'action': action,
                'errno': 0, 'errmsg': ''
            },
            'MODULE': 'push',
            'APP': {},
        }
        for k, v in kwargs.items():
            if k in ['errno', 'errmsg']:
                self._data['SYS'][k] = v
            else:
                self._data['APP'][k] = v

    def app(self, **kwargs):
        for k, v in kwargs.items():
            self._data['APP'][k] = v

    def error(self, errno, errmsg='unknown error'):
        self._data['SYS']['errno'] = errno
        self._data['SYS']['errmsg'] = errmsg

    @property
    def dict(self):
        dt = timezone(settings.TIME_ZONE).localize(datetime.now())
        self._data['TIME'] = dt.isoformat()
        return self._data


rnd = random.SystemRandom()


def gen_sendno(length=12,
               allowed_chars='abcdefghijklmnopqrstuvwxyz'
                             'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'):
    return ''.join(rnd.choice(allowed_chars) for i in range(length))


def query_push_task(platform=None, query=None, extra=None, alert='',
                    sendno=None, labels=None, eta=None):
    users = User.objects.filter(query)
    rpc_invoker = get_rpc_remote_invoker()
    if users is not None:
        paginator = Paginator(users, settings.PUSH_MAX_USER_COUNT)
        for page_index in paginator.page_range:
            page = paginator.page(page_index)
            user_ids = [user.id for user in page]
            if user_ids:
                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=labels
                    ).unwrap()
                except Exception as e:
                    logging_exception()


def allocate_push_task_one(
        platform=None, user_id=None, extra=None, alert='',
        labels=None,
        eta=None):
    rpc_invoker = get_rpc_remote_invoker()
    try:
        rpc_invoker['push2/push/user/notification/uids'](
            user_ids=[user_id],
            platform=platform,
            alert=alert,
            extra=extra,
            push_time=eta_2_push_time(eta),
            labels=labels
        ).unwrap()
    except Exception as e:
        logging_exception()


def allocate_push_task_users(platform=None, user_id=None, extra=None, alert='',
                             push_type=None, eta=None, others={}):
    rpc_invoker = get_rpc_remote_invoker()

    if isinstance(user_id, list):
        user_ids = user_id
    else:
        user_ids = [user_id]

    try:
        return rpc_invoker['push2/push/user/automated_push/uids'](
            user_ids=user_ids,
            platform=platform,
            alert=alert,
            extra=extra,
            push_time=eta_2_push_time(eta),
            push_type=push_type,
            others=others,
        ).unwrap()
    except Exception as e:
        logging_exception()

def allocate_push_task_multi(
        platform=None, user_id=None, extra=None, alert='',
        labels=None,
        eta=None):
    rpc_invoker = get_rpc_remote_invoker()
    push_max_user_count = settings.PUSH_MAX_USER_COUNT
    user_ids = (user_id[i:i + push_max_user_count] for i in range(0, len(user_id), push_max_user_count))
    for uid_list in user_ids:
        try:
            rpc_invoker['push2/push/user/notification/uids'](
                user_ids=uid_list,
                platform=platform,
                alert=alert,
                extra=extra,
                push_time=eta_2_push_time(eta),
                labels=labels,
            ).unwrap()
        except Exception as e:
            logging_exception()


def allocate_push_task_multi_for_other_style(
        platform=None, user_ids=None, extra=None, alert='',
        labels=None, eta=None, other_style=None):
    rpc_invoker = get_rpc_remote_invoker()
    push_max_user_count = settings.PUSH_MAX_USER_COUNT
    user_idss = (user_ids[i:i + push_max_user_count] for i in range(0, len(user_ids), push_max_user_count))
    for uid_list in user_idss:
        try:
            rpc_invoker['push2/push/user/notification_multi_style/uids'](
                user_ids=uid_list,
                platform=platform,
                alert=alert,
                extra=extra,
                push_time=eta_2_push_time(eta),
                labels=labels,
                other_style=other_style
            ).unwrap()
        except Exception as e:
            logging_exception()


@shared_task
def allocate_push_task_stock_alert(diary_id, title, push_content, sms_content, text, sms_data):
    # todo 可删，英才已确认 180314
    query = Q(person__recommenddoctor__diary__id=diary_id)
    url = gm_protocol.get_service_detail(Diary.objects.get(id=diary_id).service.id)  # TODO zhangyun
    query_push_task(query=query, alert=push_content)
    send_notification(query, title=title, content=push_content, url=url)
    users = User.objects.filter(query)
    for user in filter(lambda x: x.userextra.phone, users):
        send_sms(user.userextra.phone, 18, sms_data)


def patch_change_reserve_state(service_id):
    # TODO ugly patch:It should be executed when order is created.
    for service_reserve in ServiceReserve.objects.filter(service_id=service_id):
        order_item = Order.objects.filter(
            Q(service_id=service_reserve.service_id) & Q(user_id=service_reserve.user_id)).first()
        if order_item is not None:
            service_reserve.stat = '9'  # TODO Do not use hard code
            service_reserve.save()


@shared_task
def query_send_notification(query, title, content='', url=''):
    users = User.objects.filter(query)
    for user in users:
        send_notification(user.id, title, content, url)


@shared_task
def query_send_sms(query, service_id, txt):
    users = User.objects.filter(query)
    to_url = urlquote('http://m.igengmei.com/promotion/{}/'.format(service_id))
    url = 'http://m.igengmei.com/app_router?next_url={}'.format(to_url)
    tinyurl = DWZ().make_dwz(url)
    if not tinyurl:
        return

    data = [{'service': txt}, {'ser_link': tinyurl}]
    for user in users:
        if user.userextra.phone:
            send_sms(user.userextra.phone, 19, data, platform='md')


@shared_task
def push_checkin_message():
    url = 'gengmei://check_in_detail'
    extra = {
        'type': PUSH_INFO_TYPE.GM_PROTOCOL,
        'msgType': 4,
        'pushUrl': url,
        'push_url': url,
    }

    user_ids = UserCheckinLog.get_yesterday_checked_today_not_checked_user_ids()
    step = 128
    _u = [user_ids[i: i + step] for i in range(0, len(user_ids), step)]
    rpc_invoker = get_rpc_remote_invoker()
    for _user_ids in _u:
        rpc_invoker['push2/push/user/notification/uids'](
            user_ids=_user_ids,
            extra=extra,
            alert=u'今天你来签到领福利了吗？来，点开就可以拿走哦~',
        ).unwrap()


@shared_task
def pushtask_coupon(pushcoupon_id):
    pushcoupon = PushCoupon.objects.get(id=pushcoupon_id)
    if pushcoupon.status == PUSHCOUPON_STATUS.PASS:
        #  第一次执行，区别于celery执行过程中重启一次再进入
        pushcoupon.status = PUSHCOUPON_STATUS.SENDING
        pushcoupon.save()
    elif pushcoupon.status == PUSHCOUPON_STATUS.SENDING:
        # 避免celery重启后，重新运行任务不能正常执行
        pass
    else:
        return
    # pushcoupon.status = PUSHCOUPON_STATUS.SENDING
    # pushcoupon.save()
    gm_protocol = GmProtocol()
    coupon_user_list = list(pushcoupon.user.filter(
        push_coupon_status=PUSHCOUPON_USER_STATUS.DEFAULT).values_list('id', flat=True))
    for id in coupon_user_list:
        with transaction.atomic():
            obj = PushCouponUser.objects.select_for_update().get(id=id)
            if obj.push_coupon_status == PUSHCOUPON_USER_STATUS.SUCCESS:
                continue
            channelgift = ChannelGift.objects.select_for_update().get(id=pushcoupon.gift_id)
            try:
                success, _ = _try_get_gift(channelgift, obj.user)
            except:
                success = False

            if success:
                obj.push_coupon_status = PUSHCOUPON_USER_STATUS.SUCCESS
                obj.get_coupon_time = datetime.now()
            else:
                obj.push_coupon_status = PUSHCOUPON_USER_STATUS.FAIL
            obj.save()

        if success and pushcoupon.push_alert:
            content = pushcoupon.alert_msg
            try:
                extra = {
                    'type': PUSH_INFO_TYPE.GM_PROTOCOL,
                    'pushUrl': gm_protocol.coupon_list,
                }
                kwargs = {
                    'platform': ['android', 'iPhone'],
                    'user_id': obj.user_id,
                    'extra': extra,
                    'alert': content,
                    'labels': {'hera_push_task_id': id, 'hera_task_type': HERA_TASK_TYPE.COUPON},
                }
                allocate_push_task_one(**kwargs)
                notification_tool.send_notification(uid=obj.user_id, title='美券到账提醒',
                                                    content=content, url=extra['pushUrl'])
            except:
                logging_exception()

    pushcoupon = PushCoupon.objects.get(id=pushcoupon_id)
    pushcoupon.status = PUSHCOUPON_STATUS.SUCCESS
    pushcoupon.save()


def get_last_lever_tagname(service):
        '''获取美购的标帖'''
        tag_name,tag_id="",0

        bodypart_subitem=service.bodypart_subitem
        if bodypart_subitem:
            tag_name=bodypart_subitem.name
            tag_id=bodypart_subitem.tag_id
            return tag_name,tag_id

        if service.tags:
            tag=service.tags.first()
            tag_name,tag_id=tag.name,tag.id
        return tag_name,tag_id


@shared_task
def push_groupbuy_success_message(groupbuy_id):
    '''
        拼团成功消息
    '''
    try:
        gbt = GroupBuyTeam.objects.filter(id=groupbuy_id).\
            only('service_item_id', 'status').first()
        if not gbt or gbt.status != GROUPBUY_STATUS.GROUPBUY_SUCCESSED:
            return

        user_id_to_order_id_list = GroupBuyTeamOrder.objects.filter(
            groupbuy_team_id=groupbuy_id, paid=True,
        ).values_list('user_id', 'order_id')
        serviceitem = ServiceItem.objects.filter(id=gbt.service_item_id).only('id').first()
        service_item_name = ""
        if serviceitem:
            service_item_name = ''.join(serviceitem.items_name)

        alert = u"恭喜您拼团{}成功".format(service_item_name)
        for user_id, order_id in user_id_to_order_id_list:
            extra = {
                'type': PUSH_INFO_TYPE.GM_PROTOCOL,
                'pushUrl': gm_protocol.get_order_detail(order_id),
                'push_url': gm_protocol.get_order_detail(order_id),
            }
            push_message_user_multi(user_ids=[user_id], alert=alert, extra=extra)

            # 打榜任务触发
            from gm_types.gaia import YOUNG_HIT_TASK_TYPE
            from variety_show.services import YoungTaskService
            YoungTaskService.set_today_user_completed_task(user_id=user_id, task_type=YOUNG_HIT_TASK_TYPE.GROUP_BY)
        try:
            #成功消息小程序推送
            tag_name,tag_id=get_last_lever_tagname(serviceitem.service)
            message="您发起的【{tag_name}】拼团已完成，快和小伙伴们约起来吧".format(tag_name=tag_name)

            #小程序推送
            uw=UserExtraWechatInfo.objects.get(user_id=gbt.user_id)

            form_id=gbt.form_id
            open_id=uw.open_id

            push_result_code=wechat_template_push(open_id=open_id, template_type='groupbuy', form_id=form_id, data=message)

            info_logger.info("拼团成功推送结果---")
            info_logger.info(push_result_code)
        except Exception as e:
            info_logger.info(e)

    except Exception as e:

        logging_exception()


@shared_task
def push_tags_task(platform=None, extra=None, alert='', eta=None, hera_pushtype=None,
                             sandbox=False, hera_pushtask_id=None):
    tag_ids = PushTask.objects.get(id=hera_pushtask_id).push_tags.values_list(
        'id', flat=True)
    device_ids = list(
        PushUserTag.objects.filter(tag_id__in=tag_ids).values_list(
            'device_id', flat=True))
    devices = Device.objects.filter(device_id__in=device_ids)
    user_ids = [device.user.last().id for device in devices if
                device.user.last()]
    user_ids = list(set(user_ids))
    from api.view.support import HERA2PUSH_STAT
    kwargs = {
        'platform': platform,
        'user_id': user_ids,
        'extra': extra,
        'alert': alert,
        'eta': eta,
        'labels': {
            'hera_push_task_id': hera_pushtask_id,
            'push_stat_category': HERA2PUSH_STAT[hera_pushtype]
            if hera_pushtype in HERA2PUSH_STAT else None,
            'event_type': 'push',
            'event': 'operator_daily' if hera_pushtype in HERA2PUSH_STAT else '',
            'hera_task_type': HERA_TASK_TYPE.MESSAGE,
        },
    }
    allocate_push_task_multi(**kwargs)


def automate_push_task(user_ids, push_type, platform=None, alert='', extra=None,
              push_time=None, silent=False, urgency=PUSH_URGENCY.NORMAL, others=None):
    """
    精准自动化推送
    :return:
    """
    if push_type not in AUTOMATED_PUSH:
        return

    step = 128
    _u = [user_ids[i: i + step] for i in range(0, len(user_ids), step)]

    rpc_invoker = get_rpc_remote_invoker()
    for _user_ids in _u:
        try:
            res = rpc_invoker['push2/push/user/automated_push/uids'](
                user_ids=_user_ids,
                push_type=push_type,
                platform=platform,
                alert=alert,
                extra=extra,
                push_time=push_time,
                silent=silent,
                urgency=urgency,
                others=others,
            ).unwrap()
            info_logger.info(res)
        except Exception as e:
            logging_exception()


def push_all_task(platform=None, alert='', extra=None, eta=None):
    """
    全量推送
    :return:
    """
    rpc_invoker = get_rpc_remote_invoker()
    try:
        res = rpc_invoker['push2/push/user/notification/all'](
            platform=platform,
            alert=alert,
            extra=extra,
            push_time=eta_2_push_time(eta),
        ).unwrap()
        info_logger.info(res)
    except Exception as e:
        logging_exception()


@shared_task
def ai_push_tags_task(device_id):

    if not device_id:
        return

    rpc_invoker = get_rpc_remote_invoker()
    rpc_invoker['demeter/push/ai/add_skin_tag'](
        device_ids=[device_id]
    ).unwrap()


@shared_task
def intelligent_push_task(content_id, user_ids, push_type, extra, platform=None, alert='', others=None, labels={}):
    """
    push 统一管理 使用demeter发送 push
    :param content_id:  内容id
    :param user_ids:    接收push用户 list
    :param push_type:   推送类型
    :param platform:    推送渠道 Android ios
    :param extra:       dict
    :param alert:       object
    :param others:      object
    :param labels:      object
    :return:
    """
    rpc_invoker = get_rpc_remote_invoker()
    _rpc_url = "demeter/push/user/community_push"
    kwargs = {
        "content_id": content_id,
        "user_ids": user_ids,
        "push_type": push_type,
        "platform": platform,
        "extra": extra,
        "alert": alert,
        "labels": labels,
    }
    if others:
        kwargs.update({'others': others})

    try:
        rpc_invoker[_rpc_url](**kwargs).unwrap()
    except:
        logging_exception()
