package com.red.circle.other.app.command.game.egg;

import com.google.common.collect.Lists;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.other.app.dto.clientobject.game.GameEggRewardCO;
import com.red.circle.other.app.dto.cmd.game.GameEggLotteryCmd;
import com.red.circle.other.infra.database.mongo.entity.game.egg.GameEggStockConfig;
import com.red.circle.other.infra.database.mongo.service.game.GameEggStockConfigService;
import com.red.circle.other.infra.database.rds.entity.game.GameUserLotteryDetailsRecord;
import com.red.circle.other.infra.database.rds.entity.game.GameUserLotteryRecord;
import com.red.circle.other.infra.database.rds.service.game.GameEggCacheService;
import com.red.circle.other.infra.database.rds.service.game.GameUserLotteryDetailsRecordService;
import com.red.circle.other.infra.database.rds.service.game.GameUserLotteryRecordService;
import com.red.circle.other.infra.database.rds.service.game.GameUserShardsBackpackService;
import com.red.circle.other.infra.enums.game.LotteryGameTypeEnum;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.num.NumUtils;
import com.red.circle.tool.core.sequence.IdWorkerUtils;
import com.red.circle.wallet.inner.endpoint.wallet.WalletGoldClient;
import com.red.circle.wallet.inner.model.cmd.GoldReceiptCmd;
import com.red.circle.wallet.inner.model.dto.WalletReceiptResDTO;
import com.red.circle.wallet.inner.model.enums.GoldOrigin;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

/**
 * <p>
 * 砸金蛋抽奖.
 * </p>
 *
 * @author pengshigang
 * @since 2021-11-15
 */
@Component
@RequiredArgsConstructor
public class GameEggLotteryExe {

  private final WalletGoldClient walletGoldClient;
  private final GameEggCacheService gameEggCacheService;
  private final GameEggStockConfigService gameEggStockConfigService;
  private final GameUserLotteryRecordService gameUserLotteryRecordService;
  private final GameUserShardsBackpackService gameUserShardsBackpackService;
  private final GameUserLotteryDetailsRecordService gameUserLotteryDetailsRecordService;

  public static void main(String[] args) {
    BigDecimal processAmount = new BigDecimal("1000");
    BigDecimal extractRatio = new BigDecimal("0");

    BigDecimal extractAmount = processAmount.multiply(extractRatio)
        .setScale(0, RoundingMode.DOWN);
    System.out.println(extractAmount);
    BigDecimal thisPrizeAmount = processAmount.subtract(extractAmount)
        .setScale(0, RoundingMode.DOWN);
    System.out.println(thisPrizeAmount.longValue());
  }

  public List<GameEggRewardCO> execute(GameEggLotteryCmd cmd) {

    // 扣钱
    changeGoldBalance(cmd);

//    BigDecimal extractRatio = new BigDecimal(
//        gameEggCacheService.getExtractRatio(cmd.getSysOriginName(), "0"));
//
//    BigDecimal extractAmount = receipt.getProcessAmount().multiply(extractRatio)
//        .setScale(0, RoundingMode.DOWN);
//
//    BigDecimal thisPrizeAmount = receipt.getProcessAmount().subtract(extractAmount)
//        .setScale(0, RoundingMode.DOWN);
//
//    gameEggCacheService.incrThisMonthPrizePool(cmd.getSysOriginName(),
//        thisPrizeAmount.longValue());

    // 开始抽奖
    Map<Long, List<GameEggStockConfig>> fragmentsConfigPrize = startLottery(cmd);

    if (CollectionUtils.isEmpty(fragmentsConfigPrize)) {
      return Lists.newArrayList();
    }

    // 保存抽奖记录与用户碎片
    saveLotteryRecord(cmd, fragmentsConfigPrize);

    //返回组装好的结果
    return fragmentsConfigPrize.entrySet().stream()
        .map(entry -> {
          GameEggStockConfig config = entry.getValue().get(0);
          return new GameEggRewardCO()
              .setId(entry.getKey())
              .setShredCount(entry.getValue().size())
              .setCover(config.getSourceCover())
              .setDetailType(config.getSourceDetailType())
              .setType(config.getSourceType())
              .setContent(config.content());
        })
        .collect(Collectors.toList());
  }


  private Map<Long, List<GameEggStockConfig>> startLottery(GameEggLotteryCmd cmd) {

    // 创建抽奖结果容器
    List<GameEggStockConfig> fragmentsPrizes = Lists.newArrayList();
    for (int index = 0; index < cmd.getFrequency(); index++) {
      GameEggStockConfig gameEggStockConfig = loopPrize(cmd, 15);
      if (Objects.isNull(gameEggStockConfig)) {
        continue;
      }
      fragmentsPrizes.add(gameEggStockConfig);
    }
    return fragmentsPrizes.stream()
        .collect(Collectors.groupingBy(GameEggStockConfig::getId));
  }

  private GameEggStockConfig loopPrize(GameEggLotteryCmd cmd, int maxLoopSize) {

    if (maxLoopSize <= 0) {
      return null;
    }

    GameEggStockConfig gameEggStockConfig = getPrize(getGameEggStockConfigs(cmd));
    if (Objects.isNull(gameEggStockConfig)) {
      return null;
    }

    Long consumeStockQuantity = getConsumeStockQuantity(gameEggStockConfig.getId());
    // 修改库存
    if (gameEggStockConfigService.incConsumeStockQuantity(gameEggStockConfig.getId(), 1L,
        consumeStockQuantity)) {
      // 扣除库存
      gameEggCacheService.decrConsumeStockQuantity(gameEggStockConfig.getId(), 1L);
      // 奖金池扣除
      gameEggCacheService.decrThisMonthPrizePool(cmd.getReqSysOrigin().getOrigin(),
          gameEggStockConfig.getFragmentsAmount().longValue());
      return gameEggStockConfig;
    }

    return loopPrize(cmd, --maxLoopSize);
  }


  private Long getConsumeStockQuantity(Long id) {

    Long quantity = gameEggCacheService.getConsumeStockQuantity(id);

    if (Objects.nonNull(quantity)) {
      return quantity;
    }

    GameEggStockConfig gameEggStockConfig = gameEggStockConfigService.getConfigById(id);

    if (Objects.isNull(gameEggStockConfig) || Objects.isNull(
        gameEggStockConfig.getConsumeStockQuantity())) {
      return null;
    }

    gameEggCacheService.setConsumeStockQuantity(id,
        gameEggStockConfig.getConsumeStockQuantity());

    return gameEggCacheService.getConsumeStockQuantity(id);
  }

  private List<GameEggStockConfig> getGameEggStockConfigs(GameEggLotteryCmd cmd) {
    return Optional.ofNullable(gameEggStockConfigService.listConfig(
            cmd.getReqSysOrigin().getOrigin()))
        .map(configs -> configs.stream().filter(config ->
                Objects.equals(config.getStockQuantity(), -1L)
                    || config.getConsumeStockQuantity() < config.getStockQuantity()
            ).collect(Collectors.toList())
        )
        .orElseGet(Lists::newArrayList);
  }

  private GameEggStockConfig getPrize(List<GameEggStockConfig> gameEggStockConfigs) {
    if (CollectionUtils.isEmpty(gameEggStockConfigs)) {
      return null;
    }
    return gameEggStockConfigs.get(NumUtils.randomBetween(0, gameEggStockConfigs.size() - 1));
  }

  private void saveLotteryRecord(GameEggLotteryCmd cmd,
      Map<Long, List<GameEggStockConfig>> fragmentsConfigPrize) {

    Long lotteryId = IdWorkerUtils.getId();

    // 抽奖记录
    gameUserLotteryRecordService.save(new GameUserLotteryRecord()
        .setId(lotteryId)
        .setGameType(LotteryGameTypeEnum.EGG.name())
        .setLotteryFee(cmd.getLotteryFee())
        .setGroupId(0L)
        .setQuantity(cmd.getFrequency())
        .setSysOrigin(cmd.getReqSysOrigin().getOrigin())
        .setUserId(cmd.getReqUserId())
    );

    //抽奖明细
    gameUserLotteryDetailsRecordService
        .saveBatch(toLotteryDetails(fragmentsConfigPrize, lotteryId));

    // 抽奖碎片保存
    fragmentsConfigPrize.forEach((id, val) -> gameUserShardsBackpackService
        .saveOrUpdateByConfigIdByUserId(id,
            cmd.getReqUserId(),
            val.size()));

  }

  private List<GameUserLotteryDetailsRecord> toLotteryDetails(
      Map<Long, List<GameEggStockConfig>> mapShardsCount,
      Long lotteryId) {

    return mapShardsCount.values().stream().map(val -> {
      GameEggStockConfig config = val.get(0);
      GameUserLotteryDetailsRecord data = new GameUserLotteryDetailsRecord()
          .setLotteryId(lotteryId)
          .setType(config.getSourceType())
          .setCover(config.getSourceCover())
          .setDetailType(config.getSourceDetailType())
          .setContent(config.content())
          .setQuantity(val.size());
      data.setCreateTime(TimestampUtils.now());
      data.setUpdateTime(TimestampUtils.now());
      return data;
    }).collect(Collectors.toList());
  }

  private WalletReceiptResDTO changeGoldBalance(GameEggLotteryCmd cmd) {
    Long id = IdWorkerUtils.getId();
    return ResponseAssert.requiredSuccess(walletGoldClient.changeBalance(GoldReceiptCmd.builder()
        .appExpenditure()
        .sysOrigin(cmd.getReqSysOrigin().getOrigin())
        .amount(cmd.getLotteryFee())
        .eventId(id)
        .userId(cmd.getReqUserId())
        .origin(GoldOrigin.EGG)
        .build()));
  }


}
