package com.red.circle.other.infra.database.cache.service.user.impl;

import com.red.circle.component.redis.service.RedisService;
import com.red.circle.other.domain.model.user.UserProfile;
import com.red.circle.other.infra.convertor.user.UserProfileInfraConvertor;
import com.red.circle.other.infra.database.cache.key.user.UserKey;
import com.red.circle.other.infra.database.cache.service.user.UserProfileCacheService;
import com.red.circle.other.infra.database.rds.entity.user.user.BaseInfo;
import com.red.circle.other.infra.database.rds.service.user.user.BaseInfoService;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.json.JacksonUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 用户资料缓存.
 *
 * @author pengliang on 2023/11/16
 */
@Service
@RequiredArgsConstructor
public class UserProfileCacheServiceImpl implements UserProfileCacheService {

  private final RedisService redisService;
  private final BaseInfoService baseInfoService;
  private final UserProfileInfraConvertor userProfileInfraConvertor;

  @Override
  public UserProfile getUserProfile(Long userId) {
    String userKey = UserKey.PROFILE.getKey(userId);
    UserProfile userProfile = redisService.getStringToObject(userKey,
        UserProfile.class);

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

    UserProfile dbProfile = userProfileInfraConvertor.toUserProfile(
        baseInfoService.getById(userId));

    if (Objects.nonNull(dbProfile)) {
      redisService.setString(userKey, dbProfile, 7, TimeUnit.DAYS);
    }

    return dbProfile;
  }

  @Override
  public List<UserProfile> listUserProfile(Set<Long> userIds) {

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

    List<UserProfile> userProfiles = redisService.multiGet(
        userIds.stream().map(UserKey.PROFILE::getKey).toList()
        , userProfile -> JacksonUtils.readValue(Objects.toString(userProfile), UserProfile.class));

    if (CollectionUtils.isEmpty(userProfiles)) {
      return listDbUserProfiles(userIds);
    }

    List<UserProfile> users = userProfiles.stream().filter(Objects::nonNull).toList();

    if (Objects.equals(users.size(), userIds.size())) {
      return users;
    }

    List<Long> existsUserIds = users.stream().map(UserProfile::getId).toList();

    List<UserProfile> dbUserProfiles = listDbUserProfiles(
        userIds.stream().filter(uid -> !existsUserIds.contains(uid)).collect(Collectors.toSet()));

    if (CollectionUtils.isNotEmpty(dbUserProfiles)) {
      userProfiles.addAll(dbUserProfiles);
    }

    return userProfiles;
  }

  private List<UserProfile> listDbUserProfiles(Set<Long> userIds) {
    List<BaseInfo> baseInfos = baseInfoService.listByUserId(userIds);

    if (CollectionUtils.isEmpty(baseInfos)) {
      return CollectionUtils.newArrayList();
    }

    for (BaseInfo baseInfo : baseInfos) {
      redisService.setString(UserKey.PROFILE.getKey(baseInfo.getId()), baseInfo, 7,
          TimeUnit.DAYS);
    }

    return userProfileInfraConvertor.toBaseInfoListUserProfile(baseInfos);
  }

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

  @Override
  public String getSysOrigin(Long userId) {
    return Optional.ofNullable(getUserProfile(userId))
        .map(UserProfile::getOriginSys)
        .orElse(null);
  }


    @Override
    public String getUserAvatar(Long userId) {
        return Optional.ofNullable(getBaseInfo(userId))
                .map(BaseInfo::getUserAvatar)
                .orElse(null);
    }

    @Override
    public String getUserNickname(Long userId) {
        return Optional.ofNullable(getBaseInfo(userId))
                .map(BaseInfo::getUserNickname)
                .orElse("");
    }

    @Override
    public BaseInfo getBaseInfo(Long userId) {
        return baseInfoService.getById(userId);
    }

}
