package com.red.circle.other.app.command.activity.query;

import com.google.common.collect.Lists;
import com.red.circle.other.infra.common.user.UserCpUtils;
import com.red.circle.other.app.convertor.user.UserProfileAppConvertor;
import com.red.circle.other.app.dto.clientobject.activity.ActivityCpUserRankingCO;
import com.red.circle.other.app.dto.clientobject.activity.ActivityWeekCpGiftCO;
import com.red.circle.other.app.dto.cmd.activity.CpRankingQueryCmd;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.infra.database.mongo.entity.user.count.WeekCpValueCount;
import com.red.circle.other.infra.database.mongo.service.user.count.WeekCpValueCountService;
import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.other.inner.model.dto.user.WeekCpUserIdDTO;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.num.NumUtils;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

/**
 * CP排行榜查询.
 */
@Service
@RequiredArgsConstructor
public class WeekCpUserGiftQueryExe {

  private static final int TOP_SIZE = 50;
  private static final int OUT_OF_RANK = 99;

  private final UserProfileGateway userProfileGateway;
  private final CpRelationshipService cpRelationshipService;
  private final WeekCpValueCountService weekCpValueCountService;
  private final UserProfileAppConvertor userProfileAppConvertor;

  public ActivityWeekCpGiftCO execute(CpRankingQueryCmd cmd) {
    if (cmd.getType() == CpRankingQueryCmd.RankingType.WEEK) {
      return getWeekRanking(cmd);
    } else {
      return getSeasonRanking(cmd);
    }
  }

  private ActivityWeekCpGiftCO getWeekRanking(CpRankingQueryCmd cmd) {
    List<ActivityCpUserRankingCO> topList = getThisWeekUser(cmd);
    ActivityCpUserRankingCO currentUser = getCurrentUserRankingCO(cmd, topList);
    
    return new ActivityWeekCpGiftCO()
        .setThisWeekUserTop(topList)
        .setThisUser(currentUser);
  }

  private ActivityWeekCpGiftCO getSeasonRanking(CpRankingQueryCmd cmd) {
    List<ActivityCpUserRankingCO> topList = getThisSeasonUser(cmd);
    ActivityCpUserRankingCO currentUser = getCurrentUserRankingCO(cmd, topList);
    
    return new ActivityWeekCpGiftCO()
        .setThisWeekUserTop(topList)
        .setThisUser(currentUser);
  }

  private ActivityCpUserRankingCO queryCurrentUserCO(Long reqUserId, Long cpUserId) {
    Set<Long> collect = Stream.of(reqUserId, cpUserId).filter(Objects::nonNull).collect(Collectors.toSet());
    Map<Long, UserProfileDTO> userProfileMap = userProfileAppConvertor.toMapUserProfileDTO(
            userProfileGateway.mapByUserIds(collect));
    ActivityCpUserRankingCO rankingCO = new ActivityCpUserRankingCO();
    UserProfileDTO userProfileDTO = userProfileMap.get(reqUserId);
    if (userProfileDTO != null) {
      rankingCO.setUserId(userProfileDTO.getId());
      rankingCO.setUserAvatar(userProfileDTO.getUserAvatar());
      rankingCO.setUserNickname(userProfileDTO.getUserNickname());
      rankingCO.setUserSex(userProfileDTO.getUserSex());
    }

    UserProfileDTO cpUserProfileDTO = userProfileMap.get(cpUserId);
    if (cpUserProfileDTO != null) {
      rankingCO.setCpUserId(cpUserProfileDTO.getId());
      rankingCO.setCpUserAvatar(cpUserProfileDTO.getUserAvatar());
      rankingCO.setCpUserNickname(cpUserProfileDTO.getUserNickname());
      rankingCO.setCpUserSex(cpUserProfileDTO.getUserSex());
    }
    rankingCO.setRank(999);
    rankingCO.setTotalNumber(0L);
    rankingCO.setTotal("0");
    return rankingCO;
  }

  private ActivityCpUserRankingCO getCurrentUserRankingCO(CpRankingQueryCmd cmd,
                                                          List<ActivityCpUserRankingCO> topList) {
    List<Long> cpUserIdList = cpRelationshipService.getCpUserId(cmd.getReqUserId());
    if (cpUserIdList.isEmpty()) {
      return queryCurrentUserCO(cmd.getReqUserId(), null);
    }

    ActivityCpUserRankingCO userInTop = new ActivityCpUserRankingCO();
    userInTop.setTotalNumber(0L);
    for (Long cpUserId : cpUserIdList) {
      ActivityCpUserRankingCO rankingCO = findUserInTopList(cmd.getReqUserId(), cpUserId, topList);
        if (rankingCO != null && rankingCO.getTotalNumber() > userInTop.getTotalNumber()) {
          userInTop = rankingCO;
        }
    }

    if (Objects.isNull(userInTop.getUserId())) {
      return queryCurrentUserCO(cmd.getReqUserId(), cpUserIdList.get(0));
    }

    return userInTop;
  }

  private ActivityCpUserRankingCO findUserInTopList(Long userId, Long cpUserId, 
      List<ActivityCpUserRankingCO> topList) {
    if (CollectionUtils.isEmpty(topList)) {
      return null;
    }
    
    return topList.stream()
        .filter(ranking -> 
            (ranking.getUserId().equals(userId) && ranking.getCpUserId().equals(cpUserId)) ||
            (ranking.getUserId().equals(cpUserId) && ranking.getCpUserId().equals(userId))
        )
        .findFirst()
        .orElse(null);
  }

  private List<ActivityCpUserRankingCO> getThisWeekUser(CpRankingQueryCmd cmd) {
    List<WeekCpValueCount> topList = weekCpValueCountService.listThisWeekTop(
        cmd.getReqSysOrigin().getOrigin(), TOP_SIZE);
    return assemble(topList, 1);
  }

  private List<ActivityCpUserRankingCO> getThisSeasonUser(CpRankingQueryCmd cmd) {
    List<WeekCpValueCount> topList = weekCpValueCountService.listThisSeasonTop(
        cmd.getReqSysOrigin().getOrigin(), TOP_SIZE);
    return assemble(topList, 1);
  }

  private List<ActivityCpUserRankingCO> assemble(List<WeekCpValueCount> weekCpValueCounts, 
      int startRank) {
    if (CollectionUtils.isEmpty(weekCpValueCounts)) {
      return Lists.newArrayList();
    }

    AtomicInteger rankCounter = new AtomicInteger(startRank);
    
    List<ActivityCpUserRankingCO> results = weekCpValueCounts.stream().map(weekCpValueCount -> {
          WeekCpUserIdDTO userIdPair = UserCpUtils.parseUserIdPair(weekCpValueCount.getId());
          return new ActivityCpUserRankingCO()
              .setRank(rankCounter.getAndIncrement())
              .setUserId(userIdPair.getUserIdOne())
              .setCpUserId(userIdPair.getUserIdTwo())
              .setTotalNumber(weekCpValueCount.getQuantity())
              .setTotal(NumUtils.formatLong(weekCpValueCount.getQuantity()));
        }
    ).toList();

    Map<Long, UserProfileDTO> userProfileMap = userProfileAppConvertor.toMapUserProfileDTO(
        userProfileGateway.mapByUserIds(getAllUserIds(results))
    );

    return results.stream().map(ranking -> {
          UserProfileDTO userProfile = userProfileMap.get(ranking.getUserId());
          UserProfileDTO cpUserProfile = userProfileMap.get(ranking.getCpUserId());
          if (Objects.isNull(userProfile) || Objects.isNull(cpUserProfile)) {
            return null;
          }
          ranking.setUserAvatar(userProfile.getUserAvatar());
          ranking.setUserNickname(userProfile.getUserNickname());
          ranking.setUserSex(userProfile.getUserSex());

          ranking.setCpUserAvatar(cpUserProfile.getUserAvatar());
          ranking.setCpUserNickname(cpUserProfile.getUserNickname());
          ranking.setCpUserSex(cpUserProfile.getUserSex());
          return ranking;
        })
        .filter(Objects::nonNull)
        .toList();
  }

  private Set<Long> getAllUserIds(List<ActivityCpUserRankingCO> results) {
    return results.stream()
        .map(ranking -> Arrays.asList(ranking.getUserId(), ranking.getCpUserId()))
        .flatMap(Collection::stream)
        .collect(Collectors.toSet());
  }

}
