# -*- coding: utf-8 -*-
# author: gaomingming
# modify time:2019.07.05

from __future__ import absolute_import, unicode_literals

import datetime
import re
import pytz
import threadpool
from django.db import IntegrityError, transaction
from django.db.models import Q
from gm_types.gaia import PUSHCOUPON_STATUS
import copy
from rpc.decorators import bind_context
from api.models import Region, City, Province, Tag, PushUser, PushUserTag, User, UserExtra, UserNewOld, Polymer
from django.core.exceptions import ObjectDoesNotExist

from hera.views.privatemessagepush import get_duration
from talos.models.live import LiveChannel
from rpc.context import get_rpc_remote_invoker
from rpc.tool.dict_mixin import to_dict
from ..queries.pushtask2 import PushTaskDQ2
from django.conf import settings
from datetime import datetime
from statistic.models import Device
import threading

# 引入新的pushtask任务
from api.models.pushtask2 import PushTask2
from rpc.tool.log_tool import push_logger, logging_exception
from api.models.order import Order
from gm_types.push import (
    PUSH_USER_TYPE2,
    HERA_PUSH_TYPE2,
    SEND_STATUS,
    HERA_PUSH_PLATFORM,
)

# rpc服务
rpc_invoker = get_rpc_remote_invoker()

uri_pre = 'hera/pushtask2'


@bind_context(uri_pre + '/query')
def pushtask_query(ctx, options):
    '''
		批量拿去数据
	'''
    dqobj = PushTaskDQ2()

    result = dqobj.process(**options)
    # push_logger.info("批量拿到的数据..")

    data = result.get('data')

    pushtask_ids = [item.get("id") for item in data if not item.get("jiguang_id") and item.get("task_id")]

    # push_logger.info("拿到的pushtask_id列表")
    # push_logger.info(pushtask_ids)

    if pushtask_ids:
        try:
            # push_logger.info("rpc请求--")
            send_results = rpc_invoker["push2/push/user/get_push_info"](
                hera_push_task_ids=pushtask_ids,
                start_id=settings.HERA_PUSHINFO_START_ID).unwrap()
            # push_logger.info(send_results)

            for item in send_results:
                msg_id, hera_push_task_id = item.get("msg_id"), item.get("hera_push_task_id")

                # 更新数据库
                if msg_id:
                    PushTask2.objects.filter(id=hera_push_task_id).update(jiguang_id=msg_id, status=SEND_STATUS.SUCCESS)
        except Exception as e:
            push_logger.info("rpc请求异常--")
            push_logger.info(e)

    return result


@bind_context(uri_pre + '/create')
@transaction.atomic
def pushtask_create2(ctx, parames):
    '''
		创建任务
	'''
    push_logger.info("*" * 50)
    push_logger.info(parames)

    #获取创建用户
    user =ctx.session.user
    #user=User.objects.get(id=2)

    push_logger.info(user)

    city = parames.pop('city')
    region = parames.pop('region')
    users = parames.pop("users")
    pushtags = parames.pop("pushtags")

    if int(parames.get("showname")) == 1:
        parames.update({"showname": True})
    else:
        parames.update({"showname": False})

    market_ids = parames.get("market_id")

    pushtask2 = PushTask2.objects.create(**parames)
    pushtask2.online = True

    # 找不到登录用户
    pushtask2.creator = user
    pushtask2.save()

    # 保存上传用户
    if market_ids:
        market_ids = [int(item) for item in market_ids]
        if not users:
            from hera.views.market import gets_user_ids
            user_ids = gets_user_ids(market_ids)
            users = []
            for obj in user_ids:
                users.append({"id": obj, "device": ''})

    save_update_user(users, pushtask2)

    # 保存大区
    regions = Region.objects.filter(id__in=region)
    pushtask2.region.add(*regions)

    # 保存城市
    citys = City.objects.filter(id__in=city)
    pushtask2.city.add(*citys)

    # 保存tag
    tags = Tag.objects.filter(id__in=pushtags)
    pushtask2.pushtags.add(*tags)

    return ""


def save_update_user(users, pushtask2):
    '''
		保存上传的用户id
	'''

    # 异步数据入库
    def async_update_user(users, pushtask2):
        if users:
            push_logger.info("保存上传的用户设备")

            pushusers = []
            for user in users:
                # 如果设备号不为空
                try:
                    if user["device"]:
                        # 查找到设备id
                        devices = Device.objects.filter(device_id=user["device"])
                        push_logger.info("查找到的设备数:{0}".format(devices.count()))
                        # 设备关联多个用户
                        for device in devices:
                            users = device.user.all()
                            push_logger.info(
                                "设备{0}关联的用户数:{1},设备号为:{2}".format(device.device_id, users.count(), device.id))
                            for item in users:
                                # pushtask2.pushuser_set.create(user=item.id,device=user["device"])
                                pushusers.append(
                                    PushUser(pushtask_id=pushtask2.id, user=item.id, device=user["device"]))
                    else:
                        push_logger.info("保存上传的用户ID")
                        pushusers.append(PushUser(pushtask_id=pushtask2.id, user=int(user["id"]), device=""))
                # pushtask2.pushuser_set.create(user=int(user["id"]),device="")
                except Exception as e:
                    push_logger.info(e)
            # 批量入库
            PushUser.objects.bulk_create(pushusers)

    threading.Thread(target=async_update_user, args=(users, pushtask2)).start()
    push_logger.info("上传用户入库结束!")


@bind_context(uri_pre + '/edit')
@transaction.atomic
def pushtask_edit2(ctx, parames, pushtask_id):
    '''
		编辑任务
	'''
    # 获取创建用户
    login_user = ctx.session.user

    push_logger.info("编辑任务开始---")

    city = parames.pop('city')
    region = parames.pop('region')
    users = parames.pop("users")
    pushtags = parames.pop("pushtags")

    # 获取任务
    PushTask2.objects.filter(id=pushtask_id).update(**parames)
    pushtask2 = PushTask2.objects.get(id=pushtask_id)

    # 删除旧用户,保存上传新的用户
    PushUser.objects.filter(pushtask_id=pushtask_id).delete()

    market_ids = parames.get("market_id")
    if market_ids:
        market_ids = [int(item) for item in market_ids]
        if not users:
            from hera.views.market import gets_user_ids
            user_ids = gets_user_ids(market_ids)
            users = []
            for obj in user_ids:
                users.append({"id": obj, "device": ''})

    save_update_user(users, pushtask2)

    # 删除再保存大区
    pushtask2.region.clear()
    regions = Region.objects.filter(id__in=region)
    pushtask2.region.add(*regions)

    # 保存城市
    pushtask2.city.clear()
    citys = City.objects.filter(id__in=city)
    pushtask2.city.add(*citys)

    # 保存tag
    pushtask2.pushtags.clear()
    tags = Tag.objects.filter(id__in=pushtags)
    pushtask2.pushtags.add(*tags)

    push_logger.info("编辑任务结束---")

    return ""


def filter_newold_user(pushtask, users):
    '''
		过滤新老用户
	'''
    if int(pushtask.usertype) == PUSH_USER_TYPE2.NEW_USER:
        push_logger.info("开始过滤新用户")
        try:
            users = UserNewOld.objects.values_list("user", flat=True).filter(is_new=True, user__in=users)
            push_logger.info("新用户长度为:{0}".format(len(users)))
        except Exception as e:
            push_logger.info(e)

    if int(pushtask.usertype) == PUSH_USER_TYPE2.OLD_USER:
        push_logger.info("开始过滤老用户")
        try:
            users = UserNewOld.objects.values_list("user", flat=True).filter(is_new=False, user__in=users)
            push_logger.info("老用户长度为:{0}".format(len(users)))
        except Exception as e:
            push_logger.info(e)

    return users


def get_push_users(pushtask):
    '''
		获取推送用户
	'''
    # 用户标帖
    device_ids = PushUserTag.objects.values_list("device_id", flat=False).filter(tag_id__in=pushtask.pushtags.all())

    # 获取关联用户
    users = []

    # 通过设备ID获取用户
    devices = Device.objects.filter(device_id__in=device_ids)
    for device in devices:
        users.extend([user.id for user in device.user.all()])

    push_logger.info("通过标帖获取用户数:{0}".format(len(users)))

    regions = pushtask.region.all()

    # 获取需要发送的城市集合
    citys = set(pushtask.city.all())

    for region in regions:
        for province in region.province_set.all():
            citys = citys.union(province.cities.all())

    # 通过城市获取用户
    if citys:
        sql = "select id,user_id from api_userextra where current_city_id in ({0})".format(
            ",".join(["'{0}'".format(item.id) for item in citys]))
        push_logger.info(sql)

        extra = UserExtra.objects.raw(sql)
        extra_users = [item.user_id for item in extra]

        # 城市用户必须是标帖用户
        if users:
            users = [item for item in extra_users if item in users]
        else:
            users = extra_users

    # 如果标帖和城市都没有用户的话
    if len(device_ids) == 0 and not citys:
        users = User.objects.values_list("id", flat=True).all()
        push_logger.info("获取全部用户长度:{0}".format(len(users)))

    users = filter_newold_user(pushtask, users)

    push_logger.info("获取的用户长度:{0}".format(len(users)))

    return list(set(users))


@bind_context(uri_pre + '/operate')
def send_message(ctx, pushtask_id):
    '''
		推送消息(重试机制)
	'''
    pass


def _send_message(pushtask_id):
    '''
		推送消息
	'''
    try:
        pushtask = PushTask2.objects.get(id=pushtask_id)
    except ObjectDoesNotExist:
        push_logger.info("pushtask id:{} not found!".format(pushtask_id))

    if pushtask.status == SEND_STATUS.OFFLINE:
        # 已下线
        return

    index_str = 'push_task_id:{}'.format(pushtask_id)
    # 如果pushvalue和url选择其一
    push_url = pushtask.pushvalue if pushtask.pushvalue else pushtask.url
    push_logger.info("推送的Url:{0}, {1}".format(push_url, index_str))

    # 其他发送
    extra = {
        'msgType': 0, 'type': 1, 'image': pushtask.image, 'pushUrl': push_url,
        'is_activity_push': True, 'push_image': pushtask.push_image, 'mutable-content': 1,
        'push_task_id': pushtask.id,
    }

    # 推送时间
    pushtime = pytz.timezone(settings.TIME_ZONE).localize(pushtask.pushtime)
    pushtime = int((pushtime - datetime.fromtimestamp(0, pytz.timezone("UTC"))).total_seconds())

    platform = ['iPhone', 'android']
    if pushtask.platform == 1:
        platform = "iPhone"
    elif pushtask.platform == 2:
        platform = "android"

    # 推送消息
    message = {
        "platform": platform,
        "title": pushtask.heading,
        "alert": pushtask.subheading,
        "extra": extra,
        "push_time": pushtime,
        "push_task_id": pushtask.id
    }

    push_logger.info(message)

    # 全量推送(全部用户)
    if not pushtask.pushtags.count() and not pushtask.region.count() and not pushtask.city.count() and pushtask.usertype == PUSH_USER_TYPE2.ALL:
        # 加一个全量标志位
        PushUser.objects.create(user=0, device="", pushtask=pushtask, is_all=True)
        push_logger.info("全量推送开始{}---.".format(index_str))
        try:
            task_id = push_message_all(message)
            pushtask.task_id = task_id
            pushtask.save()
            push_logger.info("拿到的task_id:{0}, {1}".format(task_id, index_str))
        except Exception as e:
            push_logger.info(e)
            pushtask.status = SEND_STATUS.FAILURE
            pushtask.save()
        push_logger.info("全量推送结束{}---.".format(index_str))
        return

    # 批量
    if pushtask.usertype != PUSH_USER_TYPE2.APPOINT_USER:

        # 标贴、大区、城市、新、老用户
        ids = get_push_users(pushtask)

        # 批量每一个用户入库
        def store_users(ids):
            try:
                for user_id in ids:
                    PushUser.objects.create(user=user_id, device="", pushtask=pushtask, is_all=False)
            except Exception as e:
                push_logger.info("批量入库异常{}---".format(index_str))
                push_logger.info(e)

        # 批量发送(不展示用户名)
        if not pushtask.showname:
            try:
                push_logger.info("开始批量发送{}----".format(index_str))
                push_logger.info("批量推送用户长度为:{0}".format(len(ids)))
                task_id = push_message_mulply(message, ids)
                pushtask.task_id = task_id
                pushtask.save()
                push_logger.info("批量推送结束{}---".format(index_str))
            except Exception as e:
                push_logger.info(e)
                pushtask.status = SEND_STATUS.FAILURE
                pushtask.save()
        else:
            # 批量展示用户名和消息
            push_logger.info("个性化批量推送{}.".format(index_str))

            # 超过100万不进行个性化推送
            if len(ids) > 1000000:
                pushtask.approver_status = False
                pushtask.approver = None
                pushtask.save()
                push_logger.info("个性化推送用户量太大,审核失败!{}".format(index_str))
                return

            # 组装线程池消息列表
            messages = [get_message_personality(message, pushtask.message, item, pushtask) for item in ids]

            # 启动线程池
            pool = threadpool.ThreadPool(20)

            requests = threadpool.makeRequests(push_personality_interface, messages)

            [pool.putRequest(req) for req in requests]
            pool.wait()

            push_logger.info("个性化批量推送结束{}.".format(index_str))

        push_logger.info("批量用户入pushuser库{}---".format(index_str))
        threading.Thread(target=store_users, args=(ids,)).start()
        push_logger.info("批量用户入pushuser库结束{}---".format(index_str))
        return

    # 指定用户
    if int(pushtask.usertype) == PUSH_USER_TYPE2.APPOINT_USER:

        pushusers = pushtask.pushuser_set.values_list("user", flat=True).all()

        pushusers = map(int, pushusers)

        push_logger.info("指定用户长度:{0},{1}".format(len(pushusers), index_str))
        push_logger.info(str(pushusers))

        # 指定用户批量
        if not pushtask.showname:
            push_logger.info("开始制定用户批量{}---".format(index_str))
            # 传消息id
            try:
                task_id = push_message_mulply(message, pushusers)
                pushtask.task_id = task_id
                pushtask.save()
            except Exception as e:
                push_logger.info(e)
                pushtask.status = SEND_STATUS.FAILURE
                pushtask.save()
        else:
            push_logger.info("开始制定用户个性化推送{}--".format(index_str))
            # 个性化推送
            try:
                task_id = None
                for item in pushusers:
                    task_id = push_message_personality(message, pushtask.message, item, pushtask)

                if task_id:
                    pushtask.task_id = task_id
                    pushtask.save()
                else:
                    pushtask.status = SEND_STATUS.FAILURE
                    pushtask.save()
            except Exception as e:
                push_logger.info(e)
                pushtask.status = SEND_STATUS.FAILURE
                pushtask.save()

            push_logger.info("个性化定制推送结束{}.".format(index_str))
        return

    push_logger.info("推送任务结束{}-----".format(index_str))


def push_message_personality(message, hello_message, user_id, pushtask):
    '''
		个性化推送消息
	'''

    # 组装消息
    message = get_message_personality(message, hello_message, user_id, pushtask)

    # 发送
    return push_personality_interface(message)


def push_personality_interface(message):
    '''
		推送接口
	'''
    push_url = "push2/push/hera_push_task/personality"
    result = rpc_invoker[push_url](**message).unwrap()
    # try:
    # 	result=rpc_invoker[push_url](**message).unwrap()
    # 	push_logger.info(result)
    # except Exception as e:
    # 	push_logger.info(e)
    # push_logger.info("task_id:{0}".format(result.get("task_id")))
    # push_logger.info("个性化消息,推送成功")
    return result.get("task_id")


def get_message_personality(message, hello_message, user_id, pushtask):
    '''
		组装个性化推送的消息
	'''
    message = copy.copy(message)
    try:
        user = User.objects.get(id=user_id)
    except Exception as e:
        push_logger.info(e)
        return

    _message = hello_message.encode("utf-8")
    _lastname = user.last_name.encode("utf-8")

    # 默认用户不展示问候语
    if _lastname.startswith("更美用户".encode("utf-8")):
        message.update({"title": pushtask.heading})
    else:
        message.update({"title": _message + _lastname})

    # push_logger.info("个性化用户ID:{0}".format(user_id))
    # push_logger.info(message)

    message.update({"user_ids": [user_id]})

    return message


def push_message_all(message):
    '''
		全量推送
	'''
    push_url = "push2/push/hera_push_task/all"
    try:
        result = rpc_invoker[push_url](**message).unwrap()
    except Exception as e:
        push_logger.info(e)

    return result.get("task_id")


def push_message_mulply(message, ids):
    '''
		批量推送
	'''
    push_url = "push2/push/hera_push_task/personality"
    message.update({"user_ids": ids})

    result = rpc_invoker[push_url](**message).unwrap()
    push_logger.info("批量推送结果")
    push_logger.info(result)
    # try:
    # 	result=rpc_invoker[push_url](**message).unwrap()
    # 	push_log.info(result)
    # except Exception as e:
    # 	push_log.info str(e)
    return result.get("task_id")


@bind_context(uri_pre + '/get')
def pushtask_detail2(ctx, id, options=None):
    '''
		查询任务详情
	'''
    try:
        pushtask = PushTask2.objects.get(id=id)
    except ObjectDoesNotExist:
        push_logger.info("pushtask id:{} not found!".format(id))

    if options is None:
        options = {
            'fields': None,
            'excludes': None,
            'expands': None,
        }
        pushtask.market_id = [int(item) for item in eval(pushtask.market_id)]
        pushtask = to_dict(pushtask, **options)

    return pushtask


@bind_context(uri_pre + '/approver_pass')
def approver_pass(ctx, ids):
    '''
        任务审核
    '''
    user = ctx.session.user
    # user =User.objects.get(id=3)

    # 只能审核推送时间比当前时间大的任务,排除自己给自己创建的
    task2 = PushTask2.objects.values_list("id", flat=True).filter(id__in=ids, pushtime__gt=datetime.now()).exclude(
        creator=user)
    task2.update(approver_status=True, approver=user, status=SEND_STATUS.READY)

    task2 = map(int, task2)

    push_logger.info("审核通过任务--")
    push_logger.info(task2)

    # 对审核通过的每一个任务执行推送
    map(_send_message, task2)

    return


@bind_context(uri_pre + '/approver_cancel')
def approver_cancel(ctx, id):
    '''
        取消审核(暂时不做)
    '''
    pass


# 取消已审核推送
@bind_context(uri_pre + '/revoke_push_tasks')
def revoke_push_tasks(ctx, task_ids):
    tasks = PushTask2.objects.filter(id__in=task_ids)
    revoked = _do_revoke([task.task_id for task in tasks])
    if revoked:
        tasks.update(status=SEND_STATUS.REVOKED)
    return revoked

def _do_revoke(task_ids):
    retry_num = 3
    while retry_num>0:
        try:
            resp = rpc_invoker['push2/push/revoke/push_tasks'](task_ids=task_ids).unwrap()
            if resp['error'] == 0:
                return True
        except:
            logging_exception()
        retry_num -= 1
    return False
