package com.seamew.lottery.infrastructure.repository;

import com.seamew.lottery.common.Constants;
import com.seamew.lottery.domain.strategy.model.aggregates.StrategyRich;
import com.seamew.lottery.domain.strategy.model.res.AwardStockResult;
import com.seamew.lottery.domain.strategy.model.vo.AwardBriefVO;
import com.seamew.lottery.domain.strategy.model.vo.AwardDeductStockVO;
import com.seamew.lottery.domain.strategy.model.vo.StrategyBriefVO;
import com.seamew.lottery.domain.strategy.model.vo.StrategyDetailBriefVO;
import com.seamew.lottery.domain.strategy.repository.IStrategyRepository;
import com.seamew.lottery.infrastructure.dao.IAwardDao;
import com.seamew.lottery.infrastructure.dao.IStrategyDao;
import com.seamew.lottery.infrastructure.dao.IStrategyDetailDao;
import com.seamew.lottery.infrastructure.po.Award;
import com.seamew.lottery.infrastructure.po.Strategy;
import com.seamew.lottery.infrastructure.po.StrategyDetail;
import com.seamew.lottery.infrastructure.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

/**
 * @Author: seamew
 * @Title: StrategyRepository
 * @CreateTime: 2023年02月13日 21:02:00
 * @Description: 策略表仓储服务
 * @Version: 1.0
 */
@Repository
@Slf4j
public class StrategyRepository implements IStrategyRepository {

    @Resource
    private RedisUtil redisUtil;

    @Resource
    private IStrategyDao strategyDao;

    @Resource
    private IStrategyDetailDao strategyDetailDao;

    @Resource
    private IAwardDao awardDao;

    @Override
    public StrategyRich queryStrategyRich(Long strategyId) {
        Strategy strategy = strategyDao.queryStrategy(strategyId);
        List<StrategyDetail> strategyDetailList = strategyDetailDao.queryStrategyDetailList(strategyId);

        StrategyBriefVO strategyBriefVO = new StrategyBriefVO();
        BeanUtils.copyProperties(strategy, strategyBriefVO);

        List<StrategyDetailBriefVO> strategyDetailBriefVOList = new ArrayList<>();
        for (StrategyDetail strategyDetail : strategyDetailList) {
            StrategyDetailBriefVO strategyDetailBriefVO = new StrategyDetailBriefVO();
            BeanUtils.copyProperties(strategyDetail, strategyDetailBriefVO);
            strategyDetailBriefVOList.add(strategyDetailBriefVO);
            // 插入redis缓存
            if (ObjectUtils.isEmpty(redisUtil.get(Constants.RedisKey.KEY_LOTTERY_AWARD_STOCK_COUNT(strategyDetail.getStrategyId(), strategyDetail.getAwardId())))) {
                redisUtil.set(Constants.RedisKey.KEY_LOTTERY_AWARD_STOCK_COUNT(strategyDetail.getStrategyId(), strategyDetail.getAwardId()), strategyDetail.getAwardSurplusCount());
            }
        }

        return new StrategyRich(strategyId, strategyBriefVO, strategyDetailBriefVOList);
    }

    @Override
    public AwardBriefVO queryAwardInfo(String awardId) {
        Award award = awardDao.queryAwardInfo(awardId);
        AwardBriefVO awardBriefVO = new AwardBriefVO();
        BeanUtils.copyProperties(award, awardBriefVO);
        return awardBriefVO;
    }

    @Override
    public List<String> queryNoStockStrategyAwardList(Long strategyId) {
        return strategyDetailDao.queryNoStockStrategyAwardList(strategyId);
    }

    @Override
    public boolean deductStock(Long strategyId, String awardId) {
        StrategyDetail req = new StrategyDetail();
        req.setStrategyId(strategyId);
        req.setAwardId(awardId);
        int count = strategyDetailDao.deductStock(req);
        return count == 1;
    }

    @Override
    public AwardStockResult deductStockByRedis(Long strategyId, String awardId) {
        // 1. 获取奖品的key
        String stockKey = Constants.RedisKey.KEY_LOTTERY_AWARD_STOCK_COUNT(strategyId, awardId);
        // 2. 扣减库存
        int surplusCount = redisUtil.decr(stockKey, 1).intValue();
        // 3. 超出库存判断，进行恢复原始库存
        if (surplusCount < 0) {
            redisUtil.incr(stockKey, 1);
            return new AwardStockResult(Constants.ResponseCode.OUT_OF_AWARD_STOCK.getCode(), Constants.ResponseCode.OUT_OF_AWARD_STOCK.getInfo());
        }
        // 4. 以剩余库存，生成对应加锁Key，细化锁的颗粒度
        String stockTokenKey = Constants.RedisKey.KEY_LOTTERY_AWARD_STOCK_COUNT_TOKEN(strategyId, awardId, surplusCount);
        // 5. 使用 Redis.setNx 加一个分布式锁
        boolean lockToken = redisUtil.setNx(stockTokenKey, 350L);
        if (!lockToken) {
            //todo: 暂时回滚，后面使用mq补偿
            redisUtil.incr(stockKey, 1);
            log.info("抽奖策略{}用户秒杀奖品{}扣减库存，分布式锁失败：{}", strategyId, awardId, stockTokenKey);
            return new AwardStockResult(Constants.ResponseCode.ERR_TOKEN.getCode(), Constants.ResponseCode.ERR_TOKEN.getInfo());
        }
        redisUtil.expire(stockTokenKey, 5);
        return new AwardStockResult(Constants.ResponseCode.SUCCESS.getCode(), Constants.ResponseCode.SUCCESS.getInfo(), awardId, surplusCount, stockTokenKey);
    }

    @Override
    public void updateAwardStock(AwardDeductStockVO awardDeductStockVO) {
        StrategyDetail strategyDetail = new StrategyDetail();
        strategyDetail.setStrategyId(awardDeductStockVO.getStrategyId());
        strategyDetail.setAwardId(awardDeductStockVO.getAwardId());
        strategyDetail.setAwardSurplusCount(awardDeductStockVO.getAwardSurplusCount());
        strategyDetailDao.updateAwardStock(strategyDetail);
    }

    @Override
    public void recoverAwardCacheStockByRedis(String tokenKey) {
        if (StringUtils.isNotEmpty(redisUtil.get(tokenKey))) {
            redisUtil.del(tokenKey);
        }
    }
}
