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

import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.other.app.convertor.game.GameLudoAppConvertor;
import com.red.circle.other.app.convertor.user.UserProfileAppConvertor;
import com.red.circle.other.app.dto.cmd.game.GameLudoJoinCmd;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.infra.database.cache.service.other.GameCacheService;
import com.red.circle.other.infra.database.mongo.entity.game.ludo.CheckerboardColor;
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.service.game.GameLudoRunService;
import com.red.circle.other.inner.asserts.GameErrorCode;
import com.red.circle.other.inner.asserts.user.UserErrorCode;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.tool.core.date.TimestampUtils;
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.math.BigDecimal;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

/**
 * 加入游戏.
 *
 * @author pengliang on 2021/7/27
 */
@Component
@RequiredArgsConstructor
public class GameLudoJoinCmdExe {

  private final GameCacheService gameCacheService;
  private final WalletGoldClient walletGoldClient;
  private final UserProfileGateway userProfileGateway;
  private final GameLudoRunService gameLudoRunService;
  private final GameLudoAppConvertor gameLudoAppConvertor;
  private final UserProfileAppConvertor userProfileAppConvertor;

  public void execute(GameLudoJoinCmd cmd) {
    UserProfileDTO userProfile = userProfileAppConvertor.toUserProfileDTO(
        userProfileGateway.getByUserId(cmd.getPlayerUserId()));
    // 4009 没有找到用户信息
    ResponseAssert.notNull(UserErrorCode.USER_INFO_NOT_FOUND, userProfile);

    GameLudoRun gameLudoRun = gameLudoRunService.get(cmd.getId());
    // 8004 游戏不存在
    ResponseAssert.notNull(GameErrorCode.GAME_NOT_EXIST, gameLudoRun);
    // 8006 不可加入游戏
    ResponseAssert.isTrue(GameErrorCode.CANNOT_JOIN_GAME,
        Objects.equals(gameLudoRun.getGameStatus(), GameStatus.WAIT));
    // 8009 已加入游戏
    ResponseAssert.isFalse(GameErrorCode.HAS_JOINED_GAME,
        gameCacheService.isJoinGameLudo(gameLudoRun.getId(), cmd.getPlayerUserId()));
    ResponseAssert.isFalse(GameErrorCode.HAS_JOINED_GAME,
        gameLudoRun.exitsPlayer(cmd.getPlayerUserId()));

    BigDecimal consumeAmount = executeConsumeAmount(cmd, gameLudoRun.getJoinAmount());
    // 8007 游戏人数已满
    ResponseAssert.isTrue(GameErrorCode.GAME_NUMBER_FULL, gameLudoRun.playersSize() + 1 <= 4);
    int index = gameCacheService.inrJoinNumberPeopleLudo(gameLudoRun.getId());
    ResponseAssert.isTrue(GameErrorCode.GAME_NUMBER_FULL, index <= 4);
    gameLudoRun.getPlayers().add(new GameLudoPlayer()
        .setConsumeJoinAmount(consumeAmount)
        .setCheckerboardColor(CheckerboardColor.getByIndex(index))
        .setInitiator(Boolean.FALSE)
        .setPlayerProfile(gameLudoAppConvertor.userProfileToPlayerProfile(userProfile))
        .setCreateTime(TimestampUtils.now())
    );
    gameLudoRunService.resetPlayers(gameLudoRun.getId(), gameLudoRun.getPlayers());
    gameCacheService.setJoinGameLudo(gameLudoRun.getId(), cmd.getPlayerUserId());
  }

  private BigDecimal executeConsumeAmount(GameLudoJoinCmd cmd, BigDecimal amount) {
    if (Objects.isNull(amount) || ArithmeticUtils.lteZero(amount)) {
      return BigDecimal.ZERO;
    }
    return ResponseAssert.requiredSuccess(walletGoldClient.changeBalance(GoldReceiptCmd.builder()
        .appExpenditure()
        .eventId(cmd.getId())
        .userId(cmd.getPlayerUserId())
        .sysOrigin(cmd.getReqSysOrigin().getOrigin())
        .origin(GoldOrigin.LUDO_GAME)
        .amount(amount)
        .build())
    ).getBalance().getDollarAmount();
  }
}
