package com.red.circle.other.app.common.gift;

import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
import com.red.circle.component.redis.service.RedisService;
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.cmd.message.CustomGroupMsgBodyCmd;
import com.red.circle.external.inner.model.enums.message.GroupMessageTypeEnum;
import com.red.circle.mq.business.model.event.gift.GameLuckyGiftBusinessEvent;
import com.red.circle.mq.business.model.event.gift.GameLuckyGiftUser;
import com.red.circle.other.app.convertor.user.UserProfileAppConvertor;
import com.red.circle.other.app.dto.clientobject.game.GameLuckyGiftMsgCO;
import com.red.circle.other.app.dto.cmd.task.RoomDailyTaskProgressUpdateCmd;
import com.red.circle.other.app.service.task.RoomDailyTaskProgressService;
import com.red.circle.other.app.util.DateTimeAsiaRiyadhUtils;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.infra.database.cache.service.other.GiftCacheService;
import com.red.circle.other.infra.database.mongo.entity.live.RoomProfileManager;
import com.red.circle.other.infra.database.mongo.service.live.RoomProfileManagerService;
import com.red.circle.other.infra.database.rds.entity.game.GameLuckyGiftCount;
import com.red.circle.other.infra.database.rds.entity.live.RoomMember;
import com.red.circle.other.infra.database.rds.service.game.GameLuckyGiftCountService;
import com.red.circle.other.infra.database.rds.service.live.RoomMemberService;
import com.red.circle.other.inner.enums.material.PropsCommodityType;
import com.red.circle.other.inner.enums.task.RoomDailyTaskCode;
import com.red.circle.other.inner.model.dto.material.GiftConfigDTO;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.other.inner.model.dto.user.props.UserPropsResourcesDTO;
import com.red.circle.other.inner.model.dto.user.props.UserUsePropsDTO;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils;
import com.red.circle.tool.core.text.StringUtils;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

@Slf4j
@Component
@RequiredArgsConstructor
public class LuckyGiftResultProcessor {

  private final GameLuckyGiftCountService gameLuckyGiftCountService;
  private final GiftCacheService giftCacheService;
  private final UserProfileGateway userProfileGateway;
  private final UserProfileAppConvertor userProfileAppConvertor;
  private final ImGroupClient imGroupClient;
  private final RoomDailyTaskProgressService roomDailyTaskProgressService;
  private final RoomMemberService roomMemberService;
  private final RedisService redisService;
  private final RoomProfileManagerService roomProfileManagerService;
  private final GameLuckyGiftCommon gameLuckyGiftCommon;

  public void process(GameLuckyGiftBusinessEvent event, LuckyGiftGoClient.LuckyGiftGoDrawResponse response) {
    GiftConfigDTO giftConfig = giftCacheService.getById(event.getGiftId());
    if (giftConfig == null) {
      throw new IllegalStateException("Lucky gift config not found, giftId=" + event.getGiftId());
    }

    UserProfileDTO sendUserProfile = userProfileAppConvertor.toUserProfileDTO(
        userProfileGateway.getByUserId(event.getUserId()));
    if (sendUserProfile == null) {
      throw new IllegalStateException("Lucky gift sender profile not found, userId=" + event.getUserId());
    }

    Map<String, GameLuckyGiftUser> userMap = Optional.ofNullable(event.getUsers())
        .orElse(Collections.emptyList())
        .stream()
        .collect(Collectors.toMap(user -> Objects.toString(user.getId()), Function.identity()));

    if (userMap.size() != Optional.ofNullable(response.getResults()).orElse(Collections.emptyList()).size()) {
      throw new IllegalStateException("Lucky gift result size mismatch, businessId=" + event.getBusinessId());
    }

    UserPropsResourcesDTO avatarFrame = getUserAvatarFrameProps(sendUserProfile);
    long payAmount = resolvePayAmount(event, giftConfig);
    BigDecimal balanceAfter = BigDecimal.valueOf(Objects.requireNonNullElse(response.getBalanceAfter(), 0L));

    for (LuckyGiftGoClient.LuckyGiftGoDrawResult result : response.getResults()) {
      GameLuckyGiftUser user = userMap.get(result.getId());
      if (user == null) {
        throw new IllegalStateException("Lucky gift user mapping missing, resultId=" + result.getId());
      }

      long rewardNum = Objects.requireNonNullElse(result.getRewardNum(), 0L);
      int multiple = payAmount > 0 && rewardNum > 0 ? (int) (rewardNum / payAmount) : 0;

      gameLuckyGiftCountService.saveOrUpdate(new GameLuckyGiftCount()
          .setId(Long.valueOf(result.getId()))
          .setGiftId(event.getGiftId())
          .setRoomId(event.getRoomId())
          .setQuantity(event.getQuantity())
          .setUserId(event.getUserId())
          .setSysOrigin(event.getSysOrigin())
          .setGiftIncCount(0)
          .setGiftCombos(Objects.requireNonNullElse(event.getGiftCombos(), 0))
          .setGiftCover(giftConfig.getGiftPhoto())
          .setPayAmount(payAmount)
          .setMultiple(multiple)
          .setChangeInventory(false)
          .setSysGiftIncCount(0)
          .setAwardAmount(rewardNum)
          .setRegionCode(event.getRegionCode())
          .setDateNumber(ZonedDateTimeAsiaRiyadhUtils.nowDateToInt())
      );

      if (rewardNum <= 0) {
        continue;
      }

      sendLuckyGiftMessage(event, giftConfig, sendUserProfile, avatarFrame, result, multiple, balanceAfter);
      updateRoomDailyTask(event.getUserId(), event.getRoomId(), rewardNum, RoomDailyTaskCode.PERSONAL_LUCKY_GIFT_GOLD);
    }
  }

  private long resolvePayAmount(GameLuckyGiftBusinessEvent event, GiftConfigDTO giftConfig) {
    BigDecimal giftPrice = Objects.nonNull(event.getGiftPrice()) ? event.getGiftPrice() : giftConfig.getGiftCandy();
    return giftPrice.multiply(BigDecimal.valueOf(Objects.requireNonNullElse(event.getQuantity(), 0))).longValue();
  }

  private void sendLuckyGiftMessage(
      GameLuckyGiftBusinessEvent event,
      GiftConfigDTO giftConfig,
      UserProfileDTO sendUserProfile,
      UserPropsResourcesDTO avatarFrame,
      LuckyGiftGoClient.LuckyGiftGoDrawResult result,
      Integer multiple,
      BigDecimal balanceAfter
  ) {
    GameLuckyGiftMsgCO giftMsg = new GameLuckyGiftMsgCO()
        .setSysOrigin(event.getSysOrigin())
        .setRoomId(event.getRoomId())
        .setRoomAccount(event.getRoomAccount())
        .setSendUserId(event.getUserId())
        .setAccount(sendUserProfile.actualAccount())
        .setNickname(sendUserProfile.getUserNickname())
        .setUserAvatar(sendUserProfile.getUserAvatar())
        .setAvatarFrameCover(avatarFrame.getCover())
        .setAvatarFrameSvg(avatarFrame.getSourceUrl())
        .setMultiple(multiple)
        .setAwardAmount(result.getRewardNum())
        .setGiftId(event.getGiftId())
        .setMultipleType(gameLuckyGiftCommon.getRewardMultipleType(multiple))
        .setGiftCover(giftConfig.getGiftPhoto())
        .setRegionCode(event.getRegionCode())
        .setGiftQuantity(event.getQuantity())
        .setAcceptUserId(result.getAcceptUserId())
        .setAcceptNickname(resolveAcceptNickname(result.getAcceptUserId()))
        .setBalance(balanceAfter)
        .setGiftCandy(Objects.nonNull(event.getGiftPrice()) ? event.getGiftPrice() : giftConfig.getGiftCandy())
        .setGlobalNews(Boolean.TRUE);

    imGroupClient.sendMessageBroadcast(
        BroadcastGroupMsgBodyCmd.builder()
            .toPlatform(SysOriginPlatformEnum.toEnum(giftMsg.getSysOrigin()))
            .type(GroupMessageTypeEnum.GAME_LUCKY_GIFT)
            .data(giftMsg)
            .build()
    );

    if (StringUtils.isBlank(giftMsg.getRoomAccount())) {
      return;
    }
    imGroupClient.sendCustomMessage(giftMsg.getRoomAccount(),
        CustomGroupMsgBodyCmd.builder()
            .type(GroupMessageTypeEnum.GAME_LUCKY_GIFT)
            .data(giftMsg)
            .build());
  }

  private String resolveAcceptNickname(Long acceptUserId) {
    if (acceptUserId == null) {
      return null;
    }
    return Optional.ofNullable(userProfileGateway.getByUserId(acceptUserId))
        .map(profile -> profile.getUserNickname())
        .orElse(null);
  }

  private void updateRoomDailyTask(Long userId, Long roomId, Long rewardAmount, RoomDailyTaskCode taskType) {
    RoomMember roomMember = roomMemberService.getRoomMember(roomId, userId);
    if (roomMember == null) {
      return;
    }

    RoomProfileManager profileManager = roomProfileManagerService.getByUserId(userId);
    if (profileManager == null || Objects.equals(profileManager.getId(), roomMember.getRoomId())) {
      return;
    }

    String redisKey = "room:daily:task:progress:" + taskType.name() + ":" + userId + ":"
        + ZonedDateTimeAsiaRiyadhUtils.now().toLocalDate();
    Long total = redisService.increment(redisKey, rewardAmount);
    redisService.expire(redisKey, DateTimeAsiaRiyadhUtils.getSecondsUntilMidnight(), TimeUnit.SECONDS);

    roomDailyTaskProgressService.updateTaskProgress(
        new RoomDailyTaskProgressUpdateCmd()
            .setUserId(userId)
            .setTaskCode(taskType.name())
            .setProgressValue(total.intValue())
    );
  }

  private UserPropsResourcesDTO getUserAvatarFrameProps(UserProfileDTO sendUserProfile) {
    if (CollectionUtils.isEmpty(sendUserProfile.getUseProps())) {
      return new UserPropsResourcesDTO();
    }

    return Optional.ofNullable(sendUserProfile.getUseProps().stream()
        .filter(props -> Objects.nonNull(props.getPropsResources())
            && !Objects.equals(props.getPropsResources().getType(), PropsCommodityType.AVATAR_FRAME.name())
            && props.getExpireTime() > TimestampUtils.now().getTime())
        .findFirst().orElse(new UserUsePropsDTO())
        .getPropsResources()).orElse(new UserPropsResourcesDTO());
  }
}
