package com.qs.mp.user.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.qs.mp.admin.domain.Ticket;
import com.qs.mp.admin.domain.TicketAwards;
import com.qs.mp.admin.domain.TicketAwardsPrize;
import com.qs.mp.admin.domain.dto.TicketDrawNumDTO;
import com.qs.mp.admin.domain.vo.TicketListVO;
import com.qs.mp.admin.service.*;
import com.qs.mp.common.core.domain.AjaxResult;
import com.qs.mp.common.core.redis.DistributedLocker;
import com.qs.mp.common.enums.*;
import com.qs.mp.common.exception.ServiceException;
import com.qs.mp.common.utils.LogUtil;
import com.qs.mp.common.utils.StringUtils;
import com.qs.mp.framework.redis.RedisLockKey;
import com.qs.mp.system.service.id.BizIdGenerator;
import com.qs.mp.user.domain.UserHitPrize;
import com.qs.mp.user.domain.UserTicketOrderItem;
import com.qs.mp.user.domain.param.TicketAutoCashPrizeParam;
import com.qs.mp.user.domain.vo.TicketHitPrizeBarrageVO;
import com.qs.mp.user.domain.vo.UserHitPrizeDetailVO;
import com.qs.mp.user.mapper.UserHitPrizeMapper;
import com.qs.mp.user.service.IUserCoinService;
import com.qs.mp.user.service.IUserHitPrizeService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qs.mp.user.service.IUserPrizeStorageService;
import com.qs.mp.user.service.IUserTicketOrderItemService;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

/**
 * <p>
 * 用户中奖记录 服务实现类
 * </p>
 *
 * @author quanshu
 * @since 2022-03-07
 */
@Service
public class UserHitPrizeServiceImpl extends ServiceImpl<UserHitPrizeMapper, UserHitPrize> implements IUserHitPrizeService {

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

    @Autowired
    private IUserTicketOrderItemService userTicketOrderItemService;

    @Autowired
    private ITicketAwardsPrizeService ticketAwardsPrizeService;

    @Autowired
    private IUserPrizeStorageService userPrizeStorageService;

    @Autowired
    private IUserCoinService userCoinService;

    @Autowired
    private ICouponService couponService;

    @Autowired
    private ITicketAwardsService ticketAwardsService;

    @Autowired
    private ITicketService ticketService;

    @Autowired
    private BizIdGenerator bizIdGenerator;

    @Autowired
    private ICouponPkgService couponPkgService;

    @Autowired
    private IUserHitPrizeService userHitPrizeService;

    @Autowired
    private DistributedLocker distributedLocker;

    @Override
    public List<TicketAwardsPrize> listPrize(Ticket ticket, Long userId) {
        UserHitPrize userHitPrize = getOne(new LambdaQueryWrapper<UserHitPrize>().eq(UserHitPrize::getTicketId, ticket.getTicketId()));
        UserTicketOrderItem orderItem = userTicketOrderItemService.queryFinishedOrderItem(userId, ticket.getTicketId());
        if (null == orderItem) {
            LogUtil.error(logger, "查询盲票的奖品信息，盲票订单不存在。ticketId:{0}, userId:{1}",
                    new Object[]{ticket.getTicketId(), userId});
            throw new ServiceException("盲票订单不存在");
        }

        if (null == userHitPrize) {
            List<TicketDrawNumDTO> drawNumDTOList = JSONObject.parseArray(ticket.getDrawNum(), TicketDrawNumDTO.class);
            for (TicketDrawNumDTO ticketDrawNumDTO : drawNumDTOList) {
                if (ticketDrawNumDTO.getNum() == ticket.getPlainLuckyNum()) {
                    userHitPrize = new UserHitPrize();
                    userHitPrize.setId(bizIdGenerator.newIdWithUidSharding(String.valueOf(userId)));
                    userHitPrize.setUserId(userId);
                    userHitPrize.setOrderId(orderItem.getOrderId());
                    userHitPrize.setOrderItemId(orderItem.getItemId());
                    userHitPrize.setTicketId(ticket.getTicketId());
                    TicketAwards ticketAwards = ticketAwardsService.getOne(new LambdaQueryWrapper<TicketAwards>()
                            .eq(TicketAwards::getBoxId, ticket.getBoxId()).eq(TicketAwards::getName, ticketDrawNumDTO.getName()));
                    userHitPrize.setAwardsId(ticketAwards.getAwardsId());
                    // 奖品ID等用户选择后填入
//          userHitPrize.setPrizeId();
                    save(userHitPrize);
                }
            }
        }
        QueryWrapper<TicketAwardsPrize> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("t1.awards_id", userHitPrize.getAwardsId());
        queryWrapper.orderByDesc("t1.sort_weight", "t1.value");
        List<TicketAwardsPrize> awardsPrizes = ticketAwardsPrizeService.listByQueryWrapper(queryWrapper);
        return awardsPrizes;
    }

    @Override
    public UserHitPrizeDetailVO autoCashPrize(Long userId, TicketAutoCashPrizeParam param) {

        UserHitPrizeDetailVO userHitPrizeDetailVO = new UserHitPrizeDetailVO();

        // 封装出参对象
        QueryWrapper<TicketAwardsPrize> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("t1.box_id", param.getBoxId());
        List<TicketAwardsPrize> ticketAwardsPrizeList = ticketAwardsPrizeService.listByQueryWrapper(queryWrapper);


        List<List<TicketAwardsPrize>> awardsPrizeList =  new ArrayList<>();
        int index = 0;
        for (int i = 0; i < 16; i++) {
            List<TicketAwardsPrize> list = new ArrayList<>();
            for (int j = 0; j < 3; j++) {
                TicketAwardsPrize ticketAwardsPrize = new TicketAwardsPrize();
                BeanUtils.copyProperties(ticketAwardsPrizeList.get(index),ticketAwardsPrize);
                list.add(ticketAwardsPrize);
                if (index == ticketAwardsPrizeList.size() - 1) {
                    index = 0;
                    continue;
                }
                index++;
            }
            awardsPrizeList.add(list);

        }
        userHitPrizeDetailVO.setTicketAwardsPrizeList(awardsPrizeList);
        if (param.getIsTry() == 1) {
            List<TicketAwards> awardsList = ticketAwardsService.list(new LambdaQueryWrapper<TicketAwards>()
                    .eq(TicketAwards::getBoxId,param.getBoxId())
                    .orderByAsc(TicketAwards::getSort));
            QueryWrapper<TicketAwardsPrize> prizeListQueryWrapper = new QueryWrapper<TicketAwardsPrize>();
                    prizeListQueryWrapper.eq("t1.box_id", param.getBoxId());
            if (awardsList.size() > 1) {
                prizeListQueryWrapper.in("t1.awards_id", awardsList.get(0).getAwardsId(), awardsList.get(1).getAwardsId());
            }else {
                prizeListQueryWrapper.in("t1.awards_id", awardsList.get(0).getAwardsId());
            }
            List<TicketAwardsPrize> prizeList = ticketAwardsPrizeService.listByQueryWrapper(prizeListQueryWrapper);
            TicketAwardsPrize ticketAwardsPrize = prizeList.get(new Random().nextInt(prizeList.size()));
            userHitPrizeDetailVO.setPrizeId(ticketAwardsPrize.getPrizeId());
            userHitPrizeDetailVO.setPicUrl(ticketAwardsPrize.getPicUrl());
            userHitPrizeDetailVO.setTitle(ticketAwardsPrize.getTitle());
            userHitPrizeDetailVO.setValue(ticketAwardsPrize.getValue());
            userHitPrizeDetailVO.setPrizeType(ticketAwardsPrize.getPrizeType());
            userHitPrizeDetailVO.setRefId(ticketAwardsPrize.getRefId());
        }else {

            TicketListVO ticketListVO = userTicketOrderItemService.queryTicketVO(userId,
                    param.getOrderId());

            Ticket ticket = ticketService.getById(ticketListVO.getTicketId());
            if (null == ticket) {
                throw new ServiceException("参数异常，盲票不存在");
            }
            if (ticket.getStatus() == TicketStatusEnum.CASHED) {
                throw new ServiceException(ErrorCodeEnum.ERROR_CODE_1017);
            }
            if (ticket.getStatus() != TicketStatusEnum.ACTIVATED) {
                throw new ServiceException(ErrorCodeEnum.ERROR_CODE_1019);
            }

            // 查看奖级下的奖品
            List<TicketAwardsPrize> ticketAwardsPrizes = userHitPrizeService.listPrize(ticket, userId);

            List<TicketAwardsPrize> newTicketAwardsPrizes =  new ArrayList<>();

            for (TicketAwardsPrize ticketAwardsPrize : ticketAwardsPrizes) {
                if (ticketAwardsPrize.getRemainQty() > 0) {
                    newTicketAwardsPrizes.add(ticketAwardsPrize);
                }
            }

            int random = getRandom(newTicketAwardsPrizes);

            TicketAwardsPrize ticketAwardsPrize = newTicketAwardsPrizes.get(random);

            userHitPrizeDetailVO.setPrizeId(ticketAwardsPrize.getPrizeId());
            userHitPrizeDetailVO.setPicUrl(ticketAwardsPrize.getPicUrl());
            userHitPrizeDetailVO.setTitle(ticketAwardsPrize.getTitle());
            userHitPrizeDetailVO.setValue(ticketAwardsPrize.getValue());
            userHitPrizeDetailVO.setPrizeType(ticketAwardsPrize.getPrizeType());
            userHitPrizeDetailVO.setRefId(ticketAwardsPrize.getRefId());

            // 兑奖
            String lockKey = RedisLockKey.build(RedisLockKey.USER_TICKET_CASH_LOCK, ticketAwardsPrize.getPrizeId());
            if (!distributedLocker.tryLock(lockKey)) {
                throw new ServiceException("系统繁忙，请稍后再试");
            }
            try {
                // 兑奖
                this.cashPrize(ticket, userId, ticketAwardsPrize.getAwardsId(), ticketAwardsPrize.getPrizeId());
            } finally {
                distributedLocker.unlock(lockKey);
            }
        }

        return userHitPrizeDetailVO;

    }

    /**
     * 获取中奖索引
     * @param newTicketAwardsPrizes
     * @return
     */
    private int getRandom(List<TicketAwardsPrize> newTicketAwardsPrizes) {
        // 开奖
        int random = -1;
        // 计算总权重
        double sumWeight = 0;
        for (TicketAwardsPrize ticketAwardsPrize : newTicketAwardsPrizes) {
            sumWeight += ticketAwardsPrize.getRemainQty();
        }

        //产生随机数
        double randomNumber;
        randomNumber = Math.random();

        //根据随机数在所有奖品分布的区域并确定所抽奖品
        double d1 = 0;
        double d2 = 0;
        for (int i = 0; i < newTicketAwardsPrizes.size(); i++) {
            d2 += Double.parseDouble(String.valueOf(newTicketAwardsPrizes.get(i).getRemainQty())) / sumWeight;
            if (i == 0) {
                d1 = 0;
            } else {
                d1 += Double.parseDouble(String.valueOf(newTicketAwardsPrizes.get(i - 1).getRemainQty())) / sumWeight;
            }
            if (randomNumber >= d1 && randomNumber < d2) {
                random = i;
                break;
            }
        }
        return random;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cashPrize(Ticket ticket, Long userId, String awardsId, String prizeId) {
        // 更新奖品已兑奖数量
//        TicketAwardsPrize ticketAwardsPrize = ticketAwardsPrizeService.getById(prizeId);
        QueryWrapper<TicketAwardsPrize> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("t1.prize_id", prizeId);
        List<TicketAwardsPrize> ticketAwardsPrizeList = ticketAwardsPrizeService.listByQueryWrapper(queryWrapper);
        if (CollectionUtils.isEmpty(ticketAwardsPrizeList)) {
            throw new ServiceException("奖品不存在");
        }
        TicketAwardsPrize ticketAwardsPrize = ticketAwardsPrizeList.get(0);
        if (ticketAwardsPrize.getRemainQty() <= 0) {
            throw new ServiceException("奖品已兑完，请重新选择");
        }
        // 乐观锁
        boolean result = ticketAwardsPrizeService.update(new LambdaUpdateWrapper<TicketAwardsPrize>().set(TicketAwardsPrize::getCashedQty, ticketAwardsPrize.getCashedQty() + 1)
                .set(TicketAwardsPrize::getRemainQty, ticketAwardsPrize.getRemainQty() - 1)
                .eq(TicketAwardsPrize::getPrizeId, prizeId).eq(TicketAwardsPrize::getCashedQty, ticketAwardsPrize.getCashedQty()));
        Assert.isTrue(result,"兑奖后更新奖品库存失败。prizeId:" + prizeId);


        // 保存奖品到中奖记录中
        UserHitPrize userHitPrize = getOne(new LambdaQueryWrapper<UserHitPrize>().eq(UserHitPrize::getTicketId, ticket.getTicketId()));
        if (StringUtils.isNotBlank(userHitPrize.getPrizeId())) {
            throw new ServiceException("盲票已兑奖");
        }
        userHitPrize.setPrizeId(prizeId);
        updateById(userHitPrize);

        // 放入仓库
        if (ticketAwardsPrize.getPrizeType() == TicketPrizeTypeEnum.COIN) {
            userCoinService.produce(userId, ticketAwardsPrize.getValue(), userHitPrize.getId(),CoinLogTypeEnum.PRIZE);
        } else if (ticketAwardsPrize.getPrizeType() == TicketPrizeTypeEnum.COUPON) {
            couponService.distribute(ticket, userId, ticketAwardsPrize.getRefId());
        } else if (ticketAwardsPrize.getPrizeType() == TicketPrizeTypeEnum.COUPON_PKG) {
            couponPkgService.distribute(ticket, userId, ticketAwardsPrize.getRefId());
        }else {
            userPrizeStorageService.takeInStorage(userId, ticketAwardsPrize.getTitle(),ticketAwardsPrize.getPicUrl(),ticketAwardsPrize.getRefId(), PrizeStorageInTypeEnum.TICKET_CASHED, userHitPrize.getId());
        }

        boolean rtn = ticketService.update(new LambdaUpdateWrapper<Ticket>().set(Ticket::getStatus, TicketStatusEnum.CASHED)
                .eq(Ticket::getTicketId, ticket.getTicketId()).eq(Ticket::getStatus, TicketStatusEnum.ACTIVATED));
        Assert.isTrue(rtn, "兑奖后更新盲票状态失败。ticketId:" + ticket.getTicketId());
    }


    @Override
    public List<TicketHitPrizeBarrageVO> hitPrizeBarrage(Long userId) {
        return this.getBaseMapper().selectTicketHitPrizeBarrageNotByUserId(userId);
    }

    @Override
    public List<TicketHitPrizeBarrageVO> hitPrizeBarrageByUserId(Long userId) {
        LocalDateTime startTime = LocalDateTime.of(LocalDateTime.now().toLocalDate(), LocalTime.MIN);
        LocalDateTime endTime = LocalDateTime.now().withNano(0);
        return this.getBaseMapper().selectTicketHitPrizeBarrageByUserId(userId, startTime, endTime);
    }

    @Override
    public List<TicketHitPrizeBarrageVO> expensiveHitPrizeBarrage(Long userId) {
        LocalDateTime endTime = LocalDateTime.now().withNano(0);
        LocalDateTime startTime = endTime.minusMonths(6);
        return this.getBaseMapper().selectTicketExpensiveHitPrizeBarrageNotByUserId(userId, startTime, endTime);
    }


}
