package com.red.circle.other.app.inner.service.material.props.impl;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.response.ResponseErrorCode;
import com.red.circle.framework.web.util.ValidatorUtils;
import com.red.circle.other.app.inner.convertor.material.BadgeInnerConvertor;
import com.red.circle.other.app.inner.service.material.props.BadgeBackpackClientService;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.domain.model.user.UserProfile;
import com.red.circle.other.infra.common.props.UserBadgeCommon;
import com.red.circle.other.infra.database.mongo.entity.live.RoomProfile;
import com.red.circle.other.infra.database.mongo.service.live.RoomProfileManagerService;
import com.red.circle.other.infra.database.rds.entity.badge.BadgeBackpack;
import com.red.circle.other.infra.database.rds.entity.badge.BadgeConfig;
import com.red.circle.other.infra.database.rds.entity.badge.BadgePictureConfig;
import com.red.circle.other.infra.database.rds.entity.badge.RoomBadgeBackpack;
import com.red.circle.other.infra.database.rds.service.badge.BadgeBackpackService;
import com.red.circle.other.infra.database.rds.service.badge.BadgeConfigService;
import com.red.circle.other.infra.database.rds.service.badge.BadgePictureConfigService;
import com.red.circle.other.infra.database.rds.service.badge.RoomBadgeBackpackService;
import com.red.circle.other.inner.asserts.user.UserErrorCode;
import com.red.circle.other.inner.enums.material.BadgeBackpackExpireType;
import com.red.circle.other.inner.enums.material.BadgeBackpackExpireTypeEnum;
import com.red.circle.other.inner.enums.sys.SysBadgeConfigTypeEnum;
import com.red.circle.other.inner.model.cmd.material.GiveBadgeCmd;
import com.red.circle.other.inner.model.dto.material.BadgeBackpackDTO;
import com.red.circle.other.inner.model.dto.material.BadgePictureConfigDTO;
import com.red.circle.other.inner.model.dto.material.UseBadgeDTO;
import com.red.circle.other.inner.model.dto.user.props.UserUseBadgeDTO;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.json.JacksonUtils;
import com.red.circle.tool.core.sequence.IdWorkerUtils;
import java.sql.Timestamp;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

/**
 * 徽章客户端 服务实现.
 *
 * @author pengliang on 2023/6/3
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class BadgeBackpackClientServiceImpl implements BadgeBackpackClientService {

  private final UserBadgeCommon userBadgeCommon;
  private final UserProfileGateway userProfileGateway;
  private final BadgeConfigService badgeConfigService;
  private final BadgeInnerConvertor badgeInnerConvertor;
  private final BadgeBackpackService badgeBackpackService;
  private final RoomBadgeBackpackService roomBadgeBackpackService;
  private final RoomProfileManagerService roomProfileManagerService;
  private final BadgePictureConfigService badgePictureConfigService;

  @Override
  public List<UseBadgeDTO> listUseBadge(Long userId, String sysOrigin, Set<Long> wearBadgeId) {
    return userBadgeCommon.listUseBadge(userId, sysOrigin, wearBadgeId);
  }

  @Override
  public List<BadgeBackpackDTO> listUseBadgeBackpackByUserId(Long userId) {
    return badgeInnerConvertor.toListBadgeBackpackDTO(
        badgeBackpackService.listUseBadgeByUserId(userId));
  }

  @Override
  public void updateUseBadge(Long userId, Set<Long> badgeIds) {
    badgeBackpackService.updateUseBadge(userId, badgeIds);
  }

  @Override
  public void useOffByUserId(Long userId) {
    badgeBackpackService.updateUseOffByUserId(userId);
  }

  @Override
  public void reduceDaysAndUnUse(Long userId, Set<Long> badgeIds, Integer reduceDays) {
    userBadgeCommon.reduceDaysAndUnUse(userId, badgeIds, reduceDays);
  }

  @Override
  public boolean activationPermanentAndUse(Long userId, Long badgeId) {
    return badgeBackpackService.activationPermanentAndUse(userId, badgeId);
  }

  @Override
  public boolean activationTemporaryAndUse(Long userId, Long badgeId, Integer days) {
    return badgeBackpackService.activationTemporaryAndUse(userId, badgeId, days);
  }

  @Override
  public void deleteBadge(Collection<Long> userIds, Collection<Long> badgeIds) {
    badgeBackpackService.deleteBadge(userIds, badgeIds);
  }

  @Override
  public void saveBatchBackpack(List<BadgeBackpackDTO> badges) {
    badgeBackpackService.saveBatch(badgeInnerConvertor.toListBadgeBackpack(badges));
  }

  @Override
  public void saveBackpack(BadgeBackpackDTO badge) {
    badgeBackpackService.save(badgeInnerConvertor.toBadgeBackpack(badge));
  }

  @Override
  public List<BadgeBackpackDTO> listUseNotExpireTimeByUserIds(Set<Long> userIds) {
    return userBadgeCommon.listUseNotExpireTimeByUserIds(userIds);
  }

  @Override
  public void deleteUserBadge(Long userId, Long badgeId) {
    badgeBackpackService.deleteUserBadge(userId, badgeId);
  }

  @Override
  public void deleteBadge(Long userId, Long badgeId) {
    if (Objects.isNull(badgeId)) {
      return;
    }
    badgeBackpackService.deleteBadges(userId, Sets.newHashSet(badgeId));
    userProfileGateway.removeCache(userId);
    UserProfile userProfile = userProfileGateway.getByUserId(userId);
    if (Objects.isNull(userProfile)) {
      return;
    }
    List<UserUseBadgeDTO> wearBadge = userProfile.getWearBadge();
      List<UserUseBadgeDTO> honorBadge = userProfile.getWearHonor();
      wearBadge.addAll(honorBadge);
    if (CollectionUtils.isEmpty(wearBadge)) {
      return;
    }
    //过滤需要删除的 wearBadge id
    Set<Long> saveWear = wearBadge.stream().map(UserUseBadgeDTO::getId)
        .filter(id -> !badgeId.equals(id)).collect(Collectors.toSet());
    userProfileGateway.updateUserWearBadge(userId, userProfile.getOriginSys(),saveWear);
  }

    @Override
  public void removeWearByIds(Set<Long> ids) {
    badgeBackpackService.removeWearByIds(ids);
  }

  @Override
  public void useOffByIds(Set<Long> ids) {
    if (CollectionUtils.isEmpty(ids)) {
      return;
    }
    badgeBackpackService.updateOffByIds(ids);
  }

  @Override
  public List<BadgePictureConfigDTO> listBadge(Long userId, String sysOrigin) {

    List<BadgeBackpackDTO> badgeBackpacks = badgeInnerConvertor.toListBadgeBackpackDTO(
        badgeBackpackService.listBadge(userId));

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

    Set<Long> badgeIds = badgeBackpacks.stream().map(BadgeBackpackDTO::getBadgeId)
        .collect(Collectors.toSet());
    if (CollectionUtils.isEmpty(badgeIds)) {
      return Lists.newArrayList();
    }

    Map<Long, BadgeConfig> badgeConfigMap = badgeConfigService.mapByIds(badgeIds);
    if (CollectionUtils.isEmpty(badgeConfigMap)) {
      return Lists.newArrayList();
    }

    Map<Long, BadgePictureConfig> mapBadgePicture = getMapBadgePicture(sysOrigin, badgeIds);
    if (CollectionUtils.isEmpty(mapBadgePicture)) {
      return Lists.newArrayList();
    }
    return badgeBackpacks.stream().map(badgeBackpack -> {

      BadgePictureConfigDTO pictureConfigVO = badgeInnerConvertor.toBadgePictureConfigDTO(
          mapBadgePicture.get(badgeBackpack.getBadgeId()));
      if (Objects.isNull(pictureConfigVO)) {
        return null;
      }

      BadgeConfig badgeConfig = badgeConfigMap.get(badgeBackpack.getBadgeId());
      if (Objects.isNull(badgeConfig)) {
        return null;
      }

      if (!Objects.equals(badgeConfig.getType(), SysBadgeConfigTypeEnum.ADMINISTRATOR.name()) &&
          !Objects.equals(badgeConfig.getType(), SysBadgeConfigTypeEnum.ACTIVITY.name())) {
        return null;
      }

      pictureConfigVO.setType(badgeConfig.getType());
      pictureConfigVO.setTypeName(SysBadgeConfigTypeEnum.getDescValue(badgeConfig.getType()));
      pictureConfigVO.setBadgeName(badgeConfig.getBadgeName());

      return pictureConfigVO;

    }).filter(Objects::nonNull).collect(Collectors.toList());
  }


  private Map<Long, BadgePictureConfig> getMapBadgePicture(String sysOrigin,
      Set<Long> badgeIds) {
    return badgePictureConfigService.mapBadge(sysOrigin, badgeIds);
  }


  @Override
  public void giveBadgePermanent(Long badgeId, Long userId) {

    ResponseAssert.notNull(ResponseErrorCode.REQUEST_PARAMETER_ERROR, badgeId);

    isUserNormal(userProfileGateway.getByUserId(userId));

    badgeBackpackService.deleteBadges(userId, Sets.newHashSet(badgeId));

    badgeBackpackService.save(new BadgeBackpack()
        .setUserId(userId)
        .setBadgeId(badgeId)
        .setExpireType(BadgeBackpackExpireType.PERMANENT.name())
        .setUseProps(Boolean.TRUE)
        .setExpireTime(TimestampUtils.now())
    );

    userProfileGateway.removeCacheAll(userId);
  }

  @Override
  public void giveBadge(GiveBadgeCmd cmd) {

    if (!ValidatorUtils.validateFastPass(cmd)) {
      throw new IllegalArgumentException("giveBadge param error");
    }

    isUserNormal(userProfileGateway.getByUserId(cmd.getAcceptUserId()));

    // 非用户房间徽章
    if (!Objects.equals(cmd.getBadgeType(), SysBadgeConfigTypeEnum.ROOM_ACHIEVEMENT)) {

      BadgeBackpack badgeBackpack = badgeBackpackService.getByUserIdByBadgeId(cmd.getAcceptUserId(),
          cmd.getBadgeId());
      if (Objects.isNull(badgeBackpack)) {
        badgeBackpackService
            .save(buildSaveUserBadgeBackpack(cmd.getBadgeId(), cmd.getAcceptUserId(),
                cmd.getExpireType(), cmd.getDays()));
        userProfileGateway.removeCacheAll(cmd.getAcceptUserId());
        return;
      }

      if (Objects.equals(cmd.getExpireType(), BadgeBackpackExpireTypeEnum.TEMPORARY)) {
        badgeBackpack.setExpireTime(getExpireTime(cmd.getDays(), badgeBackpack.getExpireTime()));
      }
      badgeBackpack.setExpireType(Objects.toString(cmd.getExpireType()));
      badgeBackpack.setUseProps(Boolean.TRUE);
      badgeBackpackService.updateSelectiveById(badgeBackpack);
      userProfileGateway.removeCacheAll(cmd.getAcceptUserId());
      return;
    }

    RoomProfile roomProfile = roomProfileManagerService.getProfileByUserId(cmd.getAcceptUserId());
    if (Objects.equals(roomProfile.getDel(), Boolean.TRUE)) {
      return;
    }

    BadgeConfig badgeConfig = badgeConfigService.getById(cmd.getBadgeId());
    if (Objects.isNull(badgeConfig)) {
      return;
    }

    BadgePictureConfig sysBadgePictureConfig = badgePictureConfigService
        .getByConfigIdBySysOrigin(cmd.getBadgeId(), roomProfile.getSysOrigin());
    if (Objects.isNull(sysBadgePictureConfig)) {
      return;
    }

    // 用户房间徽章
    RoomBadgeBackpack roomBadge = roomBadgeBackpackService
        .getByUserIdByBadgeId(cmd.getAcceptUserId(), cmd.getBadgeId());

    if (Objects.isNull(roomBadge)) {

      Timestamp expireTime =
          Objects.equals(cmd.getExpireType(), BadgeBackpackExpireTypeEnum.TEMPORARY)
              ? TimestampUtils.nowPlusDays(cmd.getDays())
              : TimestampUtils.now();

      roomBadgeBackpackService
          .save(new RoomBadgeBackpack()
              .setId(IdWorkerUtils.getId())
              .setUserId(cmd.getAcceptUserId())
              .setBadgeId(cmd.getBadgeId())
              .setRoomId(roomProfile.getId())
              .setExpireTime(expireTime)
              .setExpireType(Objects.toString(cmd.getExpireType()))
          );

      roomProfileManagerService.addBadge(roomProfile.getId(),
          toUseBadgeDTO(cmd.getBadgeId(), cmd.getAcceptUserId(),
              badgeConfig, sysBadgePictureConfig, expireTime));
      return;
    }

    if (Objects.equals(cmd.getExpireType(), BadgeBackpackExpireTypeEnum.TEMPORARY)) {
      roomBadge.setExpireTime(getExpireTime(cmd.getDays(), roomBadge.getExpireTime()));
    }
    roomBadge.setExpireType(Objects.toString(cmd.getExpireType()));
    roomBadgeBackpackService.updateSelectiveById(roomBadge);

    roomProfileManagerService.addBadge(roomProfile.getId(),
        toUseBadgeDTO(cmd.getBadgeId(), cmd.getAcceptUserId(),
            badgeConfig, sysBadgePictureConfig, roomBadge.getExpireTime()));

  }

  private UseBadgeDTO toUseBadgeDTO(Long badgeId, Long userId, BadgeConfig badgeConfig,
      BadgePictureConfig sysBadgePictureConfig, Timestamp expireTime) {

    UseBadgeDTO useBadgeEntity = new UseBadgeDTO();
    useBadgeEntity.setId(badgeId);
    useBadgeEntity.setUserId(userId);
    useBadgeEntity.setBadgeLevel(badgeConfig.getBadgeLevel());
    useBadgeEntity.setMilestone(badgeConfig.getMilestone());
    useBadgeEntity.setBadgeName(badgeConfig.getBadgeName());
    useBadgeEntity.setType(badgeConfig.getType());
    useBadgeEntity.setBadgeKey(badgeConfig.getBadgeKey());
    useBadgeEntity.setSelectUrl(sysBadgePictureConfig.getSelectUrl());
    useBadgeEntity.setNotSelectUrl(sysBadgePictureConfig.getNotSelectUrl());
    useBadgeEntity.setAnimationUrl(sysBadgePictureConfig.getAnimationUrl());
    useBadgeEntity.setExpireTime(expireTime.getTime());
    return useBadgeEntity;
  }


  private Timestamp getExpireTime(Integer effectiveDays, Timestamp expireTime) {
    return Optional.ofNullable(expireTime)
        .map(date -> TimestampUtils.expiredPlusDays(date, effectiveDays))
        .orElse(TimestampUtils.nowPlusDays(effectiveDays));
  }

  private BadgeBackpack buildSaveUserBadgeBackpack(Long badgeId, Long userId,
      BadgeBackpackExpireTypeEnum expireType,
      Integer effectiveDays) {
    return new BadgeBackpack()
        .setUserId(userId)
        .setBadgeId(badgeId)
        .setExpireTime(Objects.equals(expireType, BadgeBackpackExpireTypeEnum.TEMPORARY)
            ? TimestampUtils.nowPlusDays(effectiveDays)
            : TimestampUtils.now())
        .setExpireType(Objects.toString(expireType))
        .setUseProps(Boolean.TRUE);
  }


  private void isUserNormal(UserProfile userProfile) {
    ResponseAssert.notNull(UserErrorCode.USER_INFO_NOT_FOUND, userProfile);
    ResponseAssert.isTrue(UserErrorCode.PASSWORD_NOT_EMPTY,
        userProfile.checkAccountAvailable());
  }

  private void removeUserWearBadge(List<BadgeBackpack> usePropsList) {

    if (CollectionUtils.isEmpty(usePropsList) || usePropsList.size() < 5) {
      return;
    }

      removeWears(usePropsList, 8);
  }

    private void removeWears(List<BadgeBackpack> usePropsList, Integer maxDisplay) {
        Set<Long> ids = Sets.newHashSet();

        for (int i = 0; i < usePropsList.size(); i++) {

          if (i > maxDisplay) {
            ids.add(usePropsList.get(i).getId());
          }
        }
        badgeBackpackService.removeWearByIds(ids);
    }


}
