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

import com.alibaba.fastjson.JSON;
import com.red.circle.common.business.core.enums.GameStateEnum;
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.enums.message.GroupMessageTypeEnum;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.response.CommonErrorCode;
import com.red.circle.mq.business.model.event.game.GameRoomPkEvent;
import com.red.circle.mq.rocket.business.producer.GameMqMessage;
import com.red.circle.other.app.convertor.user.UserProfileAppConvertor;
import com.red.circle.other.app.dto.clientobject.game.GameRoomPkMsgCO;
import com.red.circle.other.app.dto.clientobject.game.GameRoomPkUserCO;
import com.red.circle.other.app.dto.cmd.game.roompk.GameRoomPkAcceptCmd;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.domain.gateway.user.ability.UserRegionGateway;
import com.red.circle.other.infra.database.cache.service.other.GameRoomPkCacheService;
import com.red.circle.other.infra.database.cache.service.user.UserRegionCacheService;
import com.red.circle.other.infra.database.mongo.entity.live.RoomProfile;
import com.red.circle.other.infra.database.mongo.service.live.RoomProfileManagerService;
import com.red.circle.other.infra.database.rds.entity.game.GameRoomPkRecord;
import com.red.circle.other.infra.database.rds.entity.live.RoomMember;
import com.red.circle.other.infra.database.rds.entity.props.PropsBackpack;
import com.red.circle.other.infra.database.rds.entity.props.PropsSourceRecord;
import com.red.circle.other.infra.database.rds.service.game.GameIndoorTeamPkRecordService;
import com.red.circle.other.infra.database.rds.service.game.GameRoomPkRecordService;
import com.red.circle.other.infra.database.rds.service.live.RoomMemberService;
import com.red.circle.other.infra.database.rds.service.props.PropsBackpackService;
import com.red.circle.other.infra.database.rds.service.props.PropsSourceRecordService;
import com.red.circle.other.infra.enums.game.GameRoomPkTypeEnum;
import com.red.circle.other.inner.asserts.GameErrorCode;
import com.red.circle.other.inner.asserts.RoomErrorCode;
import com.red.circle.other.inner.enums.game.GamePkTypeEnum;
import com.red.circle.other.inner.enums.material.PropsCommodityType;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.json.JacksonUtils;
import java.sql.Timestamp;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

/**
 * 房间PK - 接受pk游戏.
 *
 * @author pengshigang on 2021/9/25
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class GameRoomPkAcceptCmdExe {

  private final GameMqMessage gameMqMessage;
  private final ImGroupClient imGroupClient;
  private final RoomMemberService roomMemberService;
  private final UserProfileGateway userProfileGateway;
  private final UserRegionGateway userRegionGateway;
  private final PropsBackpackService propsBackpackService;
  private final GameRoomPkCacheService gameRoomPkCacheService;
  private final UserProfileAppConvertor userProfileAppConvertor;
  private final GameRoomPkRecordService gameRoomPkRecordService;
  private final PropsSourceRecordService propsSourceRecordService;
  private final RoomProfileManagerService roomProfileManagerService;
  private final GameIndoorTeamPkRecordService gameIndoorTeamPkRecordService;
  private final UserRegionCacheService userRegionCacheService;

  public void execute(GameRoomPkAcceptCmd cmd) {

    ResponseAssert.isTrue(GameErrorCode.YOUR_GAME_NOT_OVER, isGameOver(cmd));
    GameRoomPkRecord pkRecord = getWaitById(cmd);
    ResponseAssert.notNull(GameErrorCode.GAME_NUMBER_FULL, pkRecord);

    if (Objects.equals(pkRecord.getPkType(), GamePkTypeEnum.ROOM_VS_ROOM.name())) {

      ResponseAssert.isFalse(GameErrorCode.SAME_ROOM_UNACCEPTABLE_ERROR,
          Objects.equals(cmd.getRoomId(), pkRecord.getSponsorRoomId()));
      RoomMember member = roomMemberService
          .getRoomMember(cmd.getRoomId(), cmd.requiredReqUserId());
      ResponseAssert.notNull(CommonErrorCode.INSUFFICIENT_PERMISSION, member);
      ResponseAssert.isTrue(CommonErrorCode.INSUFFICIENT_PERMISSION,
          member.checkHomeownerOrAdmin());
    }

    pkRecord.setGameStatus(GameStateEnum.STARTED.name());
    pkRecord.setRecipientRoomId(cmd.getRoomId());
    pkRecord.setRecipientUserId(cmd.requiredReqUserId());
    pkRecord.setEndTime(getEndTime(pkRecord));
    pkRecord.setUpdateTime(TimestampUtils.now());
    pkRecord.setUpdateUser(cmd.requiredReqUserId());

    gameRoomPkRecordService.updateSelectiveById(pkRecord);

    GameRoomPkUserCO cacheRoomPkUser = cacheRoomPk(pkRecord);

    sendRedisMq(pkRecord);

    sendMsg(pkRecord, cacheRoomPkUser);

  }

  private void sendRedisMq(GameRoomPkRecord gameRoomPkRecord) {
    gameMqMessage.roomPkDelayed(new GameRoomPkEvent()
        .setGameId(gameRoomPkRecord.getId())
        .setState(GameStateEnum.STARTED)
        .setDelayMinutes(gameRoomPkRecord.getPkDuration())
    );
  }

  private GameRoomPkUserCO cacheRoomPk(GameRoomPkRecord game) {

    GameRoomPkUserCO pk = gameRoomPkCacheService.getRoomPk(game.getId(), GameRoomPkUserCO.class);
    ResponseAssert.notNull(GameErrorCode.GAME_OVER, pk);

    UserProfileDTO userProfile = userProfileAppConvertor.toUserProfileDTO(
        userProfileGateway.getByUserId(game.getRecipientUserId()));
    String account = Objects.isNull(userProfile.getOwnSpecialId()) ? userProfile.getAccount() :
        userProfile.getOwnSpecialId().getAccount();

    if (Objects.equals(game.getPkType(), GamePkTypeEnum.ROOM_VS_ROOM.name())) {

      RoomProfile room = roomProfileManagerService
          .getProfileById(game.getRecipientRoomId());
      ResponseAssert.notNull(RoomErrorCode.ROOM_NOT_EXISTS, room);
      pk.setRecipientCover(room.getRoomCover());
      pk.setRecipientName(room.getRoomName());
      pk.setRecipientUserAccount(account);
    } else {

      ResponseAssert.notNull(CommonErrorCode.DATA_ERROR, userProfile);
      pk.setRecipientCover(userProfile.getUserAvatar());
      pk.setRecipientName(userProfile.getUserNickname());
      pk.setRecipientUserAccount(account);
      PropsSourceRecord props = getPropsSourceRecord(getUseProps(userProfile.getId()));
      if (Objects.nonNull(props)) {
        pk.setRecipientPropsCover(props.getCover());
        pk.setRecipientPropsSourceUrl(props.getSourceUrl());
      }
    }

    pk.setOverTime(game.getEndTime().getTime());
    pk.setRecipientUserId(game.getRecipientUserId());
    pk.setRecipientRoomId(game.getRecipientRoomId());
    pk.setRecipientIntegral(0L);

    Map<Long, String> userMap = userRegionGateway.mapRegionCode(Set.of(pk.getSponsorUserId()));
    if (userMap != null) {
      pk.setSponsorUserRegion(userMap.get(pk.getSponsorUserId()));
    }
    gameRoomPkCacheService.cacheRoomPk(game.getId(), JSON.toJSONString(pk));
    return pk;
  }

  private Timestamp getEndTime(GameRoomPkRecord gameRoomPkRecord) {
    return TimestampUtils.nowPlusMinutes(gameRoomPkRecord.getPkDuration());
  }

  private Boolean isGameOver(GameRoomPkAcceptCmd cmd) {
    return gameIndoorTeamPkRecordService.checkCreateGame(cmd.requiredReqUserId())
        && gameRoomPkRecordService.isCanJoinOrCreateGame(cmd.requiredReqUserId());
  }

  private PropsBackpack getUseProps(Long userId) {
    return Optional.ofNullable(propsBackpackService
            .getUserNotExpiredUseProps(userId, PropsCommodityType.AVATAR_FRAME))
        .orElse(new PropsBackpack());
  }

  private void sendMsg(GameRoomPkRecord game, GameRoomPkUserCO cacheRoomPkUser) {

    GameRoomPkMsgCO pkMsgCO = new GameRoomPkMsgCO()
        .setId(game.getId())
        .setSponsorUserId(game.getSponsorUserId())
        .setSponsorRoomId(game.getSponsorRoomId())
        .setSponsorUserAccount(cacheRoomPkUser.getSponsorUserAccount())
        .setSponsorCover(cacheRoomPkUser.getSponsorCover())
        .setSponsorPropsCover(cacheRoomPkUser.getSponsorPropsCover())
        .setSponsorPropsSourceUrl(cacheRoomPkUser.getSponsorPropsSourceUrl())
        .setSponsorName(cacheRoomPkUser.getSponsorName())
        .setRecipientUserId(game.getRecipientUserId())
        .setRecipientRoomId(game.getRecipientRoomId())
        .setRecipientUserAccount(cacheRoomPkUser.getRecipientUserAccount())
        .setRecipientCover(cacheRoomPkUser.getRecipientCover())
        .setRecipientPropsCover(cacheRoomPkUser.getRecipientPropsCover())
        .setRecipientPropsSourceUrl(cacheRoomPkUser.getRecipientPropsSourceUrl())
        .setRecipientName(cacheRoomPkUser.getRecipientName())
        //本局游戏结束时间为指定时间
        .setOverTime(game.getEndTime().getTime())
        .setMsgType(GameRoomPkTypeEnum.GAME_ROOM_PK_ACCEPT.name())
        .setPkType(game.getPkType());

    imGroupClient.sendMessageBroadcast(
        BroadcastGroupMsgBodyCmd.builder()
            .allPlatform()
            .type(GroupMessageTypeEnum.GAME_ROOM_PK)
            .data(pkMsgCO)
            .build()
    );
  }

  private PropsSourceRecord getPropsSourceRecord(PropsBackpack propsBackpack) {
    if (Objects.isNull(propsBackpack.getPropsId())) {
      return null;
    }
    return propsSourceRecordService.getPropsById(propsBackpack.getPropsId());
  }


  private GameRoomPkRecord getWaitById(GameRoomPkAcceptCmd cmd) {
    return gameRoomPkRecordService.getByIdByStatus(cmd.getPkId(), GameStateEnum.WAITING);
  }

}
