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

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.red.circle.framework.dto.PageResult;
import com.red.circle.other.app.dto.clientobject.family.FamilyHomeListCO;
import com.red.circle.other.app.dto.clientobject.family.FamilyHomeMemberCO;
import com.red.circle.other.app.dto.clientobject.family.FamilyHomeRoomCO;
import com.red.circle.other.app.dto.cmd.family.FamilyHomeListCmd;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.domain.model.user.UserConsumptionLevel;
import com.red.circle.other.domain.model.user.UserProfile;
import com.red.circle.other.infra.common.family.FamilyCommon;
import com.red.circle.other.infra.database.mongo.entity.live.ActiveVoiceRoom;
import com.red.circle.other.infra.database.mongo.entity.live.RoomProfileManager;
import com.red.circle.other.infra.database.mongo.entity.user.status.OnlineUser;
import com.red.circle.other.infra.database.mongo.service.live.ActiveVoiceRoomService;
import com.red.circle.other.infra.database.mongo.service.live.RoomProfileManagerService;
import com.red.circle.other.infra.database.mongo.service.user.status.OnlineUserService;
import com.red.circle.other.infra.database.rds.entity.family.FamilyBaseInfo;
import com.red.circle.other.infra.database.rds.entity.family.FamilyMemberInfo;
import com.red.circle.other.infra.database.rds.service.family.FamilyBaseInfoService;
import com.red.circle.other.infra.database.rds.service.family.FamilyLevelExpService;
import com.red.circle.other.infra.database.rds.service.family.FamilyMemberInfoService;
import com.red.circle.other.infra.enums.family.FamilyRoleEnum;
import com.red.circle.other.infra.enums.family.FamilyStatusEnum;
import com.red.circle.other.inner.enums.room.RoomEventEnum;
import com.red.circle.other.inner.model.dto.famliy.FamilyDetailsDTO;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.num.NumUtils;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import com.red.circle.tool.core.text.StringUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * 家族首页列表
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class FamilyHomeListExe {

  private final FamilyCommon familyCommon;
  private final UserProfileGateway userProfileGateway;
  private final OnlineUserService onlineUserService;
  private final ActiveVoiceRoomService activeVoiceRoomService;
  private final FamilyBaseInfoService familyBaseInfoService;
  private final FamilyLevelExpService familyLevelExpService;
  private final FamilyMemberInfoService familyMemberInfoService;
  private final RoomProfileManagerService roomProfileManagerService;

  public PageResult<FamilyHomeListCO> execute(FamilyHomeListCmd cmd) {
    List<FamilyBaseInfo> familyList = getFamilyList(cmd);

    if (CollectionUtils.isEmpty(familyList)) {
      return PageResult.newPageResult(0);
    }

    Set<Long> familyIds = familyList.stream()
        .map(FamilyBaseInfo::getId)
        .collect(Collectors.toSet());

    Map<Long, BigDecimal> expMap = familyLevelExpService.mapTotalExpCountByFamilyIds(familyIds);

    List<FamilyHomeListCO> result = familyList.stream()
        .map(family -> buildFamilyHomeCO(cmd, family, expMap.getOrDefault(family.getId(), BigDecimal.ZERO)))
        .sorted(Comparator.comparing(FamilyHomeListExe::replaceExp, Comparator.reverseOrder()))
        .collect(Collectors.toList());

    PageResult<FamilyHomeListCO> pageResult = new PageResult<>();
    pageResult.setCurrent(cmd.getCurrent());
    pageResult.setSize(cmd.getSize());
    pageResult.setRecords(result);
    return pageResult;
  }

  private static long replaceExp(FamilyHomeListCO co) {
    String replace = co.getExp()
            .replace(",", "")
            .replace("K", "")
            .replace("M", "")
            ;
    return Long.parseLong(replace);
  }

  private List<FamilyBaseInfo> getFamilyList(FamilyHomeListCmd cmd) {
    FamilyMemberInfo familyMemberInfo = familyMemberInfoService.getFamilyMemberByUserId(cmd.getReqUserId());
    if (familyMemberInfo == null) {
      return Collections.emptyList();
    }

    return List.of(familyBaseInfoService.getBaseInfoById(familyMemberInfo.getFamilyId()));
  }

  private FamilyHomeListCO buildFamilyHomeCO(FamilyHomeListCmd cmd, FamilyBaseInfo family, BigDecimal totalExp) {
    FamilyDetailsDTO levelDetails = familyCommon.getFamilyDetails(cmd.requireReqSysOrigin(), family.getId());

    List<FamilyMemberInfo> members = familyMemberInfoService.listByFamilyId(family.getId());
    Set<Long> memberUserIds = members.stream()
        .map(FamilyMemberInfo::getMemberUserId)
        .collect(Collectors.toSet());

    List<OnlineUser> onlineUsers = CollectionUtils.isEmpty(memberUserIds) 
        ? List.of() 
        : onlineUserService.userOnlineByUserIds(memberUserIds);
    
    Set<Long> onlineUserIds = onlineUsers.stream()
        .map(OnlineUser::getUserId)
        .collect(Collectors.toSet());

    List<FamilyHomeMemberCO> displayMembers = buildDisplayMembers(cmd, members, onlineUserIds);

    List<ActiveVoiceRoom> onlineRooms = activeVoiceRoomService.listByFamilyId(family.getId(), 30);
    List<FamilyHomeRoomCO> roomCOs = buildRoomCOs(onlineRooms);

    FamilyMemberInfo myFamilyInfo = familyMemberInfoService.getFamilyMemberByUserId(cmd.getReqUserId());

    return new FamilyHomeListCO()
        .setFamilyId(family.getId())
        .setFamilyName(family.getFamilyName())
        .setFamilyAvatar(family.getFamilyAvatar())
        .setFamilyAccount(family.getFamilyAccount())
        .setFamilyNotice(family.getFamilyNotice())
        .setLevel(levelDetails.getLevel())
        .setMemberCount(members.size())
        .setOnlineCount(onlineUsers.size())
        .setExp(NumUtils.formatLong(totalExp.longValue()))
        .setAvatarFrameCover(levelDetails.getAvatarFrameCover())
        .setAvatarFrameSvg(levelDetails.getAvatarFrameSvg())
        .setDisplayMembers(displayMembers)
        .setMyFamilyRole(Optional.ofNullable(myFamilyInfo).map(FamilyMemberInfo::getMemberRole).orElse(null))
        .setOnlineRooms(roomCOs);
  }

  private List<FamilyHomeMemberCO> buildDisplayMembers(FamilyHomeListCmd cmd, 
      List<FamilyMemberInfo> members, Set<Long> onlineUserIds) {
    
    if (CollectionUtils.isEmpty(members)) {
      return List.of();
    }

    Set<Long> userIds = members.stream()
        .map(FamilyMemberInfo::getMemberUserId)
        .collect(Collectors.toSet());
    
    Map<Long, UserProfile> profileMap = userProfileGateway.mapByUserIds(userIds);
    Map<Long, UserConsumptionLevel> levelMap = userProfileGateway.getUserConsumptionLevel(
        cmd.requireReqSysOriginEnum(), userIds);

    return members.stream()
        .sorted(getMemberComparator(levelMap))
        .limit(50)
        .map(member -> {
          UserProfile profile = profileMap.get(member.getMemberUserId());
          UserConsumptionLevel level = levelMap.get(member.getMemberUserId());
          
          return new FamilyHomeMemberCO()
              .setUserId(member.getMemberUserId())
              .setAvatar(Objects.nonNull(profile) ? profile.getUserAvatar() : "")
              .setOnline(onlineUserIds.contains(member.getMemberUserId()))
              .setFamilyRole(member.getMemberRole())
              .setConsumptionLevel(Objects.nonNull(level) ? level.getWealthLevel() : 0);
        })
        .collect(Collectors.toList());
  }

  private Comparator<FamilyMemberInfo> getMemberComparator(Map<Long, UserConsumptionLevel> levelMap) {
    return (m1, m2) -> {
      int roleOrder1 = getRoleOrder(m1.getMemberRole());
      int roleOrder2 = getRoleOrder(m2.getMemberRole());
      
      if (roleOrder1 != roleOrder2) {
        return Integer.compare(roleOrder1, roleOrder2);
      }
      
      if (FamilyRoleEnum.MEMBER.name().equals(m1.getMemberRole())  || FamilyRoleEnum.MANAGE.name().equals(m1.getMemberRole()) ) {
        UserConsumptionLevel level1 = levelMap.get(m1.getMemberUserId());
        UserConsumptionLevel level2 = levelMap.get(m2.getMemberUserId());
        
        int l1 = Objects.nonNull(level1) ? level1.getWealthLevel() : 0;
        int l2 = Objects.nonNull(level2) ? level2.getWealthLevel() : 0;
        
        return Integer.compare(l2, l1);
      }
      
      return 0;
    };
  }

  private int getRoleOrder(String role) {
    if (FamilyRoleEnum.ADMIN.name().equals(role)) {
      return 1;
    } else if (FamilyRoleEnum.MANAGE.name().equals(role)) {
      return 2;
    } else {
      return 3;
    }
  }

  private List<FamilyHomeRoomCO> buildRoomCOs(List<ActiveVoiceRoom> rooms) {
    if (CollectionUtils.isEmpty(rooms)) {
      return List.of();
    }

    Set<Long> userIds = rooms.stream()
        .map(ActiveVoiceRoom::getUserId)
        .collect(Collectors.toSet());
    
    Map<Long, UserProfile> profileMap = userProfileGateway.mapByUserIds(userIds);


    Map<Long, RoomProfileManager> roomProfileManagerMap = roomProfileManagerService.mapByRoomIds(
            rooms.stream().map(ActiveVoiceRoom::getId).collect(Collectors.toSet()));


    return rooms.stream()
        .map(room -> {
          UserProfile profile = profileMap.get(room.getUserId());
          RoomProfileManager roomProfileManager = roomProfileManagerMap.get(room.getId());
          if (!isAvailableRoom(roomProfileManager)) {
            return null;
          }

          return new FamilyHomeRoomCO()
              .setRoomId(room.getId())
              .setRoomName(roomProfileManager.getRoomAccount())
              .setRoomCover(roomProfileManager.getRoomCover())
              .setCountryCode(roomProfileManager.getCountryCode())
              .setCountryName(roomProfileManager.getCountryName())
              .setRoomDesc(roomProfileManager.getRoomDesc())
               .setExistsPassword(StringUtils.isNotBlank(roomProfileManager.getSetting().getPassword()))
              .setOnlineQuantity(room.getOnlineQuantity());
        })
        .filter(Objects::nonNull)
        .collect(Collectors.toList());
  }

  private boolean isAvailableRoom(RoomProfileManager roomProfileManager) {
    return Objects.nonNull(roomProfileManager)
        && !Objects.equals(roomProfileManager.getDel(), Boolean.TRUE)
        && !Objects.equals(roomProfileManager.getEvent(), RoomEventEnum.CLOSE.name())
        && Objects.nonNull(roomProfileManager.getSetting());
  }

}
