package com.red.circle.other.infra.database.rds.service.badge.impl;

import com.google.common.collect.Lists;
import com.red.circle.framework.mybatis.constant.PageConstant;
import com.red.circle.framework.mybatis.service.impl.BaseServiceImpl;
import com.red.circle.other.infra.database.rds.dao.badge.BadgeBackpackDAO;
import com.red.circle.other.infra.database.rds.entity.badge.BadgeBackpack;
import com.red.circle.other.infra.database.rds.service.badge.BadgeBackpackService;
import com.red.circle.other.inner.enums.material.BadgeBackpackExpireType;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.TimestampUtils;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils;
import org.springframework.stereotype.Service;

/**
 * <p>
 * 用户所得徽章-背包 服务实现类.
 * </p>
 *
 * @author pengliang
 * @since 2021-03-12
 */
@Service
public class BadgeBackpackServiceImpl extends
    BaseServiceImpl<BadgeBackpackDAO, BadgeBackpack> implements BadgeBackpackService {

  @Override
  public void deleteBadge(Collection<Long> userIds, Collection<Long> badgeIds) {

    if (CollectionUtils.isEmpty(userIds) || CollectionUtils.isEmpty(badgeIds)) {
      return;
    }

    delete()
        .in(BadgeBackpack::getUserId, userIds)
        .in(BadgeBackpack::getBadgeId, badgeIds)
        .last(PageConstant.formatLimit(userIds.size() * badgeIds.size()))
        .execute();
  }

  @Override
  public void updateUseBadge(Long userId, Collection<Long> badgeIds) {
    update()
        .set(BadgeBackpack::getUseProps, Boolean.FALSE)
        .eq(BadgeBackpack::getUseProps, Boolean.TRUE)
        .eq(BadgeBackpack::getUserId, userId)
        .execute();

    if (CollectionUtils.isEmpty(badgeIds)) {
      return;
    }

    update()
        .set(BadgeBackpack::getUseProps, Boolean.TRUE)
        .eq(BadgeBackpack::getUserId, userId)
        .in(BadgeBackpack::getBadgeId, new HashSet<>(badgeIds))
        .execute();
  }

  @Override
  public void updateUseOffByUserId(Long userId) {

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

    update()
        .set(BadgeBackpack::getUseProps, Boolean.FALSE)
        .eq(BadgeBackpack::getUserId, userId)
        .execute();
  }

  @Override
  public List<BadgeBackpack> listUseBadgeByUserId(Long userId) {
    return Objects.isNull(userId)
        ? CollectionUtils.newArrayList()
        : Optional.ofNullable(query()
                .eq(BadgeBackpack::getUserId, userId)
                .eq(BadgeBackpack::getUseProps, Boolean.TRUE)
                .list())
            .map(badgeBackpacks -> badgeBackpacks.stream()
                .filter(this::filterAvailable)
                .collect(Collectors.toList()))
            .orElseGet(CollectionUtils::newArrayList);
  }

  @Override
  public List<BadgeBackpack> listByBadgeBackpack(Long userId) {
    return Optional.ofNullable(query()
            .eq(BadgeBackpack::getUserId, userId)
            .orderByDesc(BadgeBackpack::getId)
            .list())
        .map(badgeBackpacks -> badgeBackpacks.stream().filter(this::filterAvailable)
            .collect(Collectors.toList()))
        .orElseGet(CollectionUtils::newArrayList);
  }

  @Override
  public List<Long> listExistBadgeIds(Long userId, List<Long> badgeIds) {
    return Optional.ofNullable(
            query().select(BadgeBackpack::getBadgeId)
                .eq(BadgeBackpack::getUserId, userId)
                .in(BadgeBackpack::getBadgeId, badgeIds)
                .orderByDesc(BadgeBackpack::getId)
                .list()
        ).map(badgeBackpacks -> badgeBackpacks.stream().map(BadgeBackpack::getBadgeId)
            .collect(Collectors.toList()))
        .orElse(null);
  }

  @Override
  public Map<Long, BadgeBackpack> mapBadge(Long userId, Set<Long> badgeIds) {
    return Optional.ofNullable(
            query().eq(BadgeBackpack::getUserId, userId).in(BadgeBackpack::getBadgeId, badgeIds)
                    /*.gt(BadgeBackpack::getExpireTime, ZonedDateTimeAsiaRiyadhUtils.now())*/.list())
        .map(badgeBackpacks -> badgeBackpacks.stream().filter(this::filterAvailable)
            .collect(Collectors.toMap(BadgeBackpack::getBadgeId, Function.identity(), (k, v) -> v)))
        .orElse(CollectionUtils.newHashMap());
  }



  @Override
  public List<BadgeBackpack> listUseNotExpireTimeByUserIds(Set<Long> userIds) {
    if (CollectionUtils.isEmpty(userIds)) {
      return CollectionUtils.newArrayList();
    }

    return Optional.ofNullable(query()
            .in(userIds.size() > 1, BadgeBackpack::getUserId, userIds)
            .eq(Objects.equals(userIds.size(), 1), BadgeBackpack::getUserId, userIds.iterator().next())
            .eq(BadgeBackpack::getUseProps, Boolean.TRUE)
            .list())
        .map(badgeBackpacks -> badgeBackpacks.stream().filter(this::filterAvailable)
            .collect(Collectors.toList()))
        .orElseGet(CollectionUtils::newArrayList);
  }

  @Override
  public List<BadgeBackpack> listNotExpireTimeByUserIds(Set<Long> userIds) {
    if (CollectionUtils.isEmpty(userIds)) {
      return CollectionUtils.newArrayList();
    }

    return Optional.ofNullable(query()
                    .in(userIds.size() > 1, BadgeBackpack::getUserId, userIds)
                    .eq(Objects.equals(userIds.size(), 1), BadgeBackpack::getUserId, userIds.iterator().next())
                    .list())
            .map(badgeBackpacks -> badgeBackpacks.stream().filter(this::filterAvailable)
                    .collect(Collectors.toList()))
            .orElseGet(CollectionUtils::newArrayList);
  }

  @Override
  public List<BadgeBackpack> listUserNotExpireTimeByBadgeIds(Long userId, Collection<Long> badgeIds) {
    if (CollectionUtils.isEmpty(badgeIds)) {
      return CollectionUtils.newArrayList();
    }
    return Optional.ofNullable(query()
            .eq(BadgeBackpack::getUserId, userId)
            .in(BadgeBackpack::getBadgeId, badgeIds)
            .list())
        .map(badgeBackpacks -> badgeBackpacks.stream().filter(this::filterAvailable)
            .collect(Collectors.toList()))
        .orElseGet(CollectionUtils::newArrayList);
  }

  @Override
  public boolean activationPermanent(Long userId, Long badgeId) {
    deleteBadgeBackpack(userId, badgeId);
    return save(new BadgeBackpack()
        .setUserId(userId)
        .setBadgeId(badgeId)
        .setExpireType(BadgeBackpackExpireType.PERMANENT.name())
    );
  }

  @Override
  public boolean activationTemporary(Long userId, Long badgeId, Integer days) {
    deleteBadgeBackpack(userId, badgeId);
    return save(new BadgeBackpack()
        .setUserId(userId)
        .setBadgeId(badgeId)
        .setExpireType(BadgeBackpackExpireType.TEMPORARY.name())
        .setExpireTime(TimestampUtils.nowPlusDays(days))
    );
  }

  private void deleteBadgeBackpack(Long userId, Long badgeId) {
    delete()
        .eq(BadgeBackpack::getUserId, userId)
        .eq(BadgeBackpack::getBadgeId, badgeId)
        .last(PageConstant.LIMIT_ONE)
        .execute();
  }


  @Override
  public void deleteBadges(Long userId, Set<Long> badgeIds) {
    if (CollectionUtils.isEmpty(badgeIds) || Objects.isNull(userId)) {
      return;
    }
    delete().eq(BadgeBackpack::getUserId, userId)
        .in(BadgeBackpack::getBadgeId, badgeIds)
        .last(PageConstant.formatLimit(badgeIds.size()))
        .execute();
  }

  private boolean filterAvailable(BadgeBackpack badgeBackpack) {
    return Objects.equals(badgeBackpack.getExpireType(),
        BadgeBackpackExpireType.PERMANENT.name())
        || badgeBackpack.getExpireTime().after(TimestampUtils.now());
  }

  @Override
  public void deleteUserBadge(Long userId, Long badgesId) {
    if (Objects.isNull(badgesId) || Objects.isNull(userId)) {
      return;
    }
    delete().eq(BadgeBackpack::getBadgeId, badgesId).eq(BadgeBackpack::getUserId, userId).execute();
  }


  @Override
  public void updateOffByIds(Set<Long> ids) {
    if (CollectionUtils.isEmpty(ids)) {
      return;
    }
    update()
        .set(BadgeBackpack::getUseProps, Boolean.FALSE)
        .in(BadgeBackpack::getId, ids)
        .execute();
  }

  @Override
  public BadgeBackpack getByUserIdByBadgeId(Long userId, Long badgeId) {

    if (Objects.isNull(userId) || Objects.isNull(badgeId)) {
      return null;
    }

    return query()
        .eq(BadgeBackpack::getBadgeId, badgeId)
        .eq(BadgeBackpack::getUserId, userId)
        .last(PageConstant.LIMIT_ONE)
        .getOne();

  }

  @Override
  public boolean activationPermanentAndUse(Long userId, Long badgeId) {

    BadgeBackpack backpack = query()
        .eq(BadgeBackpack::getUserId, userId)
        .eq(BadgeBackpack::getBadgeId, badgeId)
        .last(PageConstant.LIMIT_ONE)
        .getOne();

    if (Objects.isNull(backpack)) {

      return save(new BadgeBackpack()
          .setUserId(userId)
          .setBadgeId(badgeId)
          .setUseProps(Boolean.TRUE)
          .setExpireType(BadgeBackpackExpireType.PERMANENT.name())
      );
    }

    backpack.setUseProps(Boolean.TRUE);
    backpack.setExpireType(BadgeBackpackExpireType.PERMANENT.name());
    return updateSelectiveById(backpack);
  }

  @Override
  public boolean activationTemporaryAndUse(Long userId, Long badgeId, Integer days) {

    BadgeBackpack backpack = query()
        .eq(BadgeBackpack::getUserId, userId)
        .eq(BadgeBackpack::getBadgeId, badgeId)
        .last(PageConstant.LIMIT_ONE)
        .getOne();

    if (Objects.isNull(backpack)) {

      return save(new BadgeBackpack()
          .setUserId(userId)
          .setBadgeId(badgeId)
          .setUseProps(Boolean.TRUE)
          .setExpireType(BadgeBackpackExpireType.TEMPORARY.name())
          .setExpireTime(TimestampUtils.nowPlusDays(days))
      );
    }

    backpack.setUseProps(Boolean.TRUE);
    backpack.setExpireType(BadgeBackpackExpireType.TEMPORARY.name());
    backpack.setExpireTime(TimestampUtils.expiredPlusDays(backpack.getExpireTime(), days));
    return updateSelectiveById(backpack);

  }

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

    if (CollectionUtils.isEmpty(badgeIds)) {
      return;
    }

    List<BadgeBackpack> badgeBackpackList = query()
        .eq(BadgeBackpack::getUserId, userId)
        .in(BadgeBackpack::getBadgeId, badgeIds)
        .last(PageConstant.formatLimit(badgeIds.size()))
        .list();

    if (CollectionUtils.isEmpty(badgeBackpackList)) {
      return;
    }

    updateBatchById(
        badgeBackpackList.stream().peek(badgeBackpack -> {
          badgeBackpack.setUseProps(Boolean.FALSE);
          badgeBackpack
              .setExpireTime(
                  TimestampUtils.dateTimeMinusDays(badgeBackpack.getExpireTime(), reduceDays));
        }).collect(Collectors.toList()));

  }


  @Override
  public void removeWearByIds(Set<Long> ids) {
    if (CollectionUtils.isEmpty(ids)) {
      return;
    }
    update()
        .set(BadgeBackpack::getUseProps, Boolean.FALSE)
        .in(BadgeBackpack::getId, ids)
        .execute();
  }


  @Override
  public List<BadgeBackpack> listBadge(Long userId) {

    List<BadgeBackpack> badgeBackpacks = query()
        .eq(BadgeBackpack::getUserId, userId)
        .orderByDesc(BadgeBackpack::getCreateTime)
        .last(PageConstant.formatLimit(500))
        .list();

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

    return badgeBackpacks.stream().filter(this::filterAvailable).collect(Collectors.toList());
  }


  @Override
  public List<BadgeBackpack> listWearByUserId(Long userId) {
    return Optional.ofNullable(query()
        .eq(BadgeBackpack::getUserId, userId)
        .eq(BadgeBackpack::getUseProps, Boolean.TRUE)
        .orderByAsc(BadgeBackpack::getCreateTime)
        .last(PageConstant.formatLimit(500))
        .list()).orElse(Lists.newArrayList());
  }


}
