package com.webchat.service.lottery;

import com.fasterxml.jackson.core.type.TypeReference;
import com.webchat.common.constants.LotteryConstants;
import com.webchat.common.enums.RedisKeyEnum;
import com.webchat.common.util.JsonUtil;
import com.webchat.domain.vo.response.lottery.LotteryActivityBaseVO;
import com.webchat.domain.vo.response.lottery.LotteryActivityVO;
import com.webchat.domain.vo.response.lottery.LotteryItemVO;
import com.webchat.repository.dao.lottery.ILotteryActivityDAO;
import com.webchat.repository.dao.lottery.ILotteryItemDAO;
import com.webchat.repository.entity.lottery.LotteryActivityEntity;
import com.webchat.repository.entity.lottery.LotteryItemEntity;
import com.webchat.service.RedisService;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.DefaultTypedTuple;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

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

    @Autowired
    private ILotteryActivityDAO lotteryActivityDAO;
    @Autowired
    private ILotteryItemDAO lotteryItemDAO;
    @Autowired
    private RedisService redisService;

    /**
     * 刷新活动缓存，在活动保存后
     *
     * @param activityId
     */
    @Async
    public LotteryActivityVO refreshLotteryActivityCacheById(String activityId) {
        LotteryActivityEntity entity = lotteryActivityDAO.findByActivityId(activityId);
        return this.refreshLotteryActivityCacheByEntity(entity);
    }

    @Async
    public LotteryActivityVO refreshLotteryActivityCacheByEntity(LotteryActivityEntity entity) {
        if (entity == null) {
            return null;
        }
        String lotteryActivityId = entity.getActivityId();
        List<LotteryItemVO> lotteryItemVOList = this.getLotteryItemVoListFromDB(lotteryActivityId);
        /**
         * 刷新奖品库存缓存
         */
        this.refreshLotteryItemStockCache(lotteryActivityId, lotteryItemVOList);
        /**
         * 刷新抽奖奖品详情缓存
         */
        this.refreshLotteryItemDetailCache(lotteryActivityId, lotteryItemVOList, null);
        /**
         * 刷新活动列表缓存
         */
        this.refreshLotteryActivityListCache();
        /**
         * 刷新历史抽奖活动缓存
         */
        this.refreshLotteryActivityHistoryCache();
        /**
         * 刷新最新一期活动id缓存
         */
        this.refreshLastLotteryActivityIdCache();
        /**
         * 刷新活动详情缓存
         */
        return refreshLotteryActivityDetailCache(entity, lotteryItemVOList);
    }

    /**
     * 查询最新一期活动id
     *
     * @return
     */
    public String getLastLotteryActivityIdFromCache() {
        String key = RedisKeyEnum.LOTTERY_ACTIVITY_LAST_ID_CACHE.getKey();
        String activityId = redisService.get(key);
        if (StringUtils.isNotBlank(activityId)) {
            return activityId;
        }
        return this.refreshLastLotteryActivityIdCache();
    }

    /**
     * 刷新最新一期活动ID缓存
     *
     * @return
     */
    private String refreshLastLotteryActivityIdCache() {
        String key = RedisKeyEnum.LOTTERY_ACTIVITY_LAST_ID_CACHE.getKey();
        String activityId = lotteryActivityDAO.findByLastLotteryActivity();
        redisService.remove(key);
        if (StringUtils.isNotBlank(activityId)) {
            redisService.set(key, activityId, RedisKeyEnum.LOTTERY_ACTIVITY_LAST_ID_CACHE.getExpireTime());
        }
        return activityId;
    }

    /**
     * 刷新历史抽奖活动缓存
     */
    private void refreshLotteryActivityHistoryCache() {
        List<LotteryActivityEntity> lotteryActivityEntities =
                lotteryActivityDAO.findAllByStatusNotOrderByCreateDateDesc(
                        LotteryConstants.LotteryActivityStatus.DELETED.getStatus());
        String key = RedisKeyEnum.LOTTERY_ACTIVITY_HISTORY_CACHE.getKey();
        if (CollectionUtils.isEmpty(lotteryActivityEntities)) {
            redisService.remove(key);
            return;
        }
        Set<ZSetOperations.TypedTuple<String>> tuples = lotteryActivityEntities.stream().map(
                activity -> {
                    String value = String.valueOf(activity.getActivityId());
                    Double score = Double.valueOf(activity.getId());
                    return new DefaultTypedTuple<>(value, score);
                }).collect(Collectors.toSet());
        redisService.zadd(key, tuples, RedisKeyEnum.LOTTERY_ACTIVITY_HISTORY_CACHE.getExpireTime());
    }

    /**
     * 查询历史活动
     * @param lastId
     * @param size
     * @return
     */
    public List<LotteryActivityBaseVO> queryHistoryActivityFromCache(Long lastId, int size) {
        String key = RedisKeyEnum.LOTTERY_ACTIVITY_HISTORY_CACHE.getKey();
        Long maxScore = lastId == null ? Long.MAX_VALUE : lastId;
        Set<String> activityIdStrSet = redisService.zreverseRangeByScore(key, maxScore, 0, size);
        if (CollectionUtils.isEmpty(activityIdStrSet)) {
            return Collections.emptyList();
        }
        return activityIdStrSet.stream().map(id -> {
            return this.getLotteryActivityDetailFromCache(id);
        }).collect(Collectors.toList());
    }

    /**
     * 刷新抽奖活动详情缓存
     *
     * @param entity
     * @param lotteryItemVOList
     * @return
     */
    private LotteryActivityVO refreshLotteryActivityDetailCache(LotteryActivityEntity entity,
                                                                List<LotteryItemVO> lotteryItemVOList) {
        String lotteryActivityId = entity.getActivityId();
        RedisKeyEnum lotteryActivityCacheKey = RedisKeyEnum.LOTTERY_ACTIVITY_DETAIL_CACHE;
        String key = lotteryActivityCacheKey.getKey(lotteryActivityId);
        LotteryActivityVO lotteryActivityVO = this.covertLotteryActivityVO(entity);
        lotteryActivityVO.setItems(lotteryItemVOList);
        lotteryActivityVO.setStatusName(LotteryConstants.getLotteryActivityStatusName(entity.getStatus()));
        redisService.set(key, JsonUtil.toJsonString(lotteryActivityVO), lotteryActivityCacheKey.getExpireTime());
        return lotteryActivityVO;
    }

    /**
     * 刷新抽奖活动奖品库存余量缓存
     *
     * @param lotteryActivityId
     * @param lotteryItemVOList
     * @return MAP<itemId, stock>
     */
    public Map<Long, Integer> refreshLotteryItemStockCache(String lotteryActivityId,
                                                           List<LotteryItemVO> lotteryItemVOList) {
        RedisKeyEnum lotteryActivityCacheKey = RedisKeyEnum.LOTTERY_ACTIVITY_ITEM_STOCK_CACHE;
        String key = lotteryActivityCacheKey.getKey(lotteryActivityId);
        if (CollectionUtils.isEmpty(lotteryItemVOList)) {
            redisService.remove(key);
            return Collections.emptyMap();
        }
        Map<Long, Integer> lotteryItemStockMap = new ConcurrentHashMap<>();
        lotteryItemVOList.stream().forEach(item -> {
            lotteryItemStockMap.put(item.getId(), item.getStock());
        });
        redisService.set(key, JsonUtil.toJsonString(lotteryItemStockMap), lotteryActivityCacheKey.getExpireTime());
        return lotteryItemStockMap;
    }

    /**
     * 刷新抽奖奖品详情缓存
     *
     * @param lotteryActivityId
     * @param lotteryItemVOList
     */
    public void refreshLotteryItemDetailCache(String lotteryActivityId, List<LotteryItemVO> lotteryItemVOList) {
        this.refreshLotteryItemDetailCache(lotteryActivityId, lotteryItemVOList, null);
    }

    public LotteryItemVO refreshLotteryItemDetailCache(String lotteryActivityId, List<LotteryItemVO> lotteryItemVOList,
                                                       Long itemId) {
        RedisKeyEnum lotteryActivityItemDetailKey = RedisKeyEnum.LOTTERY_ACTIVITY_ITEM_DETAIL_CACHE;
        String key = lotteryActivityItemDetailKey.getKey(lotteryActivityId);
        if (CollectionUtils.isEmpty(lotteryItemVOList)) {
            redisService.remove(key);
            return null;
        }
        LotteryItemVO backItem = null;
        for (LotteryItemVO lotteryItem : lotteryItemVOList) {
            redisService.hset(key, lotteryItem.getId().toString(),
                    JsonUtil.toJsonString(lotteryItem), lotteryActivityItemDetailKey.getExpireTime());
            if (itemId != null && itemId.equals(lotteryItem.getId())) {
                backItem = lotteryItem;
            }
        }
        return backItem;
    }

    /**
     * 查询抽奖商品信息
     *
     * @param lotteryActivityId
     * @param itemId
     * @return
     */
    public LotteryItemVO getLotteryItemVOFromCache(String lotteryActivityId, Long itemId) {
        RedisKeyEnum lotteryActivityItemDetailKey = RedisKeyEnum.LOTTERY_ACTIVITY_ITEM_DETAIL_CACHE;
        String key = lotteryActivityItemDetailKey.getKey(lotteryActivityId);
        String cache = redisService.hget(key, String.valueOf(itemId));
        if (StringUtils.isNotBlank(cache)) {
            return JsonUtil.fromJson(cache, LotteryItemVO.class);
        }
        List<LotteryItemVO> lotteryItemVOList = this.getLotteryItemVoListFromDB(lotteryActivityId);
        return refreshLotteryItemDetailCache(lotteryActivityId, lotteryItemVOList, itemId);
    }

    /**
     * 查询抽奖活动奖品库存数据
     *
     * @param lotteryActivityId
     * @return
     */
    public Map<Long, Integer> getLotteryItemStockFromCache(String lotteryActivityId) {
        RedisKeyEnum lotteryActivityCacheKey = RedisKeyEnum.LOTTERY_ACTIVITY_ITEM_STOCK_CACHE;
        String key = lotteryActivityCacheKey.getKey(lotteryActivityId);
        String stockCache = redisService.get(key);
        if (StringUtils.isNotBlank(stockCache)) {
            return JsonUtil.fromJson(stockCache, new TypeReference<ConcurrentHashMap<Long, Integer>>() {
            });
        }
        return Collections.emptyMap();
    }

    /**
     * 抽奖出结果后扣减库存
     *
     * @param lotteryActivityId
     * @param itemId
     */
    public void deductItemStock(String lotteryActivityId, Long itemId) {
        // 这里目前不是原子性操作，在高并发情况下可能会有问题
        RedisKeyEnum lotteryActivityCacheKey = RedisKeyEnum.LOTTERY_ACTIVITY_ITEM_STOCK_CACHE;
        String key = lotteryActivityCacheKey.getKey(lotteryActivityId);
        Map<Long, Integer> stockMap = this.getLotteryItemStockFromCache(lotteryActivityId);
        Assert.isTrue(MapUtils.isNotEmpty(stockMap), "库存为空");
        stockMap.put(itemId, stockMap.get(itemId) - 1);
        redisService.set(key, JsonUtil.toJsonString(stockMap), lotteryActivityCacheKey.getExpireTime());
    }

    /**
     * 查询活动下的奖品列表
     *
     * @param activityId
     * @return
     */
    private List<LotteryItemVO> getLotteryItemVoListFromDB(String activityId) {
        List<LotteryItemEntity> lotteryItemEntities = lotteryItemDAO.findAllByActivityIdOrderBySlotAsc(activityId);
        if (CollectionUtils.isEmpty(lotteryItemEntities)) {
            return Collections.emptyList();
        }
        return lotteryItemEntities.stream().map(item -> {
            LotteryItemVO lotteryItemVO = new LotteryItemVO();
            BeanUtils.copyProperties(item, lotteryItemVO);
            lotteryItemVO.setTypeName(LotteryConstants.getLotteryActivityItemTypeName(item.getType()));
            return lotteryItemVO;
        }).collect(Collectors.toList());
    }

    private LotteryActivityVO covertLotteryActivityVO(LotteryActivityEntity entity) {
        LotteryActivityVO lotteryActivityVO = new LotteryActivityVO();
        BeanUtils.copyProperties(entity, lotteryActivityVO);
        lotteryActivityVO.setCreateTime(entity.getCreateDate().getTime());
        return lotteryActivityVO;
    }

    /**
     * 查询最新一期活动
     *
     * @return
     */
    public LotteryActivityVO getLastLotteryActivityDetailFromCache() {
        String activityId = this.getLastLotteryActivityIdFromCache();
        if (StringUtils.isBlank(activityId)) {
            return null;
        }
        return this.getLotteryActivityDetailFromCache(activityId);
    }

    /**
     * 查询抽奖活动详情
     *
     * @param lotteryActivityId
     * @return
     */
    public LotteryActivityVO getLotteryActivityDetailFromCache(String lotteryActivityId) {
        RedisKeyEnum lotteryActivityCacheKey = RedisKeyEnum.LOTTERY_ACTIVITY_DETAIL_CACHE;
        String key = lotteryActivityCacheKey.getKey(lotteryActivityId);
        String lotteryActivityCache = redisService.get(key);
        LotteryActivityVO lotteryActivity;
        if (StringUtils.isNotBlank(lotteryActivityCache)) {
            lotteryActivity = JsonUtil.fromJson(lotteryActivityCache, LotteryActivityVO.class);
        } else {
            lotteryActivity = this.refreshLotteryActivityCacheById(lotteryActivityId);
        }
        return lotteryActivity;
    }

    /**
     * 刷新抽奖活动缓存
     */
    private void refreshLotteryActivityListCache() {

    }
}
