package com.qs.mp.quartz.task;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.qs.mp.admin.domain.Marketing;
import com.qs.mp.admin.domain.MarketingMsg;
import com.qs.mp.admin.service.IMarketingAwardsService;
import com.qs.mp.admin.service.IMarketingMsgService;
import com.qs.mp.admin.service.IMarketingService;
import com.qs.mp.common.core.redis.DistributedLocker;
import com.qs.mp.common.core.redis.RedisCache;
import com.qs.mp.common.enums.MarketingStatusEnum;
import com.qs.mp.common.enums.UserTypeEnum;
import com.qs.mp.common.utils.DateUtils;
import com.qs.mp.common.utils.LogUtil;
import com.qs.mp.framework.domain.WxSubscribeMessage;
import com.qs.mp.framework.redis.RedisKey;
import com.qs.mp.framework.redis.RedisLockKey;
import com.qs.mp.framework.service.IWxSubscribeMessage;
import com.qs.mp.user.domain.MarketingUserCode;
import com.qs.mp.user.service.IMarketingUserCodeService;
import java.util.ArrayList;
import java.util.stream.Collectors;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

/**
 * 营销活动相关任务
 *
 * @author Cup
 * @date 2022/5/18
 */
@Component("marketingTask")
public class MarketingTask {


    @Autowired
    private DistributedLocker distributedLocker;

    @Autowired
    private IMarketingService marketingService;

    @Autowired
    private IWxSubscribeMessage wxSubscribeMessage;

    @Autowired
    private IMarketingUserCodeService marketingUserCodeService;

    @Autowired
    private IMarketingMsgService marketingMsgService;


    protected final Logger logger = LoggerFactory.getLogger(this.getClass().getSimpleName());


    /**
     * 开奖任务方法
     */
    public void lottery() {
        // 获取结束时间小于等于当前时间且未开奖的活动
        Date now = DateUtils.getNowDate();
        List<Marketing> marketingList = marketingService.list(new LambdaQueryWrapper<Marketing>()
                .eq(Marketing::getTriggerStatus, 0)
                .eq(Marketing::getIsOn, MarketingStatusEnum.ON.getValue())
                .le(Marketing::getEndTime, now));
        if (CollectionUtils.isEmpty(marketingList)) {
            return;
        }

        for (Marketing marketing : marketingList) {
            String lockKey = RedisLockKey.build(RedisLockKey.MARKETING_LOTTERY_KEY, marketing.getId());
            // 加锁,自动续期
            if (!distributedLocker.tryLock(lockKey,0,-1,TimeUnit.SECONDS)) {
                continue;
            }
            try {
                // 开奖
                marketingService.lottery(marketing);
            } catch (Exception e) {
                LogUtil.error(logger, e, "活动开奖异常。marketingId:{0}", marketing.getId());
            } finally {
                // 释放锁
                distributedLocker.unlock(lockKey);
            }
        }

    }


    /**
     * 活动通知消息生成
     * @param type 消息类型,1活动开奖通知,2活动开始通知
     */
    public void sendMessage(Integer type) {
        MarketingTask proxy = (MarketingTask) AopContext.currentProxy();
        if (type == 1) {
            proxy.saveMarketingLotteryMsg();
        } else if (type == 2) {
            proxy.saveMarketingMsg();
        }
    }

    /**
     * 生成开奖发送消息
     */
    @Transactional(rollbackFor = Exception.class)
    public void saveMarketingLotteryMsg() {
        // 获取已开奖,且未发送开奖通知的活动
        List<Marketing> marketingList = marketingService.list(new LambdaQueryWrapper<Marketing>()
            .eq(Marketing::getTriggerStatus, 1)
            .eq(Marketing::getIsOn, MarketingStatusEnum.ON.getValue())
            .eq(Marketing::getIsSendLottery, 0));
        if (CollectionUtils.isEmpty(marketingList)) {
            return;
        }

        for (Marketing marketing : marketingList) {
            // 保存开奖消息
            // 获取所有的参与用户的用户id去重
            List<MarketingUserCode> sendMsgUserList = marketingUserCodeService.list(new LambdaQueryWrapper<MarketingUserCode>()
                .select(MarketingUserCode::getUserId)
                .eq(MarketingUserCode::getMarketingId, marketing.getId())
                .eq(MarketingUserCode::getUserType, UserTypeEnum.ORDINARY.getValue())
                .groupBy(MarketingUserCode::getUserId));
            if (CollectionUtils.isEmpty(sendMsgUserList)) {
                marketingService.update(new LambdaUpdateWrapper<Marketing>()
                    .set(Marketing::getIsSendLottery, 1)
                    .eq(Marketing::getId, marketing.getId()));
                continue;
            }
            List<MarketingMsg> marketingMsgList = new ArrayList<>();
            for (MarketingUserCode marketingUserCode : sendMsgUserList) {
                // 封装开奖订阅消息
                MarketingMsg marketingMsg = new MarketingMsg();
                marketingMsg.setType(1);
                marketingMsg.setMarketingId(marketing.getId());
                marketingMsg.setContext(marketing.getTitle());
                marketingMsg.setUserId(marketingUserCode.getUserId());
                marketingMsgList.add(marketingMsg);
            }
            boolean rtn = marketingMsgService.saveBatch(marketingMsgList);
            Assert.isTrue(rtn, "保存活动开奖消息失败。marketingId:" + marketing.getId());

            rtn = marketingService.update(new LambdaUpdateWrapper<Marketing>()
                .set(Marketing::getIsSendLottery, 1)
                .eq(Marketing::getId, marketing.getId()));
            Assert.isTrue(rtn, "更新活动开奖信息发送状态失败。marketingId:" + marketing.getId());
        }

    }


    @Transactional(rollbackFor = Exception.class)
    public void saveMarketingMsg() {
        // 获取开始时间小于等于当前时间且未发开始通知的活动
        Date now = DateUtils.getNowDate();
        List<Marketing> marketingList = marketingService.list(new LambdaQueryWrapper<Marketing>()
            .eq(Marketing::getTriggerStatus, 0)
            .eq(Marketing::getIsOn, MarketingStatusEnum.ON.getValue())
            .eq(Marketing::getIsSend, 0)
            .le(Marketing::getStartTime, now));
        if (CollectionUtils.isEmpty(marketingList)) {
            return;
        }

        // 获取所有参与过活动的普通用户根据用户id去重
        List<MarketingUserCode> userCodeList = marketingUserCodeService.list(new LambdaQueryWrapper<MarketingUserCode>()
            .select(MarketingUserCode::getUserId)
            .eq(MarketingUserCode::getUserType, UserTypeEnum.ORDINARY.getValue())
            .groupBy(MarketingUserCode::getUserId));
        if (CollectionUtils.isEmpty(userCodeList)) {
            return;
        }

        List<Marketing> updateMarketingList = new ArrayList<>();
        List<MarketingMsg> marketingMsgList = new ArrayList<>();
        for (Marketing marketing : marketingList) {
            for (MarketingUserCode marketingUserCode : userCodeList) {

                MarketingMsg marketingMsg = new MarketingMsg();
                marketingMsg.setMarketingId(marketing.getId());
                marketingMsg.setContext(marketing.getTitle());
                marketingMsg.setType(2);
                marketingMsg.setUserId(marketingUserCode.getUserId());

                marketingMsgList.add(marketingMsg);
            }

            Marketing updateMarketing = new Marketing();
            updateMarketing.setIsSend(1);
            updateMarketing.setId(marketing.getId());
            updateMarketingList.add(updateMarketing);

            marketingService.update(new LambdaUpdateWrapper<Marketing>()
                .set(Marketing::getIsSend, 1)
                .eq(Marketing::getId, marketing.getId()));
        }
        if (CollectionUtils.isNotEmpty(marketingMsgList)) {
            boolean rtn = marketingMsgService.saveBatch(marketingMsgList);
            Assert.isTrue(rtn, "保存活动开始通知消息失败");
        }

        // 更新活动通知发送状态
        if (CollectionUtils.isNotEmpty(updateMarketingList)) {
            boolean rtn = marketingService.updateBatchById(updateMarketingList);
            Assert.isTrue(rtn, "更新活动通知发送状态失败");
        }

    }

    /**
     * 增加活动参与的虚拟人数
     */
    public void addFake() {
        // 获取开始时间大于等于当前时间且未结束和开奖的活动
        Date now = DateUtils.getNowDate();
        List<Marketing> marketingList = marketingService.list(new LambdaQueryWrapper<Marketing>()
                .eq(Marketing::getTriggerStatus, 0)
                .eq(Marketing::getIsOn, MarketingStatusEnum.ON.getValue())
                .lt(Marketing::getStartTime, now)
                .gt(Marketing::getEndTime, now));
        if (CollectionUtils.isEmpty(marketingList)) {
            return;
        }

        for (Marketing marketing : marketingList) {
            // 随机加1-10
            marketingService.update(new LambdaUpdateWrapper<Marketing>()
                    .set(Marketing::getFakeNum, marketing.getFakeNum() + RandomUtil.randomInt(1,10))
                    .eq(Marketing::getId, marketing.getId()));
        }
    }

    /**
     * 定时发送一定数量的活动订阅消息
     * @param limit
     * @param type 1开奖消息,2活动开始消息
     */
    public void sendMsgByLimit(Integer limit, Integer type) {

        String lockKey = "MARKETING_SEND_MSG_LIMIT_KEY";
        // 同时只能有一个线程发送活动通知任务,自动续期
        if (!distributedLocker.tryLock(lockKey,0, -1, TimeUnit.SECONDS)) {
            return;
        }

        try {
            List<MarketingMsg> marketingMsgList = new ArrayList<>();
            if (type == 3) {
                // 获取活动通知信息
                marketingMsgList = marketingMsgService.list(new LambdaQueryWrapper<MarketingMsg>()
                    .select(MarketingMsg::getMarketingId, MarketingMsg::getUserId, MarketingMsg::getContext)
                    .eq(MarketingMsg::getType, 3)
                    .groupBy(MarketingMsg::getMarketingId, MarketingMsg::getUserId, MarketingMsg::getContext)
                    .last("limit " + limit));

            } else {
                marketingMsgList = marketingMsgService.list(new LambdaQueryWrapper<MarketingMsg>()
                    .eq(MarketingMsg::getType, type)
                    .last("limit " + limit));
            }


            if (CollectionUtils.isEmpty(marketingMsgList)) {
                return;
            }

            for (MarketingMsg marketingMsg : marketingMsgList) {
                Marketing marketing = new Marketing();
                marketing.setId(marketingMsg.getMarketingId());
                marketing.setTitle(marketingMsg.getContext());
                if (type == 1) {
                    // 发送活动开奖通知
                    wxSubscribeMessage.sendMarketingLottery(marketingMsg.getUserId(), marketing);
                } else if (type == 2){
                    // 发送活动开始通知
                    wxSubscribeMessage.sendMarketingStart(marketingMsg.getUserId(), marketing);
                } else if (type == 3) {
                    // 发送助力成功通知
                    wxSubscribeMessage.sendMarketingHelp(marketingMsg.getUserId(), marketing);
                }
            }

            if (type == 3) {
                List<Long> userIdList = marketingMsgList.stream().map(MarketingMsg::getUserId)
                    .collect(Collectors.toList());
                marketingMsgService.remove(new LambdaUpdateWrapper<MarketingMsg>()
                    .eq(MarketingMsg::getType, 3)
                    .in(MarketingMsg::getUserId, userIdList));
            } else {
                // 删除已发送的消息
                marketingMsgService.removeByIds(marketingMsgList.stream().map(MarketingMsg::getId).collect(Collectors.toList()));
            }

        } catch (Exception e) {
            LogUtil.error(logger, e, "发送活动订阅通知异常");
        } finally {
            distributedLocker.unlock(lockKey);
        }
    }

}
