package com.webchat.service.lottery;

import com.google.common.collect.Lists;
import com.webchat.common.bean.APIPageResponseBean;
import com.webchat.common.constants.LotteryConstants;
import com.webchat.common.constants.WebConstant;
import com.webchat.common.enums.RedisKeyEnum;
import com.webchat.common.util.IDGenerateUtil;
import com.webchat.common.util.JsonUtil;
import com.webchat.domain.vo.request.lottery.LotteryOrderStatusUpdateVO;
import com.webchat.domain.vo.response.UserBaseResponseInfoVO;
import com.webchat.domain.vo.response.lottery.LotteryActivityVO;
import com.webchat.domain.vo.response.lottery.LotteryItemVO;
import com.webchat.domain.vo.response.lottery.LotteryUserOrderVO;
import com.webchat.repository.dao.lottery.ILotteryOrderDAO;
import com.webchat.repository.entity.lottery.LotteryOrderEntity;
import com.webchat.service.RedisService;
import com.webchat.service.UserService;
import com.webchat.service.queue.dto.LotteryOrderQueueDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import javax.persistence.criteria.Predicate;
import java.util.*;
import java.util.stream.Collectors;

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

    @Autowired
    private RedisService redisService;
    @Autowired
    private LotteryCacheService lotteryCacheService;
    @Autowired
    private ILotteryOrderDAO lotteryOrderDAO;
    @Autowired
    private UserService userService;


    /**
     * 处理中奖订单
     *
     * @param lotteryOrderMessage
     */
    public void handleLotteryOrder(LotteryOrderQueueDTO lotteryOrderMessage) {
        LotteryOrderEntity lotteryOrder = new LotteryOrderEntity();
        lotteryOrder.setOrderId(IDGenerateUtil.createId(LotteryConstants.LOTTERY_ORDER_ID_PREFIX));
        lotteryOrder.setUserId(lotteryOrderMessage.getUserId());
        lotteryOrder.setItemId(lotteryOrderMessage.getItemId());
        lotteryOrder.setActivityId(lotteryOrderMessage.getActivityId());
        lotteryOrder.setStatus(LotteryConstants.LotteryOrderStatus.UNCHANGED.getStatus());
        lotteryOrder.setCreateBy(WebConstant.SYSTEM_USER_ID);
        lotteryOrder.setCreateDate(new Date());
        /**
         * 订单入库
         */
        lotteryOrder = lotteryOrderDAO.save(lotteryOrder);
        /**
         * 添加活动累计中奖名单缓存
         */
        this.addLotteryOrderHistoryCache(lotteryOrder);
        /**
         * 添加活动下用户累计中奖名单缓存
         */
        this.addLotteryUserOrderHistoryCache(lotteryOrder);
        /**
         * 处理抽到金币商品的情况
         */
        lotteryOrder = this.handleIntegralItemLuckResult(lotteryOrder);
        /**
         * 添加订单详情缓存
         */
        this.addLotteryOrderDetailCache(lotteryOrder);
    }

    /**
     * 处理抽到虚拟商品的情况
     *
     * @param lotteryOrder
     */
    public LotteryOrderEntity handleIntegralItemLuckResult(LotteryOrderEntity lotteryOrder) {
        log.info("抽奖抽中金币，消息消费内容：{}", JsonUtil.toJsonString(lotteryOrder));
        String activityId = lotteryOrder.getActivityId();
        Long itemId = lotteryOrder.getItemId();
        String userId = lotteryOrder.getUserId();
        LotteryItemVO lotteryItemVO = lotteryCacheService.getLotteryItemVOFromCache(activityId, itemId);
        if (lotteryItemVO == null) {
            return lotteryOrder;
        }
        if (!LotteryConstants.LotteryItemType.INTEGRAL.getType().equals(lotteryItemVO.getType())) {
            // 非猿气值
            log.info("抽奖未抽中金币，消息消费内容：{}", JsonUtil.toJsonString(lotteryOrder));
            return lotteryOrder;
        }
        String name = lotteryItemVO.getName();
        String countStr = name.substring(name.indexOf("X") + 1);
        /**
         * 修改订单状态
         */
        lotteryOrder.setStatus(LotteryConstants.LotteryOrderStatus.EXCHANGED.getStatus());
        lotteryOrder.setUpdateBy(LotteryConstants.SYSTEM);
        lotteryOrder.setUpdateDate(new Date());
        return lotteryOrderDAO.save(lotteryOrder);
    }

    /**
     * 更新订单状态
     *
     * @param lotteryOrderStatusUpdate
     * @param userId
     */
    public void updateLotteryOrderStatus(LotteryOrderStatusUpdateVO lotteryOrderStatusUpdate, String userId) {
        String orderId = lotteryOrderStatusUpdate.getOrderId();
        Integer status = lotteryOrderStatusUpdate.getStatus();
        LotteryOrderEntity lotteryOrder = lotteryOrderDAO.findByOrderId(orderId);
        Assert.isTrue(lotteryOrder != null, "订单不存在");
        lotteryOrder.setStatus(status);
        lotteryOrder.setUpdateBy(userId);
        lotteryOrder.setUpdateDate(new Date());
        lotteryOrderDAO.save(lotteryOrder);
        /**
         * 刷新订单状态缓存
         */
        refreshLotteryOrderDetailCache(orderId);
    }

    /**
     * 查询用户的中奖订单
     *
     * @param activityId
     * @param userId
     * @param size
     * @return
     */
    public List<LotteryUserOrderVO> queryActivityUserOrder(String activityId, String userId, int size) {
        String key = RedisKeyEnum.LOTTERY_ACTIVITY_USER_ORDER_CACHE.getKey(activityId, userId);
        Set<String> orderIdSet = redisService.zreverseRange(key, 0, size);
        if (CollectionUtils.isEmpty(orderIdSet)) {
            return Collections.emptyList();
        }
        String orderKey = RedisKeyEnum.LOTTERY_ACTIVITY_ORDER_DETAIL_CACHE.getKey(activityId);
        List<String> orderCaches = redisService.hmget(orderKey, Lists.newArrayList(orderIdSet));
        if (CollectionUtils.isEmpty(orderCaches)) {
            return Collections.emptyList();
        }
        List<LotteryOrderEntity> lotteryOrderEntities = orderCaches.stream()
                .map(cache -> JsonUtil.fromJson(cache, LotteryOrderEntity.class)).collect(Collectors.toList());
        List<LotteryUserOrderVO> lotteryUserOrderVOList = new ArrayList<>();
        Map<Long, LotteryItemVO> itemTempMap = new HashMap<>();
        for (LotteryOrderEntity lotteryOrder : lotteryOrderEntities) {
            LotteryUserOrderVO lotteryUserOrderVO = new LotteryUserOrderVO();
            LotteryItemVO item;
            Long itemId = lotteryOrder.getItemId();
            if ((item = itemTempMap.get(itemId)) == null) {
                item = lotteryCacheService.getLotteryItemVOFromCache(activityId, itemId);
                itemTempMap.put(itemId, item);
            }
            lotteryUserOrderVO.setOrderId(lotteryOrder.getOrderId());
            lotteryUserOrderVO.setItem(item);
            lotteryUserOrderVO.setOrderStatus(lotteryOrder.getStatus());
            lotteryUserOrderVO.setOrderStatusName(LotteryConstants.getLotteryOrderStatusName(lotteryOrder.getStatus()));
            if (lotteryOrder.getCreateDate() != null) {
                lotteryUserOrderVO.setLuckTime(lotteryOrder.getCreateDate().getTime());
            }
            if (lotteryOrder.getUpdateDate() != null) {
                lotteryUserOrderVO.setExchangeTime(lotteryOrder.getUpdateDate().getTime());
            }
            lotteryUserOrderVOList.add(lotteryUserOrderVO);
        }
        return lotteryUserOrderVOList;
    }

    /**
     * 查询活动下的中奖订单
     *
     * @param activityId
     * @param size
     * @return
     */
    public List<LotteryUserOrderVO> queryActivityOrder(String activityId, int size) {
        String key = RedisKeyEnum.LOTTERY_ACTIVITY_ORDER_CACHE.getKey(activityId);
        Set<String> orderIdSet = redisService.zreverseRange(key, 0, size);
        if (CollectionUtils.isEmpty(orderIdSet)) {
            return Collections.emptyList();
        }
        String orderKey = RedisKeyEnum.LOTTERY_ACTIVITY_ORDER_DETAIL_CACHE.getKey(activityId);
        List<String> orderCaches = redisService.hmget(orderKey, Lists.newArrayList(orderIdSet));
        if (CollectionUtils.isEmpty(orderCaches)) {
            return Collections.emptyList();
        }
        List<LotteryOrderEntity> lotteryOrderEntities = orderCaches.stream()
                .map(cache -> JsonUtil.fromJson(cache, LotteryOrderEntity.class)).collect(Collectors.toList());

        List<String> userIdList = lotteryOrderEntities.stream().map(LotteryOrderEntity::getUserId).collect(Collectors.toList());
        Map<String, UserBaseResponseInfoVO> luckUserMap = userService.batchGetUserInfoFromCache(userIdList);
        List<LotteryUserOrderVO> lotteryUserOrderVOList = new ArrayList<>();
        Map<Long, LotteryItemVO> itemTempMap = new HashMap<>();
        for (LotteryOrderEntity lotteryOrder : lotteryOrderEntities) {
            LotteryUserOrderVO lotteryUserOrderVO = new LotteryUserOrderVO();
            LotteryItemVO item;
            Long itemId = lotteryOrder.getItemId();
            if ((item = itemTempMap.get(itemId)) == null) {
                item = lotteryCacheService.getLotteryItemVOFromCache(activityId, itemId);
                itemTempMap.put(itemId, item);
            }
            lotteryUserOrderVO.setUser(luckUserMap.get(lotteryOrder.getUserId()));
            lotteryUserOrderVO.setItem(item);
            lotteryUserOrderVO.setOrderStatus(lotteryOrder.getStatus());
            lotteryUserOrderVO.setOrderStatusName(LotteryConstants.getLotteryOrderStatusName(lotteryOrder.getStatus()));
            if (lotteryOrder.getCreateDate() != null) {
                lotteryUserOrderVO.setLuckTime(lotteryOrder.getCreateDate().getTime());
            }
            if (lotteryOrder.getUpdateDate() != null) {
                lotteryUserOrderVO.setExchangeTime(lotteryOrder.getUpdateDate().getTime());
            }
            lotteryUserOrderVOList.add(lotteryUserOrderVO);
        }
        return lotteryUserOrderVOList;
    }

    /**
     * 订单检索
     *
     * @return
     */
    public APIPageResponseBean<List<LotteryUserOrderVO>> pageList(String activityId, String orderId, Integer status, int pageNo, int pageSize) {
        Pageable pageable = PageRequest.of(pageNo - 1, pageSize, Sort.by(Sort.Order.desc("id")));
        Specification<LotteryOrderEntity> specification = ((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            if (StringUtils.isNotBlank(activityId)) {
                predicates.add(criteriaBuilder.equal(root.get("activityId").as(String.class), activityId));
            }
            if (StringUtils.isNotBlank(orderId)) {
                predicates.add(criteriaBuilder.equal(root.get("activityId").as(String.class), orderId));
            }
            if (status != null) {
                predicates.add(criteriaBuilder.equal(root.get("status").as(Integer.class), status));
            }
            Predicate[] pre = new Predicate[predicates.size()];
            criteriaQuery.where(predicates.toArray(pre));
            return criteriaQuery.getRestriction();
        });
        Page<LotteryOrderEntity> lotteryOrderEntityPage = lotteryOrderDAO.findAll(specification, pageable);
        if (CollectionUtils.isEmpty(lotteryOrderEntityPage.getContent())) {
            return APIPageResponseBean.success(pageNo, pageSize, lotteryOrderEntityPage.getTotalPages(), Collections.emptyList());
        }
        List<LotteryOrderEntity> lotteryOrderEntities = lotteryOrderEntityPage.getContent();
        List<String> userIdList = lotteryOrderEntities.stream().map(LotteryOrderEntity::getUserId).collect(Collectors.toList());
        Map<String, UserBaseResponseInfoVO> luckUserMap = userService.batchGetUserInfoFromCache(userIdList);
        List<LotteryUserOrderVO> lotteryUserOrderVOList = new ArrayList<>();
        Map<Long, LotteryItemVO> itemTempMap = new HashMap<>();
        Map<String, LotteryActivityVO> activityTempMap = new HashMap<>();
        for (LotteryOrderEntity lotteryOrder : lotteryOrderEntities) {
            LotteryUserOrderVO lotteryUserOrderVO = new LotteryUserOrderVO();
            LotteryItemVO item;
            LotteryActivityVO activity;
            Long itemId = lotteryOrder.getItemId();
            String activityIdStr = lotteryOrder.getActivityId();
            if ((item = itemTempMap.get(itemId)) == null) {
                item = lotteryCacheService.getLotteryItemVOFromCache(activityIdStr, itemId);
                itemTempMap.put(itemId, item);
            }
            if ((activity = activityTempMap.get(activityIdStr)) == null) {
                activity = lotteryCacheService.getLotteryActivityDetailFromCache(activityIdStr);
                activityTempMap.put(activityIdStr, activity);
            }
            lotteryUserOrderVO.setUser(luckUserMap.get(lotteryOrder.getUserId()));
            lotteryUserOrderVO.setOrderId(lotteryOrder.getOrderId());
            lotteryUserOrderVO.setItem(item);
            lotteryUserOrderVO.setActivity(activity);
            lotteryUserOrderVO.setOrderStatus(lotteryOrder.getStatus());
            lotteryUserOrderVO.setOrderStatusName(LotteryConstants.getLotteryOrderStatusName(lotteryOrder.getStatus()));
            if (lotteryOrder.getCreateDate() != null) {
                lotteryUserOrderVO.setLuckTime(lotteryOrder.getCreateDate().getTime());
            }
            if (lotteryOrder.getUpdateDate() != null) {
                lotteryUserOrderVO.setExchangeTime(lotteryOrder.getUpdateDate().getTime());
            }
            lotteryUserOrderVOList.add(lotteryUserOrderVO);
        }
        return APIPageResponseBean.success(pageNo, pageSize, lotteryOrderEntityPage.getTotalElements(), lotteryUserOrderVOList);
    }


    public void addLotteryOrderHistoryCache(LotteryOrderEntity lotteryOrder) {
        String key = RedisKeyEnum.LOTTERY_ACTIVITY_ORDER_CACHE.getKey(lotteryOrder.getActivityId());
        redisService.zadd(key, lotteryOrder.getOrderId(), lotteryOrder.getId());
    }

    public void addLotteryOrderDetailCache(LotteryOrderEntity lotteryOrder) {
        RedisKeyEnum keyEnum = RedisKeyEnum.LOTTERY_ACTIVITY_ORDER_DETAIL_CACHE;
        String key = keyEnum.getKey(lotteryOrder.getActivityId());
        redisService.hset(key, lotteryOrder.getOrderId(), JsonUtil.toJsonString(lotteryOrder), keyEnum.getExpireTime());
    }

    public void addLotteryUserOrderHistoryCache(LotteryOrderEntity lotteryOrder) {
        String key = RedisKeyEnum.LOTTERY_ACTIVITY_USER_ORDER_CACHE.getKey(lotteryOrder.getActivityId(), lotteryOrder.getUserId());
        redisService.zadd(key, lotteryOrder.getOrderId(), lotteryOrder.getId());
    }

    /**
     * 刷新订单详情缓存
     *
     * @param orderId
     */
    public void refreshLotteryOrderDetailCache(String orderId) {
        LotteryOrderEntity orderEntity = lotteryOrderDAO.findByOrderId(orderId);
        this.addLotteryOrderDetailCache(orderEntity);
    }
}
