package com.red.circle.other.infra.gateway.user;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
import com.red.circle.common.business.core.level.LevelUtils;
import com.red.circle.external.inner.endpoint.message.ImAccountClient;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.domain.gateway.user.UserRunProfileTransportGateway;
import com.red.circle.other.domain.model.user.OwnSpecialId;
import com.red.circle.other.domain.model.user.UserConsumptionLevel;
import com.red.circle.other.domain.model.user.UserProfile;
import com.red.circle.other.domain.model.user.NobleAbilityCO;
import com.red.circle.other.infra.common.props.PropsBackpackCommon;
import com.red.circle.other.infra.common.props.UserBadgeCommon;
import com.red.circle.other.infra.convertor.user.UserProfileInfraConvertor;
import com.red.circle.other.infra.database.cache.entity.user.ConsumptionLevelCache;
import com.red.circle.other.infra.database.cache.service.user.UserCacheService;
import com.red.circle.other.infra.database.cache.service.user.UserRegionCacheService;
import com.red.circle.other.infra.database.cache.service.user.UserRelationshipCacheService;
import com.red.circle.other.infra.database.cache.service.user.UserRunProfileCacheService;
import com.red.circle.other.infra.database.mongo.entity.user.profile.UserSpecialId;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamMemberService;
import com.red.circle.other.infra.database.mongo.service.user.profile.UserRunProfileService;
import com.red.circle.other.infra.database.mongo.service.user.profile.UserSpecialIdService;
import com.red.circle.other.infra.database.rds.entity.props.PropsNobleVipAbility;
import com.red.circle.other.infra.database.rds.entity.user.user.BaseInfo;
import com.red.circle.other.infra.database.rds.entity.user.user.ConsumptionLevel;
import com.red.circle.other.infra.database.rds.entity.user.user.UserExpand;
import com.red.circle.other.infra.database.rds.service.props.PropsNobleVipAbilityService;
import com.red.circle.other.infra.database.rds.service.props.PropsVipActualEquityService;
import com.red.circle.other.infra.database.rds.service.user.user.BaseInfoService;
import com.red.circle.other.infra.database.rds.service.user.user.ConsumptionLevelService;
import com.red.circle.other.infra.database.rds.service.user.user.ExpandService;
import com.red.circle.other.infra.database.rds.service.user.user.RelationshipFriendService;
import com.red.circle.other.infra.database.rds.service.user.user.UserSubscriptionService;
import com.red.circle.other.inner.enums.material.NobleVipEnum;
import com.red.circle.other.inner.enums.material.PropsCommodityType;
import com.red.circle.other.inner.enums.material.PropsVipActualEquityEnum;
import com.red.circle.other.inner.model.cmd.user.UserProfileListQryCmd;
import com.red.circle.other.inner.model.dto.material.UsePropsDTO;
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.text.StringUtils;
import com.red.circle.tool.core.thread.ThreadPoolManager;
import com.red.circle.tool.core.tuple.ImmutableKeyValuePair;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * 用户仓库 实现.
 *
 * @author pengliang on 2021/5/26
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class UserProfileGatewayImpl implements UserProfileGateway {

  private final ExpandService expandService;
  private final ImAccountClient imAccountClient;
  private final UserBadgeCommon userBadgeCommon;
  private final BaseInfoService baseInfoService;
  private final UserCacheService userCacheService;
  private final TeamMemberService teamMemberService;
  private final PropsBackpackCommon propsBackpackCommon;
  private final UserSpecialIdService userSpecialIdService;
  private final UserRunProfileService userRunProfileService;
  private final ConsumptionLevelService consumptionLevelService;
  private final UserSubscriptionService userSubscriptionService;
  private final UserProfileInfraConvertor userProfileInfraConvertor;
  private final RelationshipFriendService relationshipFriendService;
  private final PropsNobleVipAbilityService propsNobleVipAbilityService;
  private final PropsVipActualEquityService propsVipActualEquityService;
  private final UserRunProfileTransportGateway userRunProfileTransport;
  private final UserRelationshipCacheService userRelationshipCacheService;
  private final UserRunProfileCacheService userRunProfileCacheService;
  private final UserRegionCacheService userRegionCacheService;


  @Override
  public UserProfile getByAccount(String sysOrigin, String account) {
    UserProfile userProfile = userRunProfileTransport.getByAccount(account,sysOrigin);
    if (Objects.nonNull(userProfile)) {
      return userProfile;
    }

    UserSpecialId userSpecialId = userSpecialIdService.getUserSpecialId(sysOrigin, account);
    if (Objects.isNull(userSpecialId)) {
      return null;
    } else if (userSpecialId.getExpiredTime() != null) {
      if (userSpecialId.getExpiredTime().getTime() < System.currentTimeMillis()) {
        return null;
      }
    }
    return Optional.of(userSpecialId)
            .map(specialId -> userRunProfileTransport.getByUserId(specialId.getUserId()))
            .orElse(null);
  }

  @Override
  public UserProfile getByAccountNew(String sysOrigin, String account) {
    UserProfile userProfile = userRunProfileTransport.getByAccount(account,sysOrigin);
    if (Objects.nonNull(userProfile)) {
      return userProfile;
    }
    return Optional.ofNullable(userSpecialIdService.getUserSpecialId(sysOrigin, account))
            .map(specialId -> userRunProfileTransport.getByUserId(specialId.getUserId()))
            .orElse(null);
  }

  @Override
  public List<UserProfile> pageListQuery(UserProfileListQryCmd query) {
    List<BaseInfo> userBaseInfos = baseInfoService.pageQuery(
        userProfileInfraConvertor.toUserBaseInfoPageQryCmd(query)
    );
    if (CollectionUtils.isEmpty(userBaseInfos)) {
      return Lists.newArrayList();
    }

    return listUserProfiles(userBaseInfos);
  }

  private List<UserProfile> listUserProfiles(List<BaseInfo> userBaseInfos) {
    Map<Long, UserSpecialId> userSpecialIdMap = userSpecialIdService.mapByUserIds(
        userBaseInfos.stream().map(BaseInfo::getId).collect(
            Collectors.toSet()));

    return userBaseInfos.stream()
        .map(baseInfo -> mergeUserProfile(userSpecialIdMap.get(baseInfo.getId()), baseInfo))
        .collect(Collectors.toList());
  }

  @Override
  public List<UserProfile> getByAccount(String account) {
    BaseInfo baseInfo = baseInfoService.getByAccount(account);
    if (Objects.nonNull(baseInfo)) {
      return Collections.singletonList(
          mergeUserProfile(userSpecialIdService.getByUserId(baseInfo.getId()), baseInfo));
    }
    List<UserSpecialId> userSpecialIds = userSpecialIdService.listByAccount(account);
    if (CollectionUtils.isNotEmpty(userSpecialIds)) {

      Map<Long, BaseInfo> userBaseInfoMap = baseInfoService.mapBaseInfo(
          userSpecialIds.stream().map(UserSpecialId::getUserId).collect(Collectors.toSet()));

      return userSpecialIds.stream().map(userSpecialId -> mergeUserProfile(userSpecialId,
              userBaseInfoMap.get(userSpecialId.getUserId())))
          .filter(Objects::nonNull)
          .collect(Collectors.toList());
    }
    return Lists.newArrayList();
  }

  private UserProfile mergeUserProfile(UserSpecialId userSpecialId, BaseInfo baseInfo) {
    return Optional.ofNullable(toUserProfile(baseInfo))
        .map(userProfile -> {
          userProfile.setOwnSpecialId(toOwnSpecialId(userSpecialId));
          return userProfile;
        }).orElse(null);
  }

  private UserProfile toUserProfile(BaseInfo baseInfo) {
    if (Objects.isNull(baseInfo)) {
      return null;
    }
    UserProfile userProfile = userProfileInfraConvertor.toUserProfile(baseInfo);
    
    // 从 MySQL 的 JSON 字段转换图片列表
    if (StringUtils.isNotBlank(baseInfo.getBackgroundPhoto())) {
      userProfile.setBackgroundPhotos(userProfileInfraConvertor.jsonToPhotoItems(baseInfo.getBackgroundPhoto()));
    }
    if (StringUtils.isNotBlank(baseInfo.getPersonalPhoto())) {
      userProfile.setPersonalPhotos(userProfileInfraConvertor.jsonToPhotoItems(baseInfo.getPersonalPhoto()));
    }
    
    return userProfile;
  }

  private OwnSpecialId toOwnSpecialId(UserSpecialId userSpecialId) {
    return userProfileInfraConvertor.toOwnSpecialId(userSpecialId);
  }

  @Override
  public UserProfile getByUserId(Long userId) {
    return userRunProfileTransport.getByUserId(userId);
  }

  @Override
  public String getLanguage(Long userId) {
    return userCacheService.getLanguage(userId, expandService::getLanguageStr);
  }

  @Override
  public Map<Long, String> mapLanguage(Set<Long> userIds) {

    if (CollectionUtils.isEmpty(userIds)) {
      return CollectionUtils.newHashMap();
    }

    List<UserExpand> userExpands = expandService.listUserExpand(userIds);
    if (CollectionUtils.isEmpty(userExpands)) {
      return CollectionUtils.newHashMap();
    }

    return userExpands.stream()
        .collect(Collectors.toMap(UserExpand::getUserId, UserExpand::getLanguage));
  }

  @Override
  public String getSysOrigin(Long userId) {
    return userCacheService.getSysOrigin(userId, baseInfoService::getSysOrigin);
  }

  @Override
  public List<UserProfile> listByUserIds(Set<Long> userIds) {
    List<UserProfile> userProfiles = userRunProfileTransport.listUserProfile(userIds);
    if(CollectionUtils.isEmpty(userProfiles)){
      return userProfiles;
    }
    Map<Long, UserSpecialId> userSpecialIdMap = userSpecialIdService.mapByUserIds(
        userProfiles.stream().map(UserProfile::getId).collect(
            Collectors.toSet()));
    return userProfiles.stream().map(userProfile -> {
      userProfile.setOwnSpecialId(toOwnSpecialId(userSpecialIdMap.get(userProfile.getId())));
      return userProfile;
    }).collect(Collectors.toList());
  }

  @Override
  public List<UserProfile> listByAccount(String sysOrigin, Set<String> accounts) {
    List<UserProfile> userProfiles = userRunProfileTransport.listByAccounts(accounts);

//    if (CollectionUtils.isEmpty(userProfiles)) {
//      return CollectionUtils.newArrayList();
//    }

    List<UserSpecialId> userSpecialIds = userSpecialIdService.listUserSpecialId(sysOrigin,
        accounts);
    if (CollectionUtils.isEmpty(userSpecialIds)) {
      return userProfiles;
    }

    List<UserProfile> userProfileSpecials = listByUserIds(
        userSpecialIds.stream().map(UserSpecialId::getUserId).collect(Collectors.toSet()));

    if (CollectionUtils.isEmpty(userProfileSpecials)) {
      return userProfiles;
    }

    userProfiles.addAll(userProfileSpecials);
    return userProfiles;
  }

  @Override
  public Map<Long, UserProfile> mapByUserIds(Set<Long> userIds) {
    List<UserProfile> userProfiles = listByUserIds(userIds);
    if (CollectionUtils.isEmpty(userProfiles)) {
      return Maps.newHashMap();
    }
    return userProfiles.stream().collect(Collectors.toMap(UserProfile::getId, v -> v));
  }

  @Override
  public void updateSelectiveById(UserProfile userProfile) {
    BaseInfo baseInfo = userProfileInfraConvertor.toBaseInfo(userProfile);
    if (StringUtils.isNotBlank(userProfile.getCheckStatus())){
      baseInfo.setAccountStatus(userProfile.getCheckStatus());
    }
    
    // 处理背景照片：如果传入非null，则更新（包括空列表）
    if (userProfile.getBackgroundPhotos() != null) {
      baseInfo.setBackgroundPhoto(
              CollectionUtils.isNotEmpty(userProfile.getBackgroundPhotos())
                      ? JSON.toJSONString(userProfile.getBackgroundPhotos())
                      : "[]"
      );
    }

    // 处理个人形象照片：如果传入非null，则更新（包括空列表）
    if (userProfile.getPersonalPhotos() != null) {
      baseInfo.setPersonalPhoto(
              CollectionUtils.isNotEmpty(userProfile.getPersonalPhotos() )
                      ? JSON.toJSONString(userProfile.getPersonalPhotos())
                      : "[]"
      );
    }

    baseInfoService.updateSelectiveById(baseInfo);
    userRunProfileService.updateSelectiveById(
        userProfileInfraConvertor.toUserRunProfile(userProfile));
  }

  @Override
  public void updateUserNickname(Long id, String nickname) {
    baseInfoService.updateNickname(id, nickname);
    userRunProfileService.updateUserNickname(id, nickname);

    ThreadPoolManager.getInstance()
        .execute(() -> imAccountClient.portraitSetNickname(id, nickname));
  }

  @Override
  public void updateUserNicknameBatch(
      Collection<ImmutableKeyValuePair<Long, String>> keyValuePairs) {
    baseInfoService.updateNicknameBatch(keyValuePairs);
    userRunProfileService.updateBatchUserNickname(keyValuePairs);

    ThreadPoolManager.getInstance().execute(() -> keyValuePairs.forEach(
        pair -> imAccountClient.portraitSetNickname(pair.getKey(), pair.getValue())));
  }


  @Override
  public void updateUserAvatar(Long id, String avatar) {
    baseInfoService.updateAvatar(id, avatar);
    userRunProfileService.updateUserAvatar(id, avatar);

    ThreadPoolManager.getInstance()
        .execute(() -> imAccountClient.portraitSetAvatar(id, avatar));
  }

  @Override
  public void updateUserAvatarBatch(Collection<Long> ids, String avatar) {
    baseInfoService.updateAvatar(ids, avatar);
    userRunProfileService.updateUserAvatarBatch(ids, avatar);

    ThreadPoolManager.getInstance().execute(() -> ids.forEach(
        userId -> imAccountClient.portraitSetNickname(userId, avatar)));
  }

  @Override
  public void switchUseProps(Long userId, Long propsId) {

    if (Objects.isNull(userId) || Objects.isNull(propsId)) {
      throw new IllegalArgumentException("switchUseProps param Error");
    }

    UserProfile userProfile = userRunProfileTransport.getByUserId(userId);

    if (Objects.isNull(userProfile)) {
      return;
    }

    UsePropsDTO useProps = propsBackpackCommon.switchUseProps(userId, propsId);
    userRunProfileService.updateUserUseProps(userProfile.getId(), useProps);
    userCacheService.removeUserActualEquityEnum(userProfile.getId());
  }

  @Override
  public void removeUseProps(Long userId, String propsType) {

    UserProfile userProfile = userRunProfileTransport.getByUserId(userId);
    if (Objects.isNull(userProfile)) {
      log.error("removeUseProps user not found:{}", userId);
      return;
    }

    propsBackpackCommon.unloadUseProps(userId, PropsCommodityType.valueOf(propsType));

    List<UserUsePropsDTO> availableUserUseProps = userProfile.getUseProps().stream()
        .filter(props ->
            Objects.nonNull(props.getPropsResources())
                && !Objects.equals(props.getPropsResources().getType(), propsType)
                && props.getExpireTime() > TimestampUtils.now().getTime())
        .collect(Collectors.toList());

    userRunProfileService.updateUserUsePropsOverlay(userId,
        userProfileInfraConvertor.toListUsePropsDTO(availableUserUseProps));
    userCacheService.removeUserActualEquityEnum(userProfile.getId());
  }

  @Override
  public void updateUserWearBadge(Long id, String sysOrigin, Collection<Long> wearBadgeIds) {
    // 切换使用标记
    userBadgeCommon.updateUseBadge(id, wearBadgeIds);

    // 主动刷新缓存数据
    if (CollectionUtils.isEmpty(wearBadgeIds)) {
      userRunProfileService.updateUserWearBadgesOverlay(id, Lists.newArrayList());
      return;
    }

    userRunProfileService.updateUserWearBadgesOverlay(id,
        userBadgeCommon.listUseBadge(id, sysOrigin, wearBadgeIds)
    );
  }

  @Override
  public void removeUserAllWearBadge(Long id, String sysOrigin) {
    // 切换使用标记
    userBadgeCommon.useOffByUserId(id);

    userRunProfileService.updateUserWearBadgesOverlay(id, Lists.newArrayList());
  }

  @Override
  public void removeCacheAll(Long id) {
    if (Objects.nonNull(id)) {
      userRunProfileService.remove(id);
      userCacheService.remove(id);
      userRunProfileCacheService.remove(id);
      userRegionCacheService.remove(id);
    }
  }

  @Override
  public void removeCacheAll(Collection<Long> ids) {
    if (CollectionUtils.isNotEmpty(ids)) {
      userRunProfileService.remove(ids);
      userCacheService.remove(ids);
    }
  }

  @Override
  public void removeCache(Long id) {
    userCacheService.remove(id);
  }

  @Override
  public boolean checkTeamMember(Long userId) {
    return userCacheService.cacheTeamMember(userId, teamMemberService::existsMember);
  }

  @Override
  public UserConsumptionLevel getUserConsumptionLevel(SysOriginPlatformEnum sysOrigin, Long userId) {
    return userProfileInfraConvertor.toUserConsumptionLevel(
        userCacheService.getUserConsumptionLevel(userId, (uid) -> {
              ConsumptionLevel level = consumptionLevelService.getByUserId(uid);
              if (Objects.isNull(level)) {
                return new ConsumptionLevelCache()
                    .setWealthLevel(0)
                    .setWealthExp(BigDecimal.ZERO)
                    .setCharmLevel(0)
                    .setCharmExp(BigDecimal.ZERO);
              }

              return new ConsumptionLevelCache()
                  .setWealthLevel(LevelUtils.getWealthLevel(sysOrigin, level.getConsumptionGolds().longValue()).getLevel())
                  .setWealthExp(level.getConsumptionGolds())
                  .setCharmLevel(LevelUtils.getUserLevelV2(sysOrigin, level.getConsumptionDiamond().longValue()).getLevel())
                  .setCharmExp(level.getConsumptionDiamond());
            })
    );
  }


  @Override
  public boolean checkFriend(Long userId, Long friendUserId) {
    return userRelationshipCacheService.checkFriend(userId, friendUserId,
        relationshipFriendService::checkFriend);
  }

  @Override
  public boolean checkFollow(Long userId, Long followUserId) {
    return userRelationshipCacheService.checkFollow(userId, followUserId,
        userSubscriptionService::checkSubscription);
  }

  @Override
  public boolean isPurchasing(Long userId) {
    return userCacheService.isPurchasing(userId, expandService::checkPurchasing);
  }

  @Override
  public List<PropsVipActualEquityEnum> listUserActualEquityEnum(Long userId) {
    return userCacheService.getUserActualEquityEnum(userId, this::getUserPropsVipActualEquity);
  }

  @Override
  public void updateBatchUserAutographEmpty(Collection<Long> ids) {
    if (CollectionUtils.isEmpty(ids)) {
      return;
    }
    // TODO 签名字段被干掉了， 新逻辑设计后补充
    Set<Long> userIds = new HashSet<>(ids);
    //  expandService.cleanBatchSignature(userIds);
    userRunProfileService.updateBatchUserAutographEmpty(userIds);
  }

  private List<PropsVipActualEquityEnum> getUserPropsVipActualEquity(Long userId) {
    UserProfile userProfile = getByUserId(userId);
    if (Objects.isNull(userProfile)) {
      return Lists.newArrayList();
    }

    Set<Long> userVipPropsIds = getUserVipPropsIds(userProfile.getUseProps());
    if (CollectionUtils.isEmpty(userVipPropsIds)) {
      return Lists.newArrayList();
    }

    Set<Long> propsVips = propsVipActualEquityService.getActualEquityNotExpired(
        userProfile.getId());
    if (CollectionUtils.isEmpty(propsVips)) {
      return Lists.newArrayList();
    }

    userVipPropsIds.retainAll(propsVips);
    if (CollectionUtils.isEmpty(userVipPropsIds)) {
      return Lists.newArrayList();
    }

    List<PropsNobleVipAbility> vipAbilities = propsNobleVipAbilityService.listByIds(
        userVipPropsIds);

    if (CollectionUtils.isEmpty(vipAbilities)) {
      return Lists.newArrayList();
    }

    if (vipAbilities.stream()
        .anyMatch(vip -> Objects.equals(vip.getVipType(), NobleVipEnum.EMPEROR.name()))) {

      return Lists.newArrayList(PropsVipActualEquityEnum.SPEAK, PropsVipActualEquityEnum.MICROPHONE,
          PropsVipActualEquityEnum.PROHIBIT_KICK);
    }

    if (vipAbilities.stream()
        .anyMatch(vip -> Objects.equals(vip.getVipType(), NobleVipEnum.KING.name()))) {

      return Lists.newArrayList(PropsVipActualEquityEnum.SPEAK, PropsVipActualEquityEnum.MICROPHONE,
          PropsVipActualEquityEnum.PROHIBIT_KICK);
    }

    if (Objects.equals(userProfile.getOriginSys(), SysOriginPlatformEnum.YOLO.getSysOrigin())) {
      return Lists.newArrayList();
    }
    if (Objects.equals(userProfile.getOriginSys(), SysOriginPlatformEnum.TARAB.getSysOrigin())) {
      return Lists.newArrayList();
    }
    if (vipAbilities.stream()
        .anyMatch(vip -> Objects.equals(vip.getVipType(), NobleVipEnum.DUKE.name()))) {

      return Lists
          .newArrayList(PropsVipActualEquityEnum.SPEAK, PropsVipActualEquityEnum.MICROPHONE);
    }
    return Lists.newArrayList();
  }

  @Override
  public List<NobleAbilityCO> getUserNobleAbility(Long userId) {
    return userCacheService.getUserNobleAbility(userId, this::getNobleAbility);
  }

  private List<NobleAbilityCO> getNobleAbility(Long userId) {
    UserProfile userProfile = getByUserId(userId);
    if (Objects.isNull(userProfile)) {
      return null;
    }

    Set<Long> userVipPropsIds = getUserVipPropsIds(userProfile.getUseProps());
    if (CollectionUtils.isEmpty(userVipPropsIds)) {
      return null;
    }

    List<PropsNobleVipAbility> vipAbilities = propsNobleVipAbilityService.listByIds(userVipPropsIds);

    if (CollectionUtils.isEmpty(vipAbilities)) {
      return null;
    }

    return vipAbilities.stream().map(e -> {
      NobleAbilityCO result = new NobleAbilityCO();
      result.setVipType(e.getVipType());
      result.setVipLevel(e.getVipLevel());
      result.setRoomMaxMember(e.getRoomMaxMember());
      return result;
    }).toList();
  }

  /**
   * 获得贵族道具ID
   */
  private Set<Long> getUserVipPropsIds(List<UserUsePropsDTO> useProps) {

    if (CollectionUtils.isEmpty(useProps)) {
      return Sets.newHashSet();
    }

    return useProps.stream().map(props -> {
      if (Objects.isNull(props.getPropsResources()) || !Objects
          .equals(props.getPropsResources().getType(),
              PropsCommodityType.NOBLE_VIP.name())) {
        return null;
      }
      return props.getPropsResources().getId();
    }).filter(Objects::nonNull).collect(Collectors.toSet());
  }

}
