package com.webchat.service.lottery;

import com.webchat.common.constants.LotteryConstants;
import com.webchat.common.constants.WebConstant;
import com.webchat.common.enums.WalletTransEventEnum;
import com.webchat.common.exception.BusinessException;
import com.webchat.common.util.ListUtil;
import com.webchat.domain.vo.request.lottery.LotteryActivityStatusUpdateVO;
import com.webchat.domain.vo.response.lottery.LotteryActivityVO;
import com.webchat.domain.vo.response.lottery.LotteryDrawBulkResponseVO;
import com.webchat.domain.vo.response.lottery.LotteryDrawOneResponseVO;
import com.webchat.service.UserWalletService;
import com.webchat.service.queue.LotteryOrderQueue;
import com.webchat.service.queue.dto.LotteryOrderQueueDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.concurrent.ThreadLocalRandom;

/**
 * @Author 程序员七七
 * @webSite https://www.coderutil.com
 * @Date 2022/11/16 23:40
 * @description
 */
@Slf4j
@Service
public class LotteryService {

    @Autowired
    private LotteryOrderQueue lotteryOrderQueue;
    @Autowired
    private LotteryOrderService lotteryOrderService;
    @Autowired
    private LotteryActivityService lotteryActivityService;
    @Autowired
    private LotteryItemService lotteryItemService;
    @Autowired
    private LotteryCacheService lotteryCacheService;
    @Autowired
    private UserWalletService userWalletService;

    /**
     * 幸运抽奖
     *
     * @param lotteryActivityId 抽奖活动id
     * @param userId            当前登录用户id
     * @return 中奖商品id
     */
    public LotteryDrawOneResponseVO luckDraw(String lotteryActivityId, String userId) {

        // 查询活动配置
        LotteryActivityVO activity = lotteryActivityService.getLotteryActivityDetailFromCache(lotteryActivityId);
        /**
         * 校验活动状态及用户钱包余额
         */
        this.validateUserJoinLotteryCondition(userId, activity, 1);
        /**
         * 1、抽奖
         */
        Long lotteryItemId = this.doLotteryDraw(lotteryActivityId);
        /**
         * 扣减钱包余额
         */
        boolean free = activity.getMoney() == null || activity.getMoney().compareTo(new BigDecimal("0")) == 0;
        BigDecimal lastWalletBalance = null;
        if (!free) {
            // 实时钱包余额扣减
            try {
                lastWalletBalance = userWalletService.doTrans(WalletTransEventEnum.JOIN_LOTTERY,
                        userId,
                        WebConstant.SYSTEM_WALLET_ID, activity.getMoney());
            } catch (Exception e) {
                // 钱包扣除异常
                // rollback
                // TODO
            }
        }
        /**
         * 2、处理中奖订单
         */
        this.handleLotteryOrder(userId, lotteryActivityId, lotteryItemId);

        return LotteryDrawOneResponseVO.of(lastWalletBalance, lotteryItemId);
    }

    /**
     * 校验是否满足抽奖要求
     *
     * @param userId
     * @param activity
     */
    private void validateUserJoinLotteryCondition(String userId, LotteryActivityVO activity, int times) {
        Assert.notNull(activity, "活动不存在");
        Assert.isTrue(LotteryConstants.LotteryActivityStatus.RUNNING.getStatus().equals(activity.getStatus()), "非进行中活动！");
        BigDecimal money = activity.getMoney();
        boolean free = money == null || money.compareTo(new BigDecimal("0")) == 0;
        if (free) {
            // 免费抽奖
            return;
        }
        BigDecimal balance = userWalletService.getUserWalletBalanceFromCache(userId);
        Assert.isTrue(balance.compareTo(money.multiply(new BigDecimal(times))) >=0, "钱包余额不足！");
    }

    /**
     * 多次抽奖
     *
     * @param lotteryActivityId
     * @param userId
     * @param times
     * @return
     */
    public LotteryDrawBulkResponseVO luckDraw(String lotteryActivityId, String userId, int times) {
        // 查询活动配置
        LotteryActivityVO activity = lotteryActivityService.getLotteryActivityDetailFromCache(lotteryActivityId);
        /**
         * 校验活动状态及用户钱包余额
         */
        this.validateUserJoinLotteryCondition(userId, activity, times);
        List<Long> result = new Vector<>();
        /**
         * 这里下简单实现，这种做法不是很好，并发量大下可能容易出问题
         */
        for (int i = 0; i < times; i++) {
            Long lotteryItemId = this.doLotteryDraw(lotteryActivityId);
            this.handleLotteryOrder(userId, lotteryActivityId, lotteryItemId);
            result.add(lotteryItemId);
        }
        /**
         * 扣减钱包余额
         */
        boolean free = activity.getMoney() == null || activity.getMoney().compareTo(new BigDecimal("0")) == 0;
        BigDecimal lastWalletBalance = null;
        if (!free) {
            // 实时钱包余额扣减
            try {
                lastWalletBalance = userWalletService.doTrans(WalletTransEventEnum.JOIN_LOTTERY,
                        userId,
                        WebConstant.SYSTEM_WALLET_ID,
                        activity.getMoney().multiply(new BigDecimal(times)));
            } catch (Exception e) {
                // 钱包扣除异常
                // rollback
                // TODO
            }
        }
        return LotteryDrawBulkResponseVO.of(lastWalletBalance, result);
    }

    /**
     * 抽奖，返回抽中奖品的id
     *
     * @param lotteryActivityId
     * @return
     */
    private Long doLotteryDraw(String lotteryActivityId) {

        /**
         * 1、抽奖
         */
        return this.executeLotteryDraw(lotteryActivityId);
    }

    /**
     * 执行抽奖
     *
     * @param lotteryActivityId
     * @return
     */
    private synchronized Long executeLotteryDraw(String lotteryActivityId) {
        /**
         * 获取奖品库存
         */
        Map<Long, Integer> stockMap = lotteryCacheService.getLotteryItemStockFromCache(lotteryActivityId);
        /**
         * 构造奖品池
         */
        List<Long> itemPool = new ArrayList<>();
        for (Map.Entry<Long, Integer> entry : stockMap.entrySet()) {
            Long itemId = entry.getKey();
            int stock = entry.getValue();
            if (stock == 0) {
                continue;
            }
            for (int i = 0; i < stock; i++) {
                itemPool.add(itemId);
            }
            // 打散奖池
            ListUtil.shuffle(itemPool);
        }

        /**
         * 奖品已抽完，自动结束活动
         */
        if (itemPool.size() == 0) {
            LotteryActivityStatusUpdateVO lotteryActivityStatusUpdate = new LotteryActivityStatusUpdateVO();
            lotteryActivityStatusUpdate.setActivityId(lotteryActivityId);
            lotteryActivityStatusUpdate.setStatus(LotteryConstants.LotteryActivityStatus.CLOSED.getStatus());
            lotteryActivityService.updateLotteryActivityStatus(lotteryActivityStatusUpdate, LotteryConstants.SYSTEM);
            throw new BusinessException("本期奖品抽完。活动已结束！");
        }

        /**
         * 随机抽奖
         */
        int luckIndex = this.randomLotteryIndex(itemPool.size());
        Long luckItemId = itemPool.get(luckIndex);
        /**
         * 扣减库存
         */
        lotteryCacheService.deductItemStock(lotteryActivityId, luckItemId);
        return luckItemId;
    }

    /**
     * 生成抽奖索引
     *
     * @param maxIndex
     * @return
     */
    public int randomLotteryIndex(int maxIndex) {
        ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();
        return threadLocalRandom.nextInt(0, maxIndex);
    }

    /**
     * 校验是否满足抽奖条件
     *
     * @param lotteryActivityId
     * @param userId
     * @return 满足抽奖条件返回抽奖需要消耗的金币个数
     */
    private int validateLotteryDrawCondition(String lotteryActivityId, String userId, int times) {

        // 不需要检验元气
        return Integer.MAX_VALUE;
    }

    /**
     * 校验抽奖活动
     */
    private LotteryActivityVO validateLotteryActivity(String lotteryActivityId) {
        LotteryActivityVO lotteryActivity = lotteryCacheService.getLotteryActivityDetailFromCache(lotteryActivityId);
        Assert.isTrue(lotteryActivity != null, "活动不存在！");
        // 校验活动是否进行中
        lotteryActivity.validateRunningStatus();
        // 校验活动积分配置
        lotteryActivity.validateIntegral();
        return lotteryActivity;
    }

    /**
     * 中奖结果加入队列，排队处理
     *
     * @param lotteryActivityId
     * @param userId
     * @param itemId
     */
    private void handleLotteryOrder(String userId, String lotteryActivityId, Long itemId) {
        LotteryOrderQueueDTO lotteryOrderQueueDTO = new LotteryOrderQueueDTO();
        lotteryOrderQueueDTO.setActivityId(lotteryActivityId);
        lotteryOrderQueueDTO.setItemId(itemId);
        lotteryOrderQueueDTO.setUserId(userId);
        // 消息队列处理订单
        lotteryOrderQueue.submit(lotteryOrderQueueDTO);
    }
}
