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

import com.red.circle.component.redis.service.RedisService;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.response.CommonErrorCode;
import com.red.circle.other.app.common.game.GameLudoCommon;
import com.red.circle.other.app.dto.cmd.game.GameLudoEndCmd;
import com.red.circle.other.infra.database.mongo.entity.game.ludo.GameLudoPlayer;
import com.red.circle.other.infra.database.mongo.entity.game.ludo.GameLudoRun;
import com.red.circle.other.infra.database.mongo.entity.game.ludo.GameStatus;
import com.red.circle.other.infra.database.mongo.entity.game.ludo.GameStatusEvent;
import com.red.circle.other.infra.database.mongo.service.game.GameLudoRunService;
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.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.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

/**
 * 结算游戏.
 *
 * @author pengliang on 2021/7/28
 */
@Component
@RequiredArgsConstructor
public class GameLudoLatestEndCmdExe {

  private final RedisService redisService;
  private final GameLudoCommon gameLudoCommon;
  private final WalletGoldClient walletGoldClient;
  private final GameLudoRunService gameLudoRunService;

  public void execute(GameLudoEndCmd cmd) {
    cmd.checkFilterRanking();
    String lockKey = "LUDO_END:" + cmd.getId();
    try {
      // 2067 操作冲突
      ResponseAssert.isTrue(CommonErrorCode.OPERATION_CONFLICT,
          redisService.lock(lockKey, 60));

      GameLudoRun gameLudoRun = gameLudoRunService.get(cmd.getId());
      // 8004 游戏不存在
      ResponseAssert.notNull(GameErrorCode.GAME_NOT_EXIST, gameLudoRun);
      fillRankingIndexValue(cmd, gameLudoRun);
      // 8026 游戏状态不在进行中
      ResponseAssert.isTrue(GameErrorCode.GAME_STATE_NOT_PROGRESS,
          Objects.equals(gameLudoRun.getGameStatus(), GameStatus.PROGRESS));

      // 修改游戏历史状态
      gameLudoRun.setGameStatus(GameStatus.END);
      gameLudoRun.setGameStatusEvent(getEvent(cmd));

      sendVictoryGold(cmd, gameLudoRun);

      gameLudoCommon.historyProcessing(gameLudoRun,
          getVictoryCheckerboardColor(cmd, gameLudoRun));
    } finally {
      redisService.unlock(lockKey);
    }
  }

  private void fillRankingIndexValue(GameLudoEndCmd cmd, GameLudoRun gameLudoRun) {
    Map<String, Integer> stringIntegerMap = cmd.rankingsToMap();
    gameLudoRun.setPlayers(gameLudoRun.getPlayers().stream()
        .peek(player -> player.setRankingIndex(
            stringIntegerMap.get(player.getCheckerboardColor().name())))
        .collect(Collectors.toList()));
  }

  private String getVictoryCheckerboardColor(GameLudoEndCmd cmd, GameLudoRun gameLudoRun) {
    return gameLudoRun.getPlayerByColor(cmd.getVictory()).getCheckerboardColor().name();
  }

  private GameStatusEvent getEvent(GameLudoEndCmd cmd) {
    if (Objects.equals(cmd.getEndType(), "NORMAL")) {
      return GameStatusEvent.NORMAL_SETTLEMENT;
    }

    if (Objects.equals(cmd.getEndType(), "NOT_EXIST_PLAYER")) {
      return GameStatusEvent.DISCONNECTED_DISBAND;
    }

    return GameStatusEvent.OTHER__DISBAND;
  }

  private void sendVictoryGold(GameLudoEndCmd cmd, GameLudoRun gameLudoRun) {

    // 1014 数据异常
    ResponseAssert
        .isTrue(CommonErrorCode.DATA_ERROR, CollectionUtils.isNotEmpty(gameLudoRun.getPlayers()));
    GameLudoPlayer victory = gameLudoRun.getPlayerByColor(cmd.getVictory());
    // 80010 没有找到胜利者
    ResponseAssert.notNull(GameErrorCode.NO_WINNER_FOUND, victory);

    if (ArithmeticUtils.lteZero(gameLudoRun.bonusRatio())) {
      return;
    }
    walletGoldClient.changeBalance(GoldReceiptCmd.builder()
        .appIncome()
        .eventId(gameLudoRun.getId())
        .userId(victory.getPlayerProfile().getId())
        .sysOrigin(cmd.requireReqSysOriginEnum())
        .origin(GoldOrigin.LUDO_VICTORY)
        .amount(gameLudoRun.bonusRatio())
        .build());
  }

}
