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

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
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.DaysRankingCO;
import com.red.circle.game.fruit.application.dto.clientobject.GameInfoCO;
import com.red.circle.game.fruit.application.dto.clientobject.UserRoundBetCO;
import com.red.circle.game.fruit.application.dto.cmd.TokenQryCmd;
import com.red.circle.game.fruit.application.dto.enums.MessageTypeTopic;
import com.red.circle.game.fruit.core.domain.gateway.DaysRankingGateway;
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.GameFruitPrize;
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.GameFruitUserDaysRanking;
import com.red.circle.game.fruit.infrastructure.database.memory.service.GameFruitMemoryService;
import com.red.circle.game.fruit.infrastructure.database.repository.GameCodeManageService;
import com.red.circle.game.fruit.infrastructure.database.repository.GameFruitDaysRecordService;
import com.red.circle.game.fruit.infrastructure.database.repository.GameFruitLotteryRecordService;
import com.red.circle.game.fruit.infrastructure.database.repository.GameFruitPrizeService;
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.other.inner.endpoint.sys.SysGameListConfigClient;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.other.inner.asserts.user.UserErrorCode;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.other.inner.endpoint.user.user.UserProfileClient;
import com.red.circle.wallet.inner.endpoint.wallet.WalletGoldClient;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

/**
 * @author lisizhe 2023/12/1  17:23
 */
@Component
@RequiredArgsConstructor
public class GameInfoQryExe {

  private final MessageSender messageSender;
  private final WalletGoldClient walletGoldClient;
  private final UserProfileClient userProfileClient;
  private final DaysRankingGateway daysRankingGateway;
  private final GameFruitAppConvertor gameFruitAppConvertor;
  private final GameFruitRuleService gameFruitRuleService;
  private final GameFruitUserService gameFruitUserService;
  private final GameFruitPrizeService gameFruitPrizeService;
  private final GameCodeManageService gameCodeManageService;
  private final SysGameListConfigClient gameListConfigClient;
  private final GameFruitMemoryService gameFruitMemoryService;
  private final GameFruitUserBetGateway gameFruitUserBetGateway;
  private final GameFruitDaysRecordService gameFruitDaysRecordService;
  private final GameFruitRoundRecordService gameFruitRoundRecordService;
  private final GameFruitLotteryRecordService gameFruitLotteryRecordService;

  public void execute(TokenQryCmd cmd) {
    Long userId = cmd.getUid();
    // 用户数据
    UserProfileDTO userProfileDTO = ResponseAssert.requiredSuccess(
        userProfileClient.getByUserId(userId));

    if (Objects.isNull(userProfileDTO)) {
      messageSender.sendTopic(
          TopicMessage.of(
              getTopicId(cmd), ResultResponse.failure(
                  UserErrorCode.USER_INFO_NOT_FOUND)));
      return;
    }

    String sysOrigin = userProfileDTO.getOriginSys();

    // 检查游戏是否正常运行
    Set<Long> gameIds = gameCodeManageService.getGameIdsByCode("GAME_FRUIT");

    if (CollectionUtils.isEmpty(gameIds)) {
      messageSender.sendTopic(
          TopicMessage.of(
              MessageTypeTopic.USER_BET_MSG.getTopicId(cmd.getUid()), ResultResponse.failure(
                  GameErrorCode.THE_GAME_HAS_BEEN_TAKEN_DOWN)));
    }
    // 校验是否有游戏下架
    Boolean isUnshelve = ResponseAssert.requiredSuccess(
        gameListConfigClient.checkGameUnshelve(gameIds));

    // 游戏是否下架
    if (isUnshelve) {
      messageSender.sendTopic(
          TopicMessage.of(
              MessageTypeTopic.USER_BET_MSG.getTopicId(cmd.getUid()), ResultResponse.failure(
                  GameErrorCode.THE_GAME_HAS_BEEN_TAKEN_DOWN)));
    }

    // 游戏信息（游戏轮数、游戏读秒、奖品押注情况&热点、下注单位）
    //游戏信息
    GameFruitRoundRecord gameFruitRoundRecord = gameFruitRoundRecordService.getGameRound();
    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();
    // 获取本轮读秒
    int roundTime = gameFruitMemoryService.getRoundSec();
    if (roundTime > GameTimelineManage.totalSecond) {
      messageSender.sendTopic(
          TopicMessage.of(
              getTopicId(cmd), ResultResponse.failure(
                  GameErrorCode.WAIT_NEXT_GAME)));
      return;
    }

    // 更新用户信息
    updateGameUser(userId, sysOrigin);
    // 奖品押注情况
    List<UserRoundBetCO> userRoundBetCache = gameFruitAppConvertor.toUserRoundBetCaches(
        gameFruitUserBetGateway.listLotteryUser(gameId,
            userId));
    // 榜单top3
    List<GameFruitUserDaysRanking> daysRankings = daysRankingGateway.listRankingTop(3, sysOrigin);

    Map<Long, UserProfileDTO> userProfileMap = ResponseAssert.requiredSuccess(
        userProfileClient.mapByUserIds(
            daysRankings.stream().map(GameFruitUserDaysRanking::getUserId).collect(
                Collectors.toSet())));

    List<DaysRankingCO> resultRankings = new ArrayList<>();
    if (CollectionUtils.isNotEmpty(daysRankings)) {
      resultRankings = daysRankings.stream().map(item -> {
        DaysRankingCO co = gameFruitAppConvertor.toDaysRankingCO(item);
        co.setUserInfo(
            gameFruitAppConvertor.toGameUserInfoCO(userProfileMap.get(item.getUserId())));
        return co;
      }).toList();
    }

    // 奖品信息
    List<GameFruitPrize> gameFruitPrize = gameFruitPrizeService.listPrize(sysOrigin);

    if (CollectionUtils.isEmpty(gameFruitPrize) || !Objects.equals(gameFruitPrize.size(), 8)) {
      messageSender.sendTopic(
          TopicMessage.of(
              getTopicId(cmd), ResultResponse.failure(
                  GameErrorCode.INCOMPLETE_PRIZE)));
      return;
    }
    // 赚取金币
    GameFruitUserDaysRanking userBet = daysRankingGateway.getUserEarning(userId);
    // 获取游戏规则
    GameFruitRule gameFruitRule = gameFruitRuleService.getRule(sysOrigin);

    if (Objects.isNull(gameFruitRule)) {
      messageSender.sendTopic(
          TopicMessage.of(
              getTopicId(cmd), ResultResponse.failure(
                  GameErrorCode.RULE_NOT_CONFIGURED)));
      return;
    }

    String[] wager = gameFruitRule.getWager().split(",");
    // 下注单位
    if (Objects.equals(wager.length, 3)) {
      messageSender.sendTopic(
          TopicMessage.of(
              getTopicId(cmd), ResultResponse.failure(
                  GameErrorCode.THE_BET_AMOUNT_IS_NOT_CONFIGURED)));
      return;
    }

    messageSender.sendTopic(
        TopicMessage.of(
            getTopicId(cmd),
            ResultResponse.success(new GameInfoCO()
                .setGameId(gameId)
                .setUserInfo(gameFruitAppConvertor.toGameUserInfoCO(userProfileDTO))
                .setRoundTime(gameFruitMemoryService.getRoundSec())
                .setRound(gameFruitRoundRecord.getRound())
                .setUserRoundBetCaches(userRoundBetCache)
                .setDaysRankings(resultRankings)
                .setGameFruitPrizes(gameFruitPrize)
                .setGold(getGold(userId))
                .setEarning(Objects.isNull(userBet) ? BigDecimal.ZERO : userBet.getEarning())
                .setWager(gameFruitRule.getWager())
                .setVegetableImg(gameFruitRule.getVegetableImg())
                .setPizzaImg(gameFruitRule.getPizzaImg())
                .setLotteryHistory(getLotteryHistory(sysOrigin))
            )
        ));
  }

  private String getTopicId(TokenQryCmd cmd) {
    return MessageTypeTopic.GAME_FRUIT_MSG.getTopicId(cmd.getToken());
  }

  public void updateGameUser(Long userId, String origin) {
    // 判断改用户是否存在游戏用户中，不存在则新增游戏用户数据
    GameFruitUser user = gameFruitUserService.getGameUser(userId);
    if (Objects.isNull(user)) {
      // 新增人数
      gameFruitUserService.addGameUser(userId, origin);
      gameFruitDaysRecordService.updateNewNum(userId, origin);
      return;
    }
    long time = DateUtil.between(DateUtil.beginOfDay(user.getLastActiveTime()),
        new Date(), DateUnit.DAY);
    if (time > 0) {
      gameFruitDaysRecordService.updatePlayerNum(userId, origin);
      gameFruitUserService.updateUserActiveTime(userId);
    }
  }

  /**
   * 金币余额
   */
  private BigDecimal getGold(Long userId) {
    return ResponseAssert.requiredSuccess(walletGoldClient.getBalance(userId))
        .getDollarAmount();
  }

  /**
   * 获取开奖历史
   */
  private List<GameFruitLotteryRecord> getLotteryHistory(String sysOrigin) {
    List<GameFruitRoundRecord> gameFruitRoundRecords = gameFruitRoundRecordService.limitRound(null,
        9);

    GameFruitRule gameFruitRule = gameFruitRuleService.getRule(sysOrigin);

    Map<Long, List<GameFruitLotteryRecord>> gameFruitLotteryRecordMap = gameFruitLotteryRecordService.listLotteryHistoryByGameIds(
        gameFruitRoundRecords.stream().map(GameFruitRoundRecord::getId).collect(
            Collectors.toSet()));

    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().setAward(prize.getAward())
          .setProb(prize.getProb()).setPrizeName(prizeName).setPrizeImg(prizeImg)
          .setPrizeType(prize.getPrizeType()).setPrizeId(0L).setGameId(prize.getGameId()));
    });

    return gameFruitLotteryRecords.stream()
        .sorted(Comparator.comparingLong(GameFruitLotteryRecord::getGameId).reversed()).toList();
  }
}
