package com.red.circle.game.fruit.application.command.query;

import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.dto.ResultResponse;
import com.red.circle.game.fruit.application.convertor.GameFruitAppConvertor;
import com.red.circle.game.fruit.application.dto.GameTimelineManage;
import com.red.circle.game.fruit.application.dto.clientobject.GameFruitUserBetRecordCO;
import com.red.circle.game.fruit.application.dto.clientobject.LotteryInfoCO;
import com.red.circle.game.fruit.application.dto.enums.MessageTypeTopic;
import com.red.circle.game.fruit.core.domain.gateway.GameFruitUserBetGateway;
import com.red.circle.game.fruit.infrastructure.database.entity.GameFruitLotteryRecord;
import com.red.circle.game.fruit.infrastructure.database.entity.GameFruitRoundRecord;
import com.red.circle.game.fruit.infrastructure.database.entity.GameFruitRule;
import com.red.circle.game.fruit.infrastructure.database.entity.GameFruitUser;
import com.red.circle.game.fruit.infrastructure.database.entity.GameFruitUserBetRecord;
import com.red.circle.game.fruit.infrastructure.database.memory.service.GameFruitMemoryService;
import com.red.circle.game.fruit.infrastructure.database.repository.GameFruitLotteryRecordService;
import com.red.circle.game.fruit.infrastructure.database.repository.GameFruitRoundRecordService;
import com.red.circle.game.fruit.infrastructure.database.repository.GameFruitRuleService;
import com.red.circle.game.fruit.infrastructure.database.repository.GameFruitUserService;
import com.red.circle.game.fruit.infrastructure.websocket.message.MessageSender;
import com.red.circle.game.fruit.infrastructure.websocket.message.TopicMessage;
import com.red.circle.other.inner.asserts.GameErrorCode;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.num.ArithmeticUtils;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.other.inner.endpoint.user.user.UserProfileClient;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * @author lisizhe 2023/12/8  9:44
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class LotteryInfoQryExe {

  private final MessageSender messageSender;
  private final UserProfileClient userProfileClient;
  private final GameFruitRuleService gameFruitRuleService;
  private final GameFruitUserService gameFruitUserService;
  private final GameFruitAppConvertor gameFruitAppConvertor;
  private final GameFruitMemoryService gameFruitMemoryService;
  private final GameFruitUserBetGateway gameFruitUserBetGateway;
  private final GameFruitRoundRecordService gameFruitRoundRecordService;
  private final GameFruitLotteryRecordService gameFruitLotteryRecordService;

  public void execute(Long gameId, Long userId) {
    // 获取本轮读秒
    int roundTime = gameFruitMemoryService.getRoundSec();

    if (roundTime > GameTimelineManage.lotteryDrawSecond) {
      messageSender.sendTopic(
          TopicMessage.of(
              MessageTypeTopic.DRAW_LOTTERY_MSG.getTopicId(userId),
              ResultResponse.failure(GameErrorCode.NOT_LOTTERY_TIME)
          ));
      return;
    }
    if (Objects.isNull(gameId)) {
      messageSender.sendTopic(
          TopicMessage.of(
              MessageTypeTopic.DRAW_LOTTERY_MSG.getTopicId(userId),
              ResultResponse.failure(GameErrorCode.GAME_NOT_EXIST)
          ));
      return;
    }
//    GameFruitRoundRecord gameFruitRoundRecord = gameFruitRoundRecordService.getGameRound();
    GameFruitRoundRecord gameFruitRoundRecord = gameFruitRoundRecordService.getById(gameId);
    if (Objects.isNull(gameFruitRoundRecord)) {
      messageSender.sendTopic(
          TopicMessage.of(
              MessageTypeTopic.DRAW_LOTTERY_MSG.getTopicId(userId),
              ResultResponse.failure(GameErrorCode.GAME_NOT_EXIST)
          ));
      return;
    }
//    Long gameId = gameFruitRoundRecord.getId();
    // 本轮开奖信息
    LotteryInfoCO lotteryInfo = new LotteryInfoCO();

    //校验是否开奖
    if (CollectionUtils.isEmpty(gameFruitLotteryRecordService.getRoundLotteryPrize(gameId))) {
      messageSender.sendTopic(
          TopicMessage.of(
              MessageTypeTopic.DRAW_LOTTERY_MSG.getTopicId(userId),
              ResultResponse.failure(GameErrorCode.NO_RECORD_OF_WINNING)
          ));
      return;
    }

    GameFruitUser gameFruitUser = gameFruitUserService.getGameUser(userId);

    List<GameFruitLotteryRecord> gameFruitLotteryRecords = getLotteryHistory(gameId,
        gameFruitUser.getSysOrigin());

    // 查询本轮得奖排名
    List<GameFruitUserBetRecordCO> gameFruitUserBetRecords = getTheRoundRanking(gameId,
        gameFruitUser.getSysOrigin());

    BigDecimal earnings = gameFruitUserBetGateway.getUserEarnings(
        userId, gameId);

    BigDecimal sumInvest = gameFruitUserBetGateway.sumInvest(
        userId, gameId);

    lotteryInfo.setLotteryPrizes(gameFruitLotteryRecords);
    // 下注金额
    lotteryInfo.setBets(Objects.isNull(sumInvest) ? BigDecimal.ZERO : sumInvest);

    lotteryInfo.setGameId(gameId);
    // 收益
    lotteryInfo.setEarnings(
        Objects.isNull(earnings) ? BigDecimal.ZERO : earnings);
    if (CollectionUtils.isNotEmpty(gameFruitUserBetRecords)) {

      Map<Long, UserProfileDTO> userProfileMap = ResponseAssert.requiredSuccess(
          userProfileClient.mapByUserIds(
              gameFruitUserBetRecords.stream().map(GameFruitUserBetRecordCO::getUserId).collect(
                  Collectors.toSet())));
      // 得奖排名
      lotteryInfo.setGameFruitUserBetRecords(gameFruitUserBetRecords.stream().peek(
              item -> item.setUserInfo(
                  gameFruitAppConvertor.toGameUserInfoCO(userProfileMap.get(item.getUserId()))))
              .sorted(Comparator.comparing(GameFruitUserBetRecordCO::getSum).reversed())
          .toList());
    }

    messageSender.sendTopic(
        TopicMessage.of(
            MessageTypeTopic.DRAW_LOTTERY_MSG.getTopicId(userId),
            ResultResponse.success(lotteryInfo)
        ));
  }

  /**
   * 查询本轮得奖排名
   */
  private List<GameFruitUserBetRecordCO> getTheRoundRanking(Long gameId, String sysOrigin) {

    Map<Long, List<GameFruitUserBetRecord>> gameFruitUserBetRecordsMap = gameFruitUserBetGateway.gameFruitWinnerMapList(
        gameId, sysOrigin);

    return gameFruitUserBetRecordsMap.keySet()
        .stream().map(k -> {
          List<GameFruitUserBetRecord> userBetRecords = gameFruitUserBetRecordsMap.get(k);
          // 计算用户得奖总额
          BigDecimal total = ArithmeticUtils.toBigDecimal(
              userBetRecords.stream().mapToDouble(item -> item.getInvest().multiply(
                      ArithmeticUtils.toBigDecimal(item.getAward()))
                  .doubleValue()).sum());

          return new GameFruitUserBetRecordCO().setUserId(k).setSum(total);
        }).toList();
  }


  /**
   * 获取开奖历史
   */
  private List<GameFruitLotteryRecord> getLotteryHistory(Long gameId, String sysOrigin) {

    GameFruitRule gameFruitRule = gameFruitRuleService.getRule(sysOrigin);

    Map<Long, List<GameFruitLotteryRecord>> gameFruitLotteryRecordMap = gameFruitLotteryRecordService.listLotteryHistoryByGameIds(
        Collections.singleton(gameId));

    List<GameFruitLotteryRecord> gameFruitLotteryRecords = new ArrayList<>();

    gameFruitLotteryRecordMap.keySet().forEach(k -> {
      List<GameFruitLotteryRecord> lotteryRecords = gameFruitLotteryRecordMap.get(k);
      GameFruitLotteryRecord prize = lotteryRecords.get(0);
      if (lotteryRecords.size() == 1) {
        gameFruitLotteryRecords.add(prize);
        return;
      }
      String prizeImg = prize.getPrizeType() == 1 ? gameFruitRule.getVegetableImg()
          : gameFruitRule.getPizzaImg();

      String prizeName = prize.getPrizeType() == 1 ? "蔬菜"
          : "披萨";

      gameFruitLotteryRecords.add(new GameFruitLotteryRecord()
          .setPrizeId(0L).setAward(prize.getAward())
          .setProb(prize.getProb()).setPrizeName(prizeName).setPrizeImg(prizeImg)
          .setPrizeType(prize.getPrizeType()));
    });

    return gameFruitLotteryRecords;
  }
}
