package com.red.circle.other.app.command.family;

import com.red.circle.other.app.dto.clientobject.family.FamilyLeaderboardCO;
import com.red.circle.other.app.dto.clientobject.family.FamilyMemberRankInfoCO;
import com.red.circle.other.app.dto.clientobject.family.FamilyMemberWeekRankCO;
import com.red.circle.other.app.dto.cmd.family.FamilyBaseInfoQueryCmd;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.domain.model.user.UserProfile;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamMemberService;
import com.red.circle.other.infra.database.rds.entity.family.FamilyMemberInfo;
import com.red.circle.other.infra.database.rds.entity.family.FamilyMemberTotalExp;
import com.red.circle.other.infra.database.rds.service.family.FamilyMemberInfoService;
import com.red.circle.other.infra.database.rds.service.family.FamilyMemberTotalExpService;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.num.NumUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * 家族成员Top100总排行榜
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class FamilyMemberTop100Exe {

  private final TeamMemberService teamMemberService;
  private final UserProfileGateway userProfileGateway;
  private final FamilyMemberInfoService familyMemberInfoService;
  private final FamilyMemberTotalExpService familyMemberTotalExpService;

  public FamilyLeaderboardCO execute(FamilyBaseInfoQueryCmd cmd) {
    FamilyMemberInfo member = familyMemberInfoService.getFamilyMemberByUserId(cmd.getReqUserId());
    if (Objects.isNull(member)) {
      return new FamilyLeaderboardCO();
    }

    List<FamilyMemberTotalExp> top200Exp = getTop200TotalExp(member.getFamilyId());
    if (CollectionUtils.isEmpty(top200Exp)) {
      FamilyLeaderboardCO leaderboardCO = new FamilyLeaderboardCO();

      Map<Long, UserProfile> userProfileMap = userProfileGateway.mapByUserIds(Set.of(cmd.getReqUserId()));
      Set<Long> hostUserIds = getHostUserIds(Set.of(cmd.getReqUserId()));
      FamilyMemberRankInfoCO myRankInfo = buildMyRankInfo(
              cmd.getReqUserId(),
              new FamilyMemberTotalExp().setExp(0L),
              userProfileMap.get(cmd.getReqUserId()),
              0,
              0,
              hostUserIds.contains(cmd.getReqUserId())
      );

      leaderboardCO.setMyRankInfo(myRankInfo);
      return leaderboardCO;
    }

    Set<Long> userIds = top200Exp.stream()
        .map(FamilyMemberTotalExp::getFamilyMemberId)
        .collect(Collectors.toSet());

    userIds.add(cmd.getReqUserId());
    Set<Long> hostUserIds = getHostUserIds(userIds);
    Map<Long, UserProfile> userProfileMap = userProfileGateway.mapByUserIds(userIds);

    List<FamilyMemberWeekRankCO> hostRank = new ArrayList<>();
    List<FamilyMemberWeekRankCO> supporterRank = new ArrayList<>();
    
    int myRankInHost = -1;
    int myRankInSupporter = -1;
    FamilyMemberTotalExp myExp = null;

    for (int i = 0; i < top200Exp.size(); i++) {
      FamilyMemberTotalExp exp = top200Exp.get(i);
      Long userId = exp.getFamilyMemberId();
      boolean isHost = hostUserIds.contains(userId);

      if (userId.equals(cmd.getReqUserId())) {
        myExp = exp;
      }

      FamilyMemberWeekRankCO rankCO = buildRankCO(exp, userProfileMap.get(userId), isHost);

      if (isHost && hostRank.size() < 100) {
        if (userId.equals(cmd.getReqUserId())) {
          myRankInHost = hostRank.size() + 1;
        }
        hostRank.add(rankCO);
      } else if (!isHost && supporterRank.size() < 100) {
        if (userId.equals(cmd.getReqUserId())) {
          myRankInSupporter = supporterRank.size() + 1;
        }
        supporterRank.add(rankCO);
      } else if (userId.equals(cmd.getReqUserId())) {
        if (isHost) {
          myRankInHost = 101;
        } else {
          myRankInSupporter = 101;
        }
      }

      if (hostRank.size() >= 100 && supporterRank.size() >= 100 && Objects.nonNull(myExp)) {
        break;
      }
    }

    FamilyMemberRankInfoCO myRankInfo = buildMyRankInfo(
        cmd.getReqUserId(), 
        myExp, 
        userProfileMap.get(cmd.getReqUserId()),
        myRankInHost,
        myRankInSupporter,
        hostUserIds.contains(cmd.getReqUserId())
    );

    return new FamilyLeaderboardCO()
        .setHostWeekRank(hostRank)
        .setSupporterWeekRank(supporterRank)
        .setMyRankInfo(myRankInfo);
  }

  private List<FamilyMemberTotalExp> getTop200TotalExp(Long familyId) {
    return familyMemberTotalExpService.query()
        .eq(FamilyMemberTotalExp::getFamilyId, familyId)
        .orderByDesc(FamilyMemberTotalExp::getExp)
        .last("LIMIT 200")
        .list();
  }

  private Set<Long> getHostUserIds(Set<Long> userIds) {
    if (CollectionUtils.isEmpty(userIds)) {
      return Set.of();
    }

    List<Long> hostIds = teamMemberService.listMemberIds(userIds);
    return CollectionUtils.isEmpty(hostIds) ? Set.of() : Set.copyOf(hostIds);
  }

  private FamilyMemberWeekRankCO buildRankCO(FamilyMemberTotalExp exp,
                                             UserProfile profile, boolean isHost) {
    return new FamilyMemberWeekRankCO()
        .setUserId(exp.getFamilyMemberId())
        .setAccount(Objects.nonNull(profile) ? profile.getAccount() : "")
        .setNickname(Objects.nonNull(profile) ? profile.getUserNickname() : "")
        .setAvatar(Objects.nonNull(profile) ? profile.getUserAvatar() : "")
        .setExp(NumUtils.formatLong(exp.getExp()))
        .setIsHost(isHost);
  }

  private FamilyMemberRankInfoCO buildMyRankInfo(
      Long userId,
      FamilyMemberTotalExp myExp,
      UserProfile profile,
      int myRankInHost,
      int myRankInSupporter,
      boolean isHost
  ) {
    String rank;
    if (isHost) {
      rank = myRankInHost > 0 ? (myRankInHost > 100 ? "100+" : String.valueOf(myRankInHost)) : "0";
    } else {
      rank = myRankInSupporter > 0 ? (myRankInSupporter > 100 ? "100+" : String.valueOf(myRankInSupporter)) : "0";
    }

    return new FamilyMemberRankInfoCO()
        .setUserId(userId)
        .setAccount(Objects.nonNull(profile) ? profile.getAccount() : "")
        .setNickname(Objects.nonNull(profile) ? profile.getUserNickname() : "")
        .setAvatar(Objects.nonNull(profile) ? profile.getUserAvatar() : "")
        .setExp(Objects.nonNull(myExp) ? NumUtils.formatLong(myExp.getExp()) : "0")
        .setRank(rank)
        .setIsHost(isHost);
  }
}
