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

import com.google.common.collect.Maps;
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.GameRoomVoteEvent;
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.GameRoomVoteCO;
import com.red.circle.other.app.dto.clientobject.game.GameRoomVoteOptionCO;
import com.red.circle.other.app.dto.cmd.game.vote.GameRoomCreateVoteCmd;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.infra.database.cache.service.other.GameRoomVoteCacheService;
import com.red.circle.other.infra.database.rds.entity.game.GameRoomVoteInfoRecord;
import com.red.circle.other.infra.database.rds.entity.game.GameRoomVoteRecord;
import com.red.circle.other.infra.database.rds.service.game.GameRoomVoteInfoRecordService;
import com.red.circle.other.infra.database.rds.service.game.GameRoomVoteRecordService;
import com.red.circle.other.infra.database.rds.service.live.RoomMemberService;
import com.red.circle.other.inner.asserts.GameErrorCode;
import com.red.circle.other.inner.enums.game.GameVoteTypeEnum;
import com.red.circle.other.inner.enums.live.RoomUserRolesEnum;
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 com.red.circle.tool.core.sequence.IdWorkerUtils;
import java.sql.Timestamp;
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.Service;

/**
 * <p>
 * 创建房间投票.
 * </p>
 *
 * @author pengshigang
 * @since 2022-01-04
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class GameRoomCreateVoteCmdExe {

  private final GameMqMessage gameMqMessage;
  private final ImGroupClient imGroupClient;
  private final RoomMemberService roomMemberService;
  private final UserProfileGateway userProfileGateway;
  private final UserProfileAppConvertor userProfileAppConvertor;
  private final GameRoomVoteCacheService gameRoomVoteCacheService;
  private final GameRoomVoteRecordService gameRoomVoteRecordService;
  private final GameRoomVoteInfoRecordService gameRoomVoteInfoRecordService;

  public void execute(GameRoomCreateVoteCmd cmd) {

    checkRole(cmd);
    ResponseAssert.isTrue(GameErrorCode.YOUR_GAME_NOT_OVER, isGameOver(cmd));
    Long voteId = IdWorkerUtils.getId();
    Timestamp date = TimestampUtils.now();
    Timestamp endTime = TimestampUtils.nowPlusSeconds(cmd.getPkDuration());
    //创建投票记录
    gameRoomVoteRecordService.save(new GameRoomVoteRecord()
        .setId(voteId)
        .setSysOrigin(cmd.getReqSysOrigin().getOrigin())
        .setTitle(cmd.getTitle())
        .setRoomId(cmd.getRoomId())
        .setSingleChoice(cmd.getSingleChoice())
        .setType(cmd.getType().name())
        .setGameOver(Boolean.FALSE)
        .setEndTime(endTime)
        .setPkDuration(cmd.getPkDuration())
        .setCreateTime(date)
        .setUpdateTime(date)
        .setCreateUser(cmd.requiredReqUserId())
        .setUpdateUser(cmd.requiredReqUserId())
    );

    //创建选项
    List<GameRoomVoteInfoRecord> records = cmd.getOptions().stream()
        .map(content -> new GameRoomVoteInfoRecord()
            .setId(IdWorkerUtils.getId())
            .setVoteId(voteId)
            .setOptionContent(content)
            .setVotes(0L)
            .setCreateTime(date)
            .setCreateUser(cmd.requiredReqUserId())
        )
        .collect(Collectors.toList());
    gameRoomVoteInfoRecordService.saveBatch(records);

    //发送通知并缓存投票信息
    sendMsg(cmd, voteId, endTime, records);
  }

  private void sendMsg(GameRoomCreateVoteCmd cmd, Long voteId, Timestamp overTime,
      List<GameRoomVoteInfoRecord> records) {

    Map<Long, UserProfileDTO> userMap = getBaseInfoMap(cmd);

    GameRoomVoteCO gameRoomVote = new GameRoomVoteCO()
        .setId(voteId)
        .setRoomId(cmd.getRoomId())
        .setOverTime(overTime.getTime())
        .setSingleChoice(cmd.getSingleChoice())
        .setTitle(cmd.getTitle())
        .setType(cmd.getType())
        .setOptions(records.stream().map(record -> {
          GameRoomVoteOptionCO option = new GameRoomVoteOptionCO()
              .setId(record.getId())
              .setVoteId(voteId)
              .setOption(record.getOptionContent())
              .setVotes(0L);
          if (Objects.equals(cmd.getType(), GameVoteTypeEnum.USER)) {
            UserProfileDTO baseInfo = userMap.get(Long.valueOf(record.getOptionContent()));
            option.setUserId(baseInfo.getId());
            option.setUserAccount(baseInfo.getAccount());
            option.setUserNickname(baseInfo.getUserNickname());
            option.setUserAvatar(baseInfo.getUserAvatar());
          }
          return option;
        }).toList());

    gameRoomVoteCacheService.cacheRoomVote(voteId, JacksonUtils.toJson(gameRoomVote));

    sendRedisMq(voteId, cmd.getPkDuration());

    imGroupClient.sendMessageBroadcast(
        BroadcastGroupMsgBodyCmd.builder()
            .allPlatform()
            .type(GroupMessageTypeEnum.GAME_ROOM_CREATE_VOTE)
            .data(gameRoomVote)
            .build()
    );
  }

  private Map<Long, UserProfileDTO> getBaseInfoMap(GameRoomCreateVoteCmd cmd) {

    Map<Long, UserProfileDTO> userMap = Maps.newHashMap();

    if (Objects.equals(cmd.getType(), GameVoteTypeEnum.USER)) {
      userMap = userProfileAppConvertor.toMapUserProfileDTO(
          userProfileGateway.mapByUserIds(cmd.getOptions().stream().map(Long::valueOf)
              .collect(Collectors.toSet())));
      ResponseAssert.isFalse(CommonErrorCode.REQUIRED_FIELDS_CANNOT_BE_IGNORED, userMap.isEmpty());
    }
    return userMap;
  }

  private void sendRedisMq(Long voteId, Long pkDuration) {
    gameMqMessage.roomVoteDelayed(new GameRoomVoteEvent()
        .setGameId(voteId)
        .setGameOver(Boolean.FALSE)
        .setDelaySecond(pkDuration)
    );
  }

  private Boolean isGameOver(GameRoomCreateVoteCmd cmd) {
    return gameRoomVoteRecordService.isOver(cmd.getRoomId());
  }

  private void checkRole(GameRoomCreateVoteCmd cmd) {
    RoomUserRolesEnum rolesEnum = roomMemberService
        .getRoomUserRoles(cmd.getReqUserId(), cmd.getRoomId());
    ResponseAssert.notNull(CommonErrorCode.INSUFFICIENT_PERMISSION, rolesEnum);
    ResponseAssert.isTrue(CommonErrorCode.INSUFFICIENT_PERMISSION,
        rolesEnum.equals(RoomUserRolesEnum.HOMEOWNER) ||
            rolesEnum.equals(RoomUserRolesEnum.ADMIN));
  }

}
