package com.seamew.lottery.domain.strategy.service.draw;

import com.seamew.lottery.common.Constants;
import com.seamew.lottery.domain.strategy.model.aggregates.StrategyRich;
import com.seamew.lottery.domain.strategy.model.req.DrawReq;
import com.seamew.lottery.domain.strategy.model.res.AwardStockResult;
import com.seamew.lottery.domain.strategy.model.res.DrawResult;
import com.seamew.lottery.domain.strategy.model.vo.*;
import com.seamew.lottery.domain.strategy.service.algorithm.IDrawAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * @Author: seamew
 * @Title: AbstractDrawBase
 * @CreateTime: 2023年02月14日 18:09:00
 * @Description: 定义抽象抽奖过程，模板模式
 * @Version: 1.0
 */
@Slf4j
public abstract class AbstractDrawBase extends DrawStrategySupport implements IDrawExec {
    @Override
    public DrawResult doDrawExec(DrawReq req) {
        // 1. 获取抽奖策略
        StrategyRich strategyRich = queryStrategyRich(req.getStrategyId());
        StrategyBriefVO strategy = strategyRich.getStrategy();

        // 2. 校验抽奖策略是否已经初始化到内存
        checkAndInitRateData(req.getStrategyId(), strategy.getStrategyMode(), strategyRich.getStrategyDetailList());

        // 3. 获取不在抽奖范围内的列表，包括：奖品库存为空、风控策略、临时调整等
        List<String> excludeAwardIds = queryExcludeAwardIds(req.getStrategyId());

        // 4. 执行抽奖算法
        AwardStockResult awardStockResult = drawAlgorithm(req.getStrategyId(), drawAlgorithmGroup.get(strategy.getStrategyMode()), excludeAwardIds);

        // 5. 包装中奖结果
        return buildDrawResult(req.getUId(), req.getStrategyId(), awardStockResult, strategy);
    }

    /**
     * 获取不在抽奖范围内的列表，包括：奖品库存为空、风控策略、临时调整等，这类数据是含有业务逻辑的，所以需要由具体地实现方决定
     *
     * @param strategyId 策略ID
     * @return 排除的奖品ID集合
     */
    protected abstract List<String> queryExcludeAwardIds(Long strategyId);

    /**
     * 执行抽奖算法
     *
     * @param strategyId      策略ID
     * @param drawAlgorithm   抽奖算法模型
     * @param excludeAwardIds 排除的抽奖ID集合
     * @return 抽奖算法结果
     */
    protected abstract AwardStockResult drawAlgorithm(Long strategyId, IDrawAlgorithm drawAlgorithm, List<String> excludeAwardIds);

    /**
     * 校验抽奖策略是否已经初始化到内存
     *
     * @param strategyId         抽奖策略ID
     * @param strategyMode       抽奖策略模式
     * @param strategyDetailList 抽奖策略详情
     */
    private void checkAndInitRateData(Long strategyId, Integer strategyMode, List<StrategyDetailBriefVO> strategyDetailList) {

        // 根据抽奖策略模式，获取对应的抽奖服务
        IDrawAlgorithm drawAlgorithm = drawAlgorithmGroup.get(strategyMode);

        // 判断已处理过的的数据
        if (drawAlgorithm.isExist(strategyId)) {
            return;
        }

        // 解析并初始化中奖概率数据到散列表
        List<AwardRateInfo> awardRateInfoList = new ArrayList<>(strategyDetailList.size());
        for (StrategyDetailBriefVO strategyDetail : strategyDetailList) {
            awardRateInfoList.add(new AwardRateInfo(strategyDetail.getAwardId(), strategyDetail.getAwardRate()));
        }

        drawAlgorithm.initRateTuple(strategyId, strategyMode, awardRateInfoList);

    }

    /**
     * 包装抽奖结果
     *
     * @param uId              用户ID
     * @param strategyId       策略ID
     * @param awardStockResult 扣减结果
     * @return 中奖结果
     */
    private DrawResult buildDrawResult(String uId, Long strategyId, AwardStockResult awardStockResult, StrategyBriefVO strategy) {
        if (ObjectUtils.isEmpty(awardStockResult) || !Constants.ResponseCode.SUCCESS.getCode().equals(awardStockResult.getCode())) {
            log.info("执行策略抽奖完成【未中奖】，用户：{} 策略ID：{}", uId, strategyId);
            return new DrawResult(awardStockResult.getCode(), awardStockResult.getInfo(), uId, strategyId, Constants.ResponseCode.LOSING_DRAW.getCode().equals(awardStockResult.getCode()) ? Constants.DrawState.FAIL.getCode() : Constants.DrawState.ERROR.getCode());
        }

        AwardBriefVO award = queryAwardInfoByAwardId(awardStockResult.getAwardId());
        DrawAwardVO drawAwardInfo = new DrawAwardVO(uId, award.getAwardId(), award.getAwardType(), award.getAwardName(), award.getAwardContent());
        drawAwardInfo.setStrategyMode(strategy.getStrategyMode());
        drawAwardInfo.setGrantType(strategy.getGrantType());
        drawAwardInfo.setGrantDate(strategy.getGrantDate());
        drawAwardInfo.setAwardSurplusCount(awardStockResult.getAwardSurplusCount());
        log.info("执行策略抽奖完成【已中奖】，用户：{} 策略ID：{} 奖品ID：{} 奖品名称：{}", uId, strategyId, awardStockResult.getAwardId(), award.getAwardName());

        return new DrawResult(awardStockResult.getCode(), awardStockResult.getInfo(), uId, strategyId, Constants.DrawState.SUCCESS.getCode(), drawAwardInfo, awardStockResult.getStockTokenKey());
    }

}
