package com.red.circle.other.app.common.gift;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
import com.red.circle.component.mq.MessageEvent;
import com.red.circle.component.mq.service.FixedTopic;
import com.red.circle.component.redis.service.RedisService;
import com.red.circle.external.inner.endpoint.message.ImGroupClient;
import com.red.circle.external.inner.model.cmd.message.BroadcastGroupMsgBodyCmd;
import com.red.circle.external.inner.model.cmd.message.CustomGroupMsgBodyCmd;
import com.red.circle.external.inner.model.enums.message.GroupMessageTypeEnum;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.dto.ReqSysOrigin;
import com.red.circle.mq.business.model.event.gift.GameLuckyGiftBusinessEvent;
import com.red.circle.mq.business.model.event.gift.GameLuckyGiftUser;
import com.red.circle.mq.business.model.event.gift.GiveAwayGiftBatchEvent;
import com.red.circle.mq.business.model.event.gift.GiveAwayGiftRoomAccepts;
import com.red.circle.mq.rocket.business.producer.GiftMqMessage;
import com.red.circle.mq.rocket.business.service.MessageSenderService;
import com.red.circle.mq.rocket.business.streams.GameLuckyGiftBusinessSink;
import com.red.circle.order.inner.model.enums.MonthlyRechargeType;
import com.red.circle.other.app.convertor.material.GiftAppConvertor;
import com.red.circle.other.app.convertor.user.UserProfileAppConvertor;
import com.red.circle.other.app.dto.clientobject.game.GameLuckyGiftMsgCO;
import com.red.circle.other.app.dto.cmd.activity.RankingDataUpdateCmd;
import com.red.circle.other.app.dto.cmd.gift.GiveAwayGiftBatchCmd;
import com.red.circle.other.app.dto.cmd.task.RoomDailyTaskProgressUpdateCmd;
import com.red.circle.other.app.service.activity.ActivityRechargeTicketService;
import com.red.circle.other.app.service.activity.RankingActivityService;
import com.red.circle.other.app.service.room.RoomProfileService;
import com.red.circle.other.app.service.task.RoomDailyTaskProgressService;
import com.red.circle.other.app.util.DateTimeAsiaRiyadhUtils;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.domain.ranking.RankingActivityType;
import com.red.circle.other.domain.ranking.RankingCycleType;
import com.red.circle.other.domain.ranking.RankingDimension;
import com.red.circle.other.infra.config.LuckyGiftApiConfig;
import com.red.circle.other.infra.config.LuckyGiftApiConfig.ApiAccount;
import com.red.circle.other.infra.database.cache.entity.game.luckgift.GameLuckyGiftConfigCache;
import com.red.circle.other.infra.database.cache.entity.game.luckgift.GameLuckyGiftInventoryCache;
import com.red.circle.other.infra.database.cache.entity.game.luckgift.LuckyGiftInventoryTemplateCache;
import com.red.circle.other.infra.database.cache.service.other.EnumConfigCacheService;
import com.red.circle.other.infra.database.cache.service.other.GameLuckyGiftCacheService;
import com.red.circle.other.infra.database.cache.service.other.GiftCacheService;
import com.red.circle.other.infra.database.mongo.entity.live.RoomProfileManager;
import com.red.circle.other.infra.database.mongo.service.live.RoomProfileManagerService;
import com.red.circle.other.infra.database.rds.entity.game.GameLuckyGiftCount;
import com.red.circle.other.infra.database.rds.entity.game.GameLuckyGiftRuleConfig;
import com.red.circle.other.infra.database.rds.entity.game.LuckyGiftProbability;
import com.red.circle.other.infra.database.rds.entity.game.LuckyGiftProbabilityDetails;
import com.red.circle.other.infra.database.rds.entity.live.RoomMember;
import com.red.circle.other.infra.database.rds.service.game.GameLuckyGiftCountService;
import com.red.circle.other.infra.database.rds.service.game.GameLuckyGiftRuleConfigService;
import com.red.circle.other.infra.database.rds.service.game.LuckyGiftProbabilityDetailsService;
import com.red.circle.other.infra.database.rds.service.game.LuckyGiftProbabilityService;
import com.red.circle.other.infra.database.rds.service.live.RoomMemberService;
import com.red.circle.other.infra.database.rds.service.sys.EnumConfigService;
import com.red.circle.other.inner.enums.config.EnumConfigKey;
import com.red.circle.other.inner.enums.game.luckgift.GameLuckyGiftModeEnum;
import com.red.circle.other.inner.enums.game.luckgift.GameLuckyGiftMultipleEnum;
import com.red.circle.other.inner.enums.material.GiftTabEnum;
import com.red.circle.other.inner.enums.material.PropsCommodityType;
import com.red.circle.other.inner.enums.task.RoomDailyTaskCode;
import com.red.circle.other.inner.model.dto.material.GiftConfigDTO;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.other.inner.model.dto.user.props.UserPropsResourcesDTO;
import com.red.circle.other.inner.model.dto.user.props.UserUsePropsDTO;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils;
import com.red.circle.tool.core.num.ArithmeticUtils;
import com.red.circle.tool.core.sequence.IdWorkerUtils;
import com.red.circle.tool.core.text.StringUtils;
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.enums.GoldOrigin;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.security.MessageDigest;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component;

/**
 * 幸运礼物游戏.
 *
 * @author pengshigang on 2023/06/15
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class GameLuckyGiftCommon {

  private final MessageSenderService senderService;
  private final ImGroupClient imGroupClient;
  private final WalletGoldClient walletGoldClient;
  private final GiftCacheService giftCacheService;
  private final EnumConfigService enumConfigService;
  private final UserProfileGateway userProfileGateway;
  private final EnumConfigCacheService enumConfigCacheService;
  private final UserProfileAppConvertor userProfileAppConvertor;
  private final GameLuckyGiftCountService gameLuckyGiftCountService;
  private final GameLuckyGiftCacheService gameLuckyGiftCacheService;
  private final LuckyGiftProbabilityService luckyGiftProbabilityService;
  private final GameLuckyGiftRuleConfigService gameLuckyGiftRuleConfigService;
  private final LuckyGiftProbabilityDetailsService luckyGiftProbabilityDetailsService;
  private final LuckyGiftApiConfig luckyGiftApiConfig;
  private final RankingActivityService rankingActivityService;
  private final GiftMqMessage giftMqMessage;
  private final GiftAppConvertor giftAppConvertor;
  private final ActivityRechargeTicketService activityRechargeTicketService;
  private final RoomDailyTaskProgressService roomDailyTaskProgressService;
  private final RoomMemberService roomMemberService;
  private final RedisService redisService;
  private final RoomProfileManagerService roomProfileManagerService;

  /**
   * 发送幸运礼物抽奖mq.
   */
  public void sendMq(GiveAwayGiftBatchCmd cmd, Long standardId) {

    GameLuckyGiftBusinessEvent event = buildLuckyGiftBusinessEvent(cmd, standardId);
    if (Objects.isNull(event)) {
      return;
    }

    event.setUsers(cmd.filterAcceptUserId().stream().map(acceptUserId ->
        new GameLuckyGiftUser()
            .setId(IdWorkerUtils.getId())
            .setAcceptUserId(acceptUserId)
            .setGiftsCount(getGiftsCount(event))
            .setComboLossCount(getComboLossCount(event))
    ).collect(Collectors.toList()));

    // 抽奖逻辑mq
    senderService.sendEventMessage(MessageEvent.builder()
        .consumeId(IdWorkerUtils.getIdStr())
        .topic(FixedTopic.RC_DEFAULT_APP_ORDINARY)
        .tag(GameLuckyGiftBusinessSink.TAG)
        .body(event)
        .build());

  }

  public void sendMq(
      GiveAwayGiftBatchCmd cmd,
      Long standardId,
      String businessId,
      String consumeAssetRecordId,
      BigDecimal giftPrice,
      Boolean giftIsFree
  ) {

    GameLuckyGiftBusinessEvent event = new GameLuckyGiftBusinessEvent()
        .setBusinessId(businessId)
        .setConsumeAssetRecordId(consumeAssetRecordId)
        .setUserId(cmd.requiredReqUserId())
        .setRoomId(cmd.getRoomId())
        .setSysOrigin(cmd.requireReqSysOrigin())
        .setGiftId(cmd.getGiftId())
        .setGiftPrice(giftPrice)
        .setGiftIsFree(Boolean.TRUE.equals(giftIsFree))
        .setQuantity(cmd.getQuantity())
        .setCheckCombo(cmd.getCheckCombo())
        .setGiftCombos(0)
        .setStandardId(standardId)
        .setUsers(cmd.filterAcceptUserId().stream().map(acceptUserId ->
            new GameLuckyGiftUser()
                .setId(IdWorkerUtils.getId())
                .setAcceptUserId(acceptUserId)
                .setGiftsCount(0)
                .setComboLossCount(0)
        ).collect(Collectors.toList()));

    senderService.sendEventMessage(MessageEvent.builder()
        .consumeId(businessId)
        .topic(FixedTopic.RC_DEFAULT_APP_ORDINARY)
        .tag(GameLuckyGiftBusinessSink.TAG)
        .body(event)
        .build());
  }

  private Integer getComboLossCount(GameLuckyGiftBusinessEvent event) {

    if (Boolean.TRUE.equals(event.getOpenComboLoss())) {

      if (Boolean.TRUE.equals(event.getCheckCombo())) {

        return gameLuckyGiftCacheService.incrComboLossCount(event.getUserId());
      }

      gameLuckyGiftCacheService.removeComboLossCount(event.getUserId());

    }
    return 0;
  }

  private Integer getGiftsCount(GameLuckyGiftBusinessEvent event) {

    Integer giftsCount = 0;

    if (Boolean.TRUE.equals(event.getOpenGiftGiveTotal())) {

      giftsCount = gameLuckyGiftCacheService.incrGiftsCount(event.getUserId(),
          event.getGiftId(), event.getQuantity());

      if (giftsCount > event.getSysGiftGiveTotal()) {

        giftsCount = gameLuckyGiftCacheService.resetGiftsCount(event.getUserId(),
            event.getGiftId(), event.getQuantity());
      }
    }
    return giftsCount;
  }

  private GameLuckyGiftBusinessEvent buildLuckyGiftBusinessEvent(GiveAwayGiftBatchCmd cmd,
      Long standardId) {

    LuckyGiftInventoryTemplateCache template = getInventoryTemplate(standardId, cmd.getQuantity());
    if (Objects.isNull(template) || CollectionUtils.isEmpty(template.getCaches())) {
      return null;
    }

    GameLuckyGiftBusinessEvent event = new GameLuckyGiftBusinessEvent()
        .setUserId(cmd.requiredReqUserId())
        .setRoomId(cmd.getRoomId())
        .setSysOrigin(cmd.requireReqSysOrigin())
        .setGiftId(cmd.getGiftId())
        .setQuantity(cmd.getQuantity())
        .setCheckCombo(cmd.getCheckCombo())
        .setOpenComboLoss(isOpenComboLoss(template))
        .setSysComboLoss(template.getComboLoss())
        .setOpenGiftGiveTotal(isOpenGiftGiveTotal(template))
        .setSysGiftGiveTotal(template.getGiftGiveTotal())
        .setStandardId(standardId);

    // 是否正在连击送礼物
    if (Boolean.TRUE.equals(event.getCheckCombo())) {

      event.setGiftCombos(gameLuckyGiftCacheService.incrComboCount(event.getUserId(),
          event.getGiftId(), event.getQuantity()));

    } else {

      event.setGiftCombos(gameLuckyGiftCacheService.resetComboCount(event.getUserId(),
          event.getGiftId(), event.getQuantity()));
    }
    return event;
  }

  /**
   * mq调用此处,计算并存储与发送奖励.
   */

  public void run(GameLuckyGiftParamCmd cmd) {
    runLuckyGiftGameNew(cmd);
  }

  /**
   * 抽奖.
   */
  private void runLuckyGiftGameNew(GameLuckyGiftParamCmd cmd) {
    // 组装幸运礼物参数
    GameLuckyGiftParam param = toBuildGameLuckyGift(cmd);
    log.info("runLuckyGiftGame param:{}", JSON.toJSONString(param));
    if (Objects.isNull(param)) {
      return;
    }

    List<String> orderIds = List.of("LG_" + param.getId().toString());
    ThirdPartyLotteryResult apiResult = callThirdPartyLotteryApi(param, orderIds);

    Integer totalAwardAmount = apiResult.getOrderResults().stream().mapToInt(OrderResult::getRewardNum).sum();

    // 保存幸运礼物流水
    Long rewardAmount = Long.valueOf(String.valueOf(totalAwardAmount));
    saveLuckGiftCount(param, rewardAmount);

    if (totalAwardAmount <= 0) {
      return;
    }

    // 发送中奖金币
    BigDecimal balance = sendGold(param.getUserId(), param.getGiftId(), param.getSysOrigin(), rewardAmount);

    // 发送中奖通知
    int multiple = getRewardMultiple(param, rewardAmount);
    sendMsg(param, multiple, balance, rewardAmount);

    // 魔法礼物中奖后处理
    GiftConfigDTO giftConfigDTO = getGiftConfigDTO(cmd.getGiftId());
    if (GiftTabEnum.MAGIC.name().equals(giftConfigDTO.getGiftTab())) {
      String timeId = IdWorker.getIdStr();
      GiveAwayGiftBatchEvent giftBatchEvent = buildGiftBatchEvent(cmd, timeId, param, giftConfigDTO);

      giftMqMessage.sendGift(timeId, giftBatchEvent);

      updateRoomDailyTask(param, rewardAmount, RoomDailyTaskCode.PERSONAL_MAGIC_GIFT_GOLD);
    } else {
      updateRoomDailyTask(param, rewardAmount, RoomDailyTaskCode.PERSONAL_LUCKY_GIFT_GOLD);
    }

    // 累加用户获得的金币并发送抽奖券
//    activityRechargeTicketService.accumulatedRechargeAndAddTicket(param.getUserId(), rewardAmount, MonthlyRechargeType.LUCKY_WIN);


  }

  private void updateRoomDailyTask(GameLuckyGiftParam param, Long rewardAmount, RoomDailyTaskCode taskType) {
    RoomMember roomMember = roomMemberService.getRoomMember(param.getRoomId(), param.getUserId());
    if (roomMember == null) {
        return;
    }
    // 在自己的房间直接返回
    RoomProfileManager profileManager = roomProfileManagerService.getByUserId(param.getUserId());
    if (profileManager == null || Objects.equals(profileManager.getId(), roomMember.getRoomId())) {
        return;
    }

    String redisKey = "room:daily:task:progress:" + taskType.name() + ":" + param.getUserId() + ":" + ZonedDateTimeAsiaRiyadhUtils.now().toLocalDate();
    Long total = redisService.increment(redisKey, rewardAmount);
    redisService.expire(redisKey, DateTimeAsiaRiyadhUtils.getSecondsUntilMidnight(), TimeUnit.SECONDS);

    roomDailyTaskProgressService.updateTaskProgress(
            new RoomDailyTaskProgressUpdateCmd()
                    .setUserId(param.getUserId())
                    .setTaskCode(taskType.name())
                    .setProgressValue(total.intValue())
    );
  }

  @NotNull
  private GiveAwayGiftBatchEvent buildGiftBatchEvent(GameLuckyGiftParamCmd cmd, String timeId,
                                                     GameLuckyGiftParam param, GiftConfigDTO giftConfigDTO) {
    return new GiveAwayGiftBatchEvent()
            .setSysOrigin(SysOriginPlatformEnum.valueOf(param.getSysOrigin()))
            .setRequestPlatform("Android")
            .setSendUserId(cmd.getUserId())
            .setAccepts(getAccepts(cmd))
            .setQuantity(cmd.getQuantity())
            .setGiftConfig(giftAppConvertor.toGiveGiftConfig(giftConfigDTO))
            .setRoomId(cmd.getRoomId())
            .setTrackId(Long.parseLong(timeId))
            .setBagGift(Boolean.FALSE)
            .setCreateTime(TimestampUtils.now());

  }


  private List<GiveAwayGiftRoomAccepts> getAccepts(GameLuckyGiftParamCmd cmd) {
    List<GiveAwayGiftRoomAccepts> accepts = new ArrayList<>();
    accepts.add(new GiveAwayGiftRoomAccepts()
            .setAcceptUserId(cmd.getAcceptUserId()));
    return accepts;
  }


  private static int getRewardMultiple(GameLuckyGiftParam param, Long rewardAmount) {
    long payAmount = param.getQuantity() * param.getGift().getGiftCandy().longValue();
      return (int) (rewardAmount / payAmount);
  }

  /**
   * 抽奖.
   */
  private void runLuckyGiftGame(GameLuckyGiftParamCmd cmd) {

    // 组装幸运礼物参数
    GameLuckyGiftParam param = toBuildGameLuckyGift(cmd);
    log.info("runLuckyGiftGame param:{}", JSON.toJSONString(param));
    if (Objects.isNull(param)) {
      return;
    }

    // 出奖结果
    GameLuckyGiftRewardResult rewardResult = runGame(param);
    log.info("runLuckyGiftGame rewardResult:{}", JSON.toJSONString(param));
    if (Objects.isNull(rewardResult)) {
      runLuckyGiftGame(cmd);
      return;
    }

    // 递减Redis库存
    if (Boolean.TRUE.equals(rewardResult.getChangeInventory())) {

      Long residueCount = decrementInventoryQuantity(param.getGiftId(), param.getQuantity(),
          rewardResult.getReward().getMultiple());
      if (residueCount < 0) {
        runLuckyGiftGame(cmd);
        return;
      }
    }

    //处理Redis用户计数
    processCountReset(param, rewardResult);

    // 保存幸运礼物流水
    sendMessage(param, rewardResult);

  }

  /**
   * 处理用户连续赠送幸运礼物奖励逻辑
   */
  private void sendMessage(GameLuckyGiftParam param, GameLuckyGiftRewardResult rewardResult) {

    Integer multiple = rewardResult.getReward().getMultiple();

    Long awardAmount = getSendAmount(multiple, param.getGift().getGiftCandy());

    gameLuckyGiftCountService.save(new GameLuckyGiftCount()
        .setId(param.getId())
        .setGiftId(param.getGiftId())
        .setRoomId(param.getRoomId())
        .setQuantity(param.getQuantity())
        .setUserId(param.getUserId())
        .setSysOrigin(param.getSysOrigin())
        .setGiftIncCount(param.getGiftsCount())
        .setGiftCombos(param.getComboCount())
//        .setGiftCover(param.getGift().getGiftPhoto())
        .setPayAmount(param.getQuantity() * param.getGift().getGiftCandy().longValue())
        .setMultiple(multiple)
        .setChangeInventory(rewardResult.getChangeInventory())
        .setSysGiftIncCount(param.getTemplate().getGiftGiveTotal())
        .setAwardAmount(awardAmount)
        .setRegionCode(param.getRoomRegionCode())
        .setDateNumber(ZonedDateTimeAsiaRiyadhUtils.nowDateToInt())
    );

    if (multiple <= 0) {
      return;
    }

    // 发送中奖金币
    BigDecimal balance = sendGold(param.getUserId(), param.getGiftId(), param.getSysOrigin(), awardAmount);

    // 发送中奖通知
    sendMsg(param, multiple, balance, awardAmount);

  }

  /**
   * 处理用户连续赠送幸运礼物奖励逻辑
   */
  private void saveLuckGiftCount(GameLuckyGiftParam param, Long awardAmount) {

    long payAmount = param.getQuantity() * param.getGift().getGiftCandy().longValue();
    int multiple = (int) (awardAmount / payAmount);

    gameLuckyGiftCountService.save(new GameLuckyGiftCount()
        .setId(param.getId())
        .setGiftId(param.getGiftId())
        .setRoomId(param.getRoomId())
        .setQuantity(param.getQuantity())
        .setUserId(param.getUserId())
        .setSysOrigin(param.getSysOrigin())
        .setGiftIncCount(param.getGiftsCount())
        .setGiftCombos(param.getComboCount())
        .setGiftCover(param.getGift().getGiftPhoto())
        .setPayAmount(payAmount)
        .setMultiple(multiple)
        .setChangeInventory(false)
        .setSysGiftIncCount(param.getTemplate().getGiftGiveTotal())
        .setAwardAmount(awardAmount)
        .setRegionCode(param.getRoomRegionCode())
        .setDateNumber(ZonedDateTimeAsiaRiyadhUtils.nowDateToInt())
    );
  }

  private void getDataUpdateCmd(Long userId,  Long actualAmount) {
      RankingDataUpdateCmd receiverCmd = new RankingDataUpdateCmd();
      receiverCmd.setUserId(userId);
      receiverCmd.setUserSex(1);
      receiverCmd.setActivityType(RankingActivityType.LUCK_GIFT.getCode());
      receiverCmd.setCycleType(RankingCycleType.WEEKLY.getCode());
      receiverCmd.setCycleKey(ZonedDateTimeAsiaRiyadhUtils.nowWeekMondayToInt().toString());
      receiverCmd.setDimension(RankingDimension.CONSUME_AMOUNT.getCode());
      receiverCmd.setIncrementQuantity(actualAmount);
      receiverCmd.setBizNo(IdWorkerUtils.getIdStr());
      receiverCmd.setReqSysOrigin(ReqSysOrigin.of("LIKEI"));
      rankingActivityService.accumulateRankingData(receiverCmd);
  }

  /**
   * 开启全部模式的情况下抽奖.
   *
   * @return bool:是否变动库存(true变动, false不动), 奖励
   */
  private GameLuckyGiftRewardResult runAllMode(GameLuckyGiftParam param) {
    log.info("runAllMode param:{}", JSON.toJSONString(param));

    // 获得模版配置
    LuckyGiftInventoryTemplateCache template = param.getTemplate();

    // 获得库存数据，没有数据则初始化并返回
    List<GameLuckyGiftInventoryCache> inventory = listRemainInventory(param);
    if (CollectionUtils.isEmpty(inventory)) {
      return null;
    }

    // 已满足 - 连续亏钱逻辑
    if (param.getComboLossCount() >= template.getComboLoss()) {

      // 已满足 - 累计送礼物次数逻辑
      if (param.getGiftsCount() >= template.getGiftGiveTotal()) {

        // true - 有赚钱签
        List<GameLuckyGiftInventoryCache> makeMoneyInventory = generateMakeMoneyInventory(
            param.getQuantity(), inventory);
        if (CollectionUtils.isNotEmpty(makeMoneyInventory)) {
          return buildRewardResult(GameLuckyGiftModeEnum.ALL, Boolean.TRUE, Boolean.TRUE,
              Boolean.TRUE, makeMoneyInventory, param);
        }

        // true - 有大于0倍的签
        List<GameLuckyGiftInventoryCache> awardedInventory = generateGtZeroFoldInventor(inventory);
        if (CollectionUtils.isNotEmpty(awardedInventory)) {

          return buildRewardResult(GameLuckyGiftModeEnum.ALL, Boolean.TRUE, Boolean.FALSE,
              Boolean.TRUE, awardedInventory, param);
        }

        // true - 没有大于0倍的签, 给1倍,且不变更库存
        return buildRewardResult(GameLuckyGiftModeEnum.ALL, Boolean.FALSE, Boolean.FALSE,
            Boolean.TRUE, null, param);
      }

      // 不满足 - 累计送礼物次数逻辑

      // true - 有赚钱签
      List<GameLuckyGiftInventoryCache> makeMoneyInventory = generateMakeMoneyInventory(
          param.getQuantity(), inventory);
      if (CollectionUtils.isNotEmpty(makeMoneyInventory)) {
        return buildRewardResult(GameLuckyGiftModeEnum.ALL, Boolean.TRUE, Boolean.TRUE,
            Boolean.FALSE, makeMoneyInventory, param);
      }

      // true - 没有赚钱签
      return buildRewardResult(GameLuckyGiftModeEnum.ALL, Boolean.TRUE, Boolean.FALSE,
          Boolean.FALSE, inventory, param);
    }

    // 不满足 - 连续亏钱逻辑

    // 已满足 - 累计送礼物次数逻辑
    if (param.getGiftsCount() >= template.getGiftGiveTotal()) {

      // true - 有大于0倍的签
      List<GameLuckyGiftInventoryCache> awardedInventory = generateGtZeroFoldInventor(inventory);
      if (CollectionUtils.isNotEmpty(awardedInventory)) {

        GameLuckyGiftRewardResult rewardResult = buildRewardResult(GameLuckyGiftModeEnum.ALL,
            Boolean.TRUE, Boolean.TRUE, Boolean.TRUE, awardedInventory, param);
        // 如果为赚钱签则需要重置连续亏损
        rewardResult.setResetComboLossCount(
            rewardResult.getReward().getMultiple() > param.getQuantity());
        return rewardResult;
      }

      // false - 无大于0倍的签
      return buildRewardResult(GameLuckyGiftModeEnum.ALL, Boolean.FALSE, Boolean.FALSE,
          Boolean.TRUE, null, param);
    }

    // 不满足 - 累计送礼物次数要求
    GameLuckyGiftRewardResult rewardResult = buildRewardResult(GameLuckyGiftModeEnum.ALL,
        Boolean.TRUE, Boolean.TRUE, Boolean.FALSE, inventory, param);
    // 如果为赚钱签则需要重置连续亏损
    rewardResult.setResetComboLossCount(
        rewardResult.getReward().getMultiple() > param.getQuantity());

    return rewardResult;

  }

  /**
   * 开启续亏钱次数模式的情况下抽奖.
   *
   * @return bool:是否变动库存(true变动, false不动), 奖励
   */
  private GameLuckyGiftRewardResult runLoseMoneyMode(GameLuckyGiftParam param) {
    log.info("runLoseMoneyMode param:{}", JSON.toJSONString(param));

    // 获得模版配置
    LuckyGiftInventoryTemplateCache template = param.getTemplate();

    // 获得库存数据，没有数据则初始化并返回
    List<GameLuckyGiftInventoryCache> inventory = listRemainInventory(param);
    if (CollectionUtils.isEmpty(inventory)) {
      return null;
    }

    // 已满足 - 连续亏钱逻辑
    if (param.getComboLossCount() >= template.getComboLoss()) {

      // true - 有赚钱签
      List<GameLuckyGiftInventoryCache> makeMoneyInventory = generateMakeMoneyInventory(
          param.getQuantity(), inventory);

      if (CollectionUtils.isNotEmpty(makeMoneyInventory)) {
        return buildRewardResult(GameLuckyGiftModeEnum.LOSS, Boolean.TRUE, Boolean.TRUE,
            Boolean.FALSE, makeMoneyInventory, param);
      }

      // true - 没有赚钱签
      return buildRewardResult(GameLuckyGiftModeEnum.LOSS, Boolean.TRUE, Boolean.FALSE,
          Boolean.FALSE, inventory, param);
    }

    // 不满足 - 连续亏钱逻辑
    GameLuckyGiftRewardResult rewardResult = buildRewardResult(GameLuckyGiftModeEnum.LOSS,
        Boolean.TRUE, Boolean.TRUE, Boolean.FALSE, inventory, param);
    rewardResult.setResetComboLossCount(
        rewardResult.getReward().getMultiple() > param.getQuantity());

    return rewardResult;

  }

  /**
   * 开启连续礼物赠送就模式的情况下抽奖.
   *
   * @return bool:是否变动库存(true变动, false不动), 奖励
   */
  private GameLuckyGiftRewardResult runGiftMoneyMode(GameLuckyGiftParam param) {
    log.info("runGiftMoneyMode param:{}", JSON.toJSONString(param));

    // 获得模版配置
    LuckyGiftInventoryTemplateCache template = param.getTemplate();

    // 获得库存数据，没有数据则初始化并返回
    List<GameLuckyGiftInventoryCache> inventory = listRemainInventory(param);
    if (CollectionUtils.isEmpty(inventory)) {
      return null;
    }

    // 已满足 - 累计送礼物次数逻辑
    if (param.getGiftsCount() >= template.getGiftGiveTotal()) {

      // true - 有大于0倍的签
      List<GameLuckyGiftInventoryCache> awardedInventory = generateGtZeroFoldInventor(inventory);
      if (CollectionUtils.isNotEmpty(awardedInventory)) {

        return buildRewardResult(GameLuckyGiftModeEnum.GIFT, Boolean.TRUE, Boolean.TRUE,
            Boolean.TRUE, awardedInventory, param);
      }

      // true - 给1倍,且不变更库存
      return buildRewardResult(GameLuckyGiftModeEnum.GIFT, Boolean.FALSE, Boolean.FALSE,
          Boolean.TRUE, null, param);
    }

    // 不满足 - 累计送礼物次数逻辑

    // true - 有赚钱签
    List<GameLuckyGiftInventoryCache> makeMoneyInventory = generateMakeMoneyInventory(
        param.getQuantity(), inventory);
    if (CollectionUtils.isNotEmpty(makeMoneyInventory)) {
      return buildRewardResult(GameLuckyGiftModeEnum.GIFT, Boolean.TRUE, Boolean.TRUE,
          Boolean.FALSE, makeMoneyInventory, param);
    }

    // true - 没有赚钱签
    return buildRewardResult(GameLuckyGiftModeEnum.GIFT, Boolean.TRUE, Boolean.FALSE, Boolean.FALSE,
        inventory, param);

  }

  /**
   * 随机签.
   */
  private GameLuckyGiftRewardResult runRandomMode(GameLuckyGiftParam param) {
    log.info("runRandomMode param:{}", JSON.toJSONString(param));

    return buildRewardResult(GameLuckyGiftModeEnum.NONE, Boolean.TRUE, Boolean.TRUE, Boolean.TRUE,
        listRemainInventory(param), param);
  }

  /**
   * 返回抽奖结果等数据.
   *
   * @param mode                游戏模式
   * @param changeInventory     是否变动库存余额
   * @param resetComboLossCount 是否重置连续亏损次数
   * @param resetGiftsCount     是否重置连续送礼累计
   * @param makeMoneyInventory  签库存
   * @param param               游戏参数
   * @return 抽奖结果
   */
  private GameLuckyGiftRewardResult buildRewardResult(GameLuckyGiftModeEnum mode,
      Boolean changeInventory, Boolean resetComboLossCount, Boolean resetGiftsCount,
      List<GameLuckyGiftInventoryCache> makeMoneyInventory, GameLuckyGiftParam param) {

    return new GameLuckyGiftRewardResult()
        .setMode(mode)
        .setChangeInventory(changeInventory)
        .setResetComboLossCount(resetComboLossCount)
        .setResetGiftsCount(resetGiftsCount)
        // makeMoneyInventory 是空则表示返回一个固定的一倍
        .setReward(makeMoneyInventory == null ? new GameLuckyGiftInventoryCache()
            .setMultiple(1)
            .setQuantity(1)
            : drawLuckyGift(makeMoneyInventory, param)
        );
  }

  /**
   * 获得大于0倍的签库存.
   *
   * @param inventory 剩余库存签数
   * @return 大于0倍库存签数
   */
  private static List<GameLuckyGiftInventoryCache> generateGtZeroFoldInventor(
      List<GameLuckyGiftInventoryCache> inventory) {
    return inventory.stream().filter(v -> v.getMultiple() > 0).collect(Collectors.toList());
  }

  /**
   * 获得赚钱签库存.
   *
   * @param quantity  选择的礼物数量
   * @param inventory 剩余库存签数
   * @return 赚钱库存签数
   */
  private List<GameLuckyGiftInventoryCache> generateMakeMoneyInventory(Integer quantity,
      List<GameLuckyGiftInventoryCache> inventory) {
    return inventory.stream().filter(v -> v.getMultiple() > quantity).collect(Collectors.toList());
  }


  /**
   * 运行游戏并获得结果.
   */
  private GameLuckyGiftRewardResult runGame(GameLuckyGiftParam param) {
    log.info("runGame param:{}", JSON.toJSONString(param));

    // 开启了连击亏钱次数奖励与累计送礼次数奖励
    if (Boolean.TRUE.equals(param.getOpenComboLoss()) && Boolean.TRUE.equals(
        param.getOpenGiftGiveTotal())) {
      return runAllMode(param);
    }

    // 开启了连击亏钱次数奖励
    if (Boolean.TRUE.equals(param.getOpenComboLoss())) {
      return runLoseMoneyMode(param);
    }

    // 开启了累计送礼次数奖励
    if (Boolean.TRUE.equals(param.getOpenGiftGiveTotal())) {
      return runGiftMoneyMode(param);
    }

    // 随机
    return runRandomMode(param);
  }

  /**
   * 处理用户计数缓存问题.
   */
  private void processCountReset(GameLuckyGiftParam param, GameLuckyGiftRewardResult rewardResult) {

    if (Objects.equals(rewardResult.mode, GameLuckyGiftModeEnum.NONE)) {
      return;
    }

    // 移除连续亏损模式次数
    if (Boolean.TRUE.equals(param.getOpenComboLoss()) && rewardResult.getResetComboLossCount()) {

      gameLuckyGiftCacheService.removeComboLossCount(param.getUserId());
    }

    // 重置累计送礼次数
    if (Boolean.TRUE.equals(param.getOpenGiftGiveTotal()) && rewardResult.getResetGiftsCount()) {

      Integer newGiftsCount = gameLuckyGiftCacheService.getGiftsCount(param.getUserId(),
          param.getGiftId(), param.getQuantity());
      if (Objects.isNull(newGiftsCount) || param.getSysGiftGiveTotal() > newGiftsCount) {
        return;
      }
      gameLuckyGiftCacheService.removeGiftsCount(param.getUserId(), param.getGiftId(),
          param.getQuantity());
    }

  }

  /**
   * 获得用户头像.
   */
  private UserPropsResourcesDTO getUserAvatarFrameProps(UserProfileDTO sendUserProfile) {

    if (CollectionUtils.isEmpty(sendUserProfile.getUseProps())) {
      return new UserPropsResourcesDTO();
    }

    return Optional.ofNullable(sendUserProfile.getUseProps().stream()
        .filter(props -> Objects.nonNull(props.getPropsResources())
            && !Objects.equals(props.getPropsResources().getType(),
            PropsCommodityType.AVATAR_FRAME.name())
            && props.getExpireTime() > TimestampUtils.now().getTime())
        .findFirst().orElse(new UserUsePropsDTO())
        .getPropsResources()).orElse(new UserPropsResourcesDTO());
  }

  /**
   * 调用第三方幸运礼物API.
   */
  private ThirdPartyLotteryResult callThirdPartyLotteryApi(GameLuckyGiftParam param, List<String> orderIds) {
    try {
      // 根据 standardId 获取对应的API配置
      ApiAccount apiConfig = luckyGiftApiConfig.getConfigByStandardId(param.getStandardId());
      log.info("使用API配置, standardId: {}, config: {}", param.getStandardId(), apiConfig);

      Map<String, Object> requestData = new HashMap<>();
      requestData.put("room_id", param.getRoomId().toString());
      requestData.put("user_id", param.getUserId().toString());
      requestData.put("order_id", orderIds);
      requestData.put("app_id", apiConfig.getAppId());
      requestData.put("app_channel", apiConfig.getAppChannel());
      requestData.put("gift_id", param.getGiftId().intValue());
      requestData.put("gift_price", param.getGift().getGiftCandy().intValue());
      requestData.put("gift_num", param.getQuantity());
      requestData.put("gift_is_free", 0); // 0收费 1免费
      requestData.put("timestamp", System.currentTimeMillis() / 1000);
      
      // 生成签名
      String signature = generateSignature(requestData, apiConfig.getAppKey());
      requestData.put("signature", signature);
      
      log.info("调用第三方幸运礼物API，请求参数：{}", JSON.toJSONString(requestData));
      
      // 特别记录order_id的编码情况
      log.debug("order_id原始值: {}", JSON.toJSONString(orderIds));
      log.debug("order_id URL编码后: {}", URLEncoder.encode(JSON.toJSONString(orderIds), StandardCharsets.UTF_8));
      
      // 发送HTTP请求 - 使用Hutool的HttpUtil
      String requestBody = JSON.toJSONString(requestData);
      String response = HttpUtil.createPost(apiConfig.getUrl())
          .header("Content-Type", "application/json")
          .header("Accept", "application/json")
          .timeout(10000)
          .body(requestBody)
          .execute()
          .body();

      if (StringUtils.isBlank(response)) {
        log.error("第三方幸运礼物API返回空内容");
        return createFailedResult();
      }
      
      log.info("第三方幸运礼物API响应：{}", response);
      
      // 解析响应
      JSONObject responseJson = JSON.parseObject(response);
      if (responseJson.getInteger("code") == 200) {
        JSONObject data = responseJson.getJSONObject("data");
        if (data == null) {
          log.error("第三方幸运礼物API返回data为空");
          return createFailedResult();
        }
        
        JSONArray orderList = data.getJSONArray("OrderList");
        if (orderList == null || orderList.isEmpty()) {
          log.error("第三方幸运礼物API返回订单列表为空");
          return createFailedResult();
        }
        
        ThirdPartyLotteryResult result = new ThirdPartyLotteryResult();
        result.setSuccess(true);
        result.setOrderResults(new ArrayList<>());
        
        for (int i = 0; i < orderList.size(); i++) {
          JSONObject order = orderList.getJSONObject(i);
          OrderResult orderResult = new OrderResult();
          orderResult.setOrderId(order.getString("order_id"));
          orderResult.setIsWin(order.getInteger("is_win"));
          orderResult.setRewardNum(order.getInteger("reward_num"));
          result.getOrderResults().add(orderResult);
        }
        
        return result;
      } else {
        log.error("第三方幸运礼物API返回错误，code：{}，docs：{}", 
            responseJson.getInteger("code"), responseJson.getString("docs"));
        return createFailedResult();
      }
    } catch (Exception e) {
      log.error("调用第三方幸运礼物API异常", e);
      return createFailedResult();
    }
  }
  
  /**
   * 生成API签名.
   *
   * @param params 请求参数
   * @param appKey 应用密钥
   * @return 签名字符串
   */
  private String generateSignature(Map<String, Object> params, String appKey) {
    try {
      // 按键名升序排列参数
      TreeMap<String, Object> sortedParams = new TreeMap<>(params);
      
      // 构建签名字符串
      StringBuilder signBuilder = new StringBuilder();
      for (Map.Entry<String, Object> entry : sortedParams.entrySet()) {
        if (!"signature".equals(entry.getKey())) {
          if (!signBuilder.isEmpty()) {
            signBuilder.append("&");
          }
          
          String key = entry.getKey();
          Object value = entry.getValue();
          String valueStr;
          
          // 特殊处理order_id字段，需要URL编码
          if ("order_id".equals(key) && value instanceof List) {
            // 将List转为JSON字符串然后URL编码
            String jsonStr = JSON.toJSONString(value);
            valueStr = URLEncoder.encode(jsonStr, StandardCharsets.UTF_8);
          } else {
            valueStr = String.valueOf(value);
          }
          
          signBuilder.append(key).append("=").append(valueStr);
        }
      }
      signBuilder.append("&app_key=").append(appKey);
      
      log.debug("签名原始字符串: {}", signBuilder.toString());
      
      // MD5加密
      MessageDigest md = MessageDigest.getInstance("MD5");
      byte[] digest = md.digest(signBuilder.toString().getBytes("UTF-8"));
      
      // 转换为16进制字符串
      StringBuilder hexString = new StringBuilder();
      for (byte b : digest) {
        String hex = Integer.toHexString(0xff & b);
        if (hex.length() == 1) {
          hexString.append('0');
        }
        hexString.append(hex);
      }
      
      String signature = hexString.toString();
      log.debug("生成的签名: {}", signature);
      return signature;
    } catch (Exception e) {
      log.error("生成签名异常", e);
      return "";
    }
  }
  
  /**
   * 创建失败的结果.
   */
  private ThirdPartyLotteryResult createFailedResult() {
    ThirdPartyLotteryResult result = new ThirdPartyLotteryResult();
    result.setSuccess(false);
    result.setOrderResults(Collections.emptyList());
    return result;
  }

  /**
   * 获得中奖倍数.
   */
  private Long getSendAmount(Integer multiple, BigDecimal giftAmount) {

    if (multiple <= 0 || Objects.isNull(giftAmount) || giftAmount.longValue() <= 0) {
      return 0L;
    }

    return multiple * giftAmount.longValue();
  }

  /**
   * 倍数奖励效果Key.
   */
  public String getRewardMultipleType(Integer multiple) {

    GameLuckyGiftConfigCache giftConfig = getCacheTemplateCache();
    if (Objects.isNull(giftConfig)) {
      return GameLuckyGiftMultipleEnum.WIN.name();
    }

    if (multiple >= giftConfig.getWinStart() && multiple <= giftConfig.getWinEnd()) {
      return GameLuckyGiftMultipleEnum.WIN.name();
    }
    if (multiple >= giftConfig.getBigWinStart() && multiple <= giftConfig.getBigWinEnd()) {
      return GameLuckyGiftMultipleEnum.BIG_WIN.name();
    }
    if (multiple >= giftConfig.getSuperStart() && multiple <= giftConfig.getSuperEnd()) {
      return GameLuckyGiftMultipleEnum.SUPER.name();
    }
    return GameLuckyGiftMultipleEnum.WIN.name();

  }

  /**
   * 递减一，并返回实际数量.
   */
  private Long decrementInventoryQuantity(Long giftId, Integer quantity,
      Integer multiple) {

    return gameLuckyGiftCacheService.decrInventoryQuantity(giftId,
        quantity, multiple);
  }

  /**
   * 用户抽奖，使用第三方API或本地算法.
   */
  private GameLuckyGiftInventoryCache drawLuckyGift(
      List<GameLuckyGiftInventoryCache> listInventory, GameLuckyGiftParam param) {

    // 如果启用第三方API，则调用第三方接口
    if (luckyGiftApiConfig.isEnabled()) {
      return drawLuckyGiftWithThirdPartyApi(listInventory, param);
    }
    
    // 否则使用原有本地算法
    return drawLuckyGiftLocal(listInventory);
  }
  
  /**
   * 使用第三方API抽奖.
   */
  private GameLuckyGiftInventoryCache drawLuckyGiftWithThirdPartyApi(
      List<GameLuckyGiftInventoryCache> listInventory, GameLuckyGiftParam param) {
    
    try {
      // 生成订单ID列表（这里只有一个订单ID）
      List<String> orderIds = List.of(param.getId().toString());
      
      // 调用第三方API
      ThirdPartyLotteryResult apiResult = callThirdPartyLotteryApi(param, orderIds);
      
      if (apiResult.isSuccess() && !apiResult.getOrderResults().isEmpty()) {
        OrderResult firstOrder = apiResult.getOrderResults().get(0);
        
        // 根据第三方API返回的结果确定倍数
        int rewardMultiple = 1; // 默认1倍
        if (firstOrder.getIsWin() == 1 && firstOrder.getRewardNum() > 0) {
          // 第三方返回的奖励数量转换为倍数
          // 根据礼物价格计算倍数：奖励金额 / 礼物价格
          rewardMultiple = firstOrder.getRewardNum() / param.getGift().getGiftCandy().intValue();
          if (rewardMultiple <= 0) {
            rewardMultiple = 1;
          }
        }
        
        // 查找对应倍数的库存，如果没有则返回最接近的
        GameLuckyGiftInventoryCache selectedInventory = null;
        for (GameLuckyGiftInventoryCache inventory : listInventory) {
          if (inventory.getMultiple().equals(rewardMultiple)) {
            selectedInventory = inventory;
            break;
          }
        }
        
        // 如果没找到完全匹配的倍数，选择最接近的
        if (selectedInventory == null) {
          int finalRewardMultiple = rewardMultiple;
          selectedInventory = listInventory.stream()
              .min(Comparator.comparing(inv -> Math.abs(inv.getMultiple() - finalRewardMultiple)))
              .orElse(listInventory.get(0));
        }
        
        // 创建新的库存对象，使用API返回的倍数
        return new GameLuckyGiftInventoryCache()
            .setMultiple(rewardMultiple)
            .setQuantity(selectedInventory.getQuantity());
      }
    } catch (Exception e) {
      log.error("第三方API抽奖异常，降级使用本地算法", e);
    }
    
    // API调用失败，降级使用本地算法
    return drawLuckyGiftLocal(listInventory);
  }
  
  /**
   * 本地抽奖算法（原有逻辑）.
   */
  private GameLuckyGiftInventoryCache drawLuckyGiftLocal(
      List<GameLuckyGiftInventoryCache> listInventory) {

    long totalQuantity = listInventory.stream().mapToLong(GameLuckyGiftInventoryCache::getQuantity)
        .sum();

    BigDecimal randomValue = ArithmeticUtils.toBigDecimal(new Random().nextDouble());
    BigDecimal cumulativeProbability = BigDecimal.ZERO;

    for (GameLuckyGiftInventoryCache instance : listInventory) {

      BigDecimal instanceProbability = BigDecimal.valueOf(instance.getQuantity())
          .divide(BigDecimal.valueOf(totalQuantity), 10, RoundingMode.DOWN)
          .setScale(10, RoundingMode.DOWN);

      cumulativeProbability = cumulativeProbability.add(instanceProbability);
      if (randomValue.compareTo(cumulativeProbability) < 0) {
        return instance;
      }
    }

    // 如果无法抽中实例，则返回最后一个实例
    return listInventory.stream()
        .sorted(Comparator.comparing(GameLuckyGiftInventoryCache::getMultiple))
        .toList()
        .get(0);
  }

  /**
   * 获得现签库存.
   */
  private List<GameLuckyGiftInventoryCache> listRemainInventory(GameLuckyGiftParam param) {

    Map<Integer, Integer> mapRemainInventory = gameLuckyGiftCacheService.mapRemainInventory(
        param.getGiftId(),
        param.getQuantity(),
        param.getTemplate().getCaches().stream().map(GameLuckyGiftInventoryCache::getMultiple)
            .collect(Collectors.toSet())
    );

    if (CollectionUtils.isEmpty(mapRemainInventory)) {

      // 生成新的一批签
      return gameLuckyGiftCacheService.initRemainInventory(param.getGiftId(), param.getQuantity(),
          param.getTemplate().getCaches());
    }

    // 过滤库存数为0的签
    List<GameLuckyGiftInventoryCache> remainInventoryList = CollectionUtils.newArrayList();
    mapRemainInventory.forEach((multiple, quantity) -> {

      if (quantity <= 0L) {
        return;
      }

      remainInventoryList.add(new GameLuckyGiftInventoryCache()
          .setMultiple(multiple)
          .setQuantity(quantity)
      );
    });

    if (CollectionUtils.isEmpty(remainInventoryList)) {

      // 生成新的一批签
      return gameLuckyGiftCacheService.initRemainInventory(param.getGiftId(), param.getQuantity(),
          param.getTemplate().getCaches());
    }

    return remainInventoryList;
  }

  /**
   * 获得被赠送的礼物.
   */
  private GiftConfigDTO getGiftConfigDTO(Long giftId) {
    return giftCacheService.getById(giftId);
  }

  /**
   * 获得基础配置数据.
   */
  private GameLuckyGiftConfigCache getCacheTemplateCache() {

    GameLuckyGiftConfigCache configCache = gameLuckyGiftCacheService.getBaseConfig();
    if (Objects.nonNull(configCache)) {
      return configCache;
    }

    GameLuckyGiftRuleConfig giftRuleConfig = gameLuckyGiftRuleConfigService.getRuleConfig();
    if (Objects.isNull(giftRuleConfig)) {
      return null;
    }

    configCache = new GameLuckyGiftConfigCache()
        .setGiftShareRatio(giftRuleConfig.getGiftShareRatio())
        .setWinStart(giftRuleConfig.getWinStart())
        .setWinEnd(giftRuleConfig.getWinEnd())
        .setBigWinStart(giftRuleConfig.getBigWinStart())
        .setBigWinEnd(giftRuleConfig.getBigWinEnd())
        .setSuperStart(giftRuleConfig.getSuperStart())
        .setSuperEnd(giftRuleConfig.getSuperEnd());

    gameLuckyGiftCacheService.cacheBaseConfig(configCache);
    return configCache;
  }

  /**
   * 获得库存模版.
   */
  private LuckyGiftInventoryTemplateCache getInventoryTemplate(Long standardId, Integer quantity) {

    LuckyGiftInventoryTemplateCache inventoryTemplate = gameLuckyGiftCacheService
        .getInventoryTemplate(standardId, quantity);
    if (Objects.nonNull(inventoryTemplate)) {
      return inventoryTemplate;
    }

    LuckyGiftProbability probability = luckyGiftProbabilityService.getOneByStandardIdByQuantity(
        standardId, quantity);
    if (Objects.isNull(probability)) {
      return null;
    }

    List<LuckyGiftProbabilityDetails> detailsList = luckyGiftProbabilityDetailsService.listByProbabilityId(
        probability.getId());
    if (CollectionUtils.isEmpty(detailsList)) {
      return null;
    }

    LuckyGiftInventoryTemplateCache templateCache = new LuckyGiftInventoryTemplateCache()
        .setComboLoss(probability.getComboIncrements())
        .setGiftGiveTotal(probability.getGiftGiveTotal())
        .setSignTotal(probability.getSignTotal())
        .setCaches(detailsList.stream().map(details -> new GameLuckyGiftInventoryCache()
                .setQuantity(details.getQuantity())
                .setMultiple(details.getMultiple())
            ).toList()
        );

    gameLuckyGiftCacheService.cacheInventoryTemplate(standardId, quantity, templateCache);

    return templateCache;
  }

  /**
   * 是否开启了累计送礼次数.
   *
   * @return true 是
   */
  private Boolean isOpenGiftGiveTotal(LuckyGiftInventoryTemplateCache probabilityConfig) {
    return Objects.nonNull(probabilityConfig.getGiftGiveTotal())
        && probabilityConfig.getGiftGiveTotal() > 0;
  }
  
  /**
   * 第三方抽奖API结果.
   */
  @Data
  @Accessors(chain = true)
  static class ThirdPartyLotteryResult implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    
    private boolean success;
    private List<OrderResult> orderResults;
  }
  
  /**
   * 订单结果.
   */
  @Data
  @Accessors(chain = true)
  static class OrderResult implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    
    private String orderId;
    private Integer isWin; // 0未中 1中奖
    private Integer rewardNum; // 奖励数量
  }

  /**
   * 是否开启了用户连续亏钱次数.
   *
   * @return true 是
   */
  private Boolean isOpenComboLoss(LuckyGiftInventoryTemplateCache probabilityConfig) {
    return Objects.nonNull(probabilityConfig.getComboLoss())
        && probabilityConfig.getComboLoss() > 0;
  }

  /**
   * 发送金币.
   */
  private BigDecimal sendGold(Long userId, Long giftId, String sysOrigin, Long amount) {
    return ResponseAssert.requiredSuccess(
        walletGoldClient.changeBalance(GoldReceiptCmd.builder()
            .appIncome()
            .userId(userId)
            .eventId(giftId)
            .sysOrigin(SysOriginPlatformEnum.valueOf(sysOrigin))
            .origin(GoldOrigin.LUCKY_GIFT_GOLD_REWARD)
            .amount(ArithmeticUtils.toBigDecimal(amount))
            .build())
    ).getBalance().getDollarAmount();
  }

  /**
   * 发送中奖通知.
   *
   * @param param    参数
   * @param multiple 中奖倍数
   * @param balance  余额
   */
  private void sendMsg(GameLuckyGiftParam param, Integer multiple, BigDecimal balance, Long awardAmount) {

    GameLuckyGiftMsgCO giftMsg = new GameLuckyGiftMsgCO()
        .setSysOrigin(param.getSysOrigin())
        .setRoomId(param.getRoomId())
        .setRoomAccount(param.getRoomAccount())
        .setSendUserId(param.getUserId())
        .setAccount(param.getAccount())
        .setNickname(param.getNickname())
        .setUserAvatar(param.getUserAvatar())
        .setAvatarFrameCover(param.getAvatarFrameCover())
        .setAvatarFrameSvg(param.getAvatarFrameSvg())
        .setMultiple(multiple)
        .setAwardAmount(awardAmount)
        .setGiftId(param.getGiftId())
        .setMultipleType(getRewardMultipleType(multiple))
        .setGiftCover(param.getGift().getGiftPhoto())
        .setRegionCode(param.getRoomRegionCode())
        .setGiftQuantity(param.getQuantity())
        .setAcceptUserId(param.getAcceptUserId())
        .setAcceptNickname(userProfileGateway.getByUserId(param.getAcceptUserId())
            .getUserNickname())
        .setBalance(balance)
        .setGiftCandy(param.getGift().getGiftCandy())
        .setGlobalNews(Boolean.FALSE);

    int luckyGiftPopupMultiple = enumConfigCacheService.getValueInt(
        EnumConfigKey.LUCKY_GIFT_POPUP_MULTIPLE, giftMsg.getSysOrigin());
    // 中奖了发送飘窗
    if (awardAmount > 0) {
      giftMsg.setGlobalNews(Boolean.TRUE);
      imGroupClient.sendMessageBroadcast(
          BroadcastGroupMsgBodyCmd.builder()
              .toPlatform(SysOriginPlatformEnum.toEnum(giftMsg.getSysOrigin()))
              .type(GroupMessageTypeEnum.GAME_LUCKY_GIFT)
              .data(giftMsg)
              .build()
      );
    }

    if (StringUtils.isBlank(giftMsg.getRoomAccount())) {
      return;
    }

    imGroupClient.sendCustomMessage(giftMsg.getRoomAccount(),
        CustomGroupMsgBodyCmd.builder()
            .type(GroupMessageTypeEnum.GAME_LUCKY_GIFT)
            .data(giftMsg)
            .build());
  }


  /**
   * 组装幸运礼物参数.
   */
  private GameLuckyGiftParam toBuildGameLuckyGift(GameLuckyGiftParamCmd cmd) {

    GiftConfigDTO gift = getGiftConfigDTO(cmd.getGiftId());
    if (Objects.isNull(gift)) {
      return null;
    }

    LuckyGiftInventoryTemplateCache template = getInventoryTemplate(gift.getStandardId(),
        cmd.getQuantity());
    if (Objects.isNull(template) || CollectionUtils.isEmpty(template.getCaches())) {
      return null;
    }

    UserProfileDTO sendUserProfile = userProfileAppConvertor.toUserProfileDTO(
        userProfileGateway.getByUserId(cmd.getUserId()));
    if (Objects.isNull(sendUserProfile)) {
      return null;
    }

    // 获得用户头像框
    UserPropsResourcesDTO avatarFrameProps = getUserAvatarFrameProps(sendUserProfile);

    // 优先使用cmd传递的standardId，如果为空则降级使用gift的standardId
    Long standardId = Objects.nonNull(cmd.getStandardId()) ? cmd.getStandardId() : gift.getStandardId();

    return new GameLuckyGiftParam()
        .setId(cmd.getId())
        .setGift(gift)
        .setTemplate(template)
        .setGiftId(gift.getId())
        .setRoomId(cmd.getRoomId())
        .setUserId(cmd.getUserId())
        .setQuantity(cmd.getQuantity())
        .setSysOrigin(cmd.getSysOrigin())
        .setStandardId(standardId)
        .setGiftsCount(cmd.getGiftsCount())
        .setComboCount(cmd.getGiftCombos())
        .setRoomAccount(cmd.getRoomAccount())
        .setAcceptUserId(cmd.getAcceptUserId())
        .setRoomRegionCode(cmd.getRegionCode())
        .setOpenComboLoss(cmd.getOpenComboLoss())
        .setComboLossCount(cmd.getComboLossCount())
        .setNickname(sendUserProfile.getUserNickname())
        .setAccount(sendUserProfile.actualAccount())
        .setUserAvatar(sendUserProfile.getUserAvatar())
        .setSysGiftGiveTotal(cmd.getSysGiftGiveTotal())
        .setAvatarFrameCover(avatarFrameProps.getCover())
        .setOpenGiftGiveTotal(cmd.getOpenGiftGiveTotal())
        .setAvatarFrameSvg(avatarFrameProps.getSourceUrl());
  }

  /**
   * 获得幸运礼物目标比例.
   */
  public BigDecimal getLuckyGiftTargetRatio(String sysOrigin) {

    if (StringUtils.isBlank(sysOrigin)) {
      sysOrigin = SysOriginPlatformEnum.ASWAT.name();
    }

    BigDecimal targetRatio = enumConfigCacheService.getValueBigDecimal(
        EnumConfigKey.ACCEPT_ORDINARY_GIFT_TARGET_RATIO,
        sysOrigin);

    if (ArithmeticUtils.lteZero(targetRatio)) {
      return BigDecimal.ZERO;
    }

    return targetRatio;
  }

  /**
   * 获得幸运礼物比例普通榜单比例处理, 固定10%.
   */
  public BigDecimal getLuckyGiftShareRatio(String sysOrigin) {
    return getLuckyGiftTargetRatio(sysOrigin);
  }

  public static BigDecimal convertBigDecimal(String input) {
    if (StringUtils.isBlank(input)) {
      log.info("获取---幸运礼物比例---为空");
      return BigDecimal.valueOf(0.2).setScale(2, RoundingMode.HALF_UP);
    }
    try {
      // 直接转换为BigDecimal并设置精度
      return new BigDecimal(input).setScale(2, RoundingMode.HALF_UP);
    } catch (NumberFormatException e) {
      log.info("获取---幸运礼物比例---发生异常");
      return BigDecimal.valueOf(0.2).setScale(2, RoundingMode.HALF_UP);
    }
  }

  /**
   * 返回结果.
   */
  @Data
  @Accessors(chain = true)
  static class GameLuckyGiftRewardResult implements Serializable {

    @Serial
    private static final long serialVersionUID = 1L;

    /**
     * 游戏模式.
     */
    private GameLuckyGiftModeEnum mode;

    /**
     * 是否修改签仓库库存(true是).
     */
    private Boolean changeInventory;

    /**
     * 是否重置送礼物次数(true是).
     */
    private Boolean resetGiftsCount;

    /**
     * 是否重置连续亏钱次数(true是).
     */
    private Boolean resetComboLossCount;

    /**
     * 出奖奖励.
     */
    private GameLuckyGiftInventoryCache reward;

  }

  /**
   * 幸运礼物中整合基础参数模版.
   *
   * @author pengshigang on 2023/03/23
   */
  @Data
  @Accessors(chain = true)
  static class GameLuckyGiftParam implements Serializable {

    @Serial
    private static final long serialVersionUID = 1L;

    /**
     * 记录.
     */
    @JsonSerialize(using = ToStringSerializer.class)
    private Long id;

    /**
     * 系统.
     */
    private String sysOrigin;

    /**
     * 房间ID.
     */
    @JsonSerialize(using = ToStringSerializer.class)
    private Long roomId;

    /**
     * 房间账号.
     */
    private String roomAccount;

    /**
     * 数量.
     */
    private Integer quantity;

    /**
     * 礼物ID.
     */
    @JsonSerialize(using = ToStringSerializer.class)
    private Long giftId;

    /**
     * 规格id.
     */
    @JsonSerialize(using = ToStringSerializer.class)
    private Long standardId;

    /**
     * 礼物信息.
     */
    private GiftConfigDTO gift;

    /**
     * 连续点击次数.
     */
    private Integer comboCount;

    /**
     * 获得用户累计送礼次数.
     */
    private Integer giftsCount;

    /**
     * 获得送礼用户连续亏钱次数.
     */
    private Integer comboLossCount;

    /**
     * 是否开启了用户连续亏钱次数模式.
     */
    private Boolean openComboLoss;

    /**
     * 是否开启了累计送礼次数模式.
     */
    private Boolean openGiftGiveTotal;

    /**
     * 系统设定累计礼物次数.
     */
    private Integer sysGiftGiveTotal;

    /**
     * 幸运礼物库存基础配置-模版不做业务改动.
     */
    private LuckyGiftInventoryTemplateCache template;

    /**
     * 房间区域code.
     */
    private String roomRegionCode;

    /**
     * 用户ID.
     */
    @JsonSerialize(using = ToStringSerializer.class)
    private Long userId;

    /**
     * 账号
     */
    private String account;

    /**
     * 昵称.
     */
    private String nickname;

    /**
     * 用户头像.
     */
    private String userAvatar;

    /**
     * 头像框封面.
     */
    private String avatarFrameCover;

    /**
     * 头像框资源链接.
     */
    private String avatarFrameSvg;

    /**
     * 接受礼物用户ID.
     */
    @JsonSerialize(using = ToStringSerializer.class)
    private Long acceptUserId;

  }

}
