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

import com.google.common.collect.Maps;
import com.red.circle.framework.mybatis.constant.PageConstant;
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.other.infra.database.mongo.entity.user.profile.UserSpecialId;
import com.red.circle.other.infra.database.mongo.service.user.profile.UserSpecialIdService;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;

/**
 * 用户靓号 实现.
 *
 * @author pengliang on 2022/2/16
 */
@Service
@RequiredArgsConstructor
public class UserSpecialIdServiceImpl implements UserSpecialIdService {

  private final MongoTemplate mongoTemplate;

  @Override
  public void addOrUpdate(UserSpecialId userSpecialId) {
    if (StringUtils.isBlank(userSpecialId.getId())) {
      add(userSpecialId);
      return;
    }

    if (exists(userSpecialId.getSysOrigin(), userSpecialId.getAccount())) {
      updateSelective(userSpecialId);
      return;
    }

    add(userSpecialId);
  }

  @Override
  public void updateSelective(UserSpecialId userSpecialId) {
    Update update = new Update().set("updateTime", TimestampUtils.now());
    if (Objects.nonNull(userSpecialId.getUserId())) {
      update.set("userId", userSpecialId.getUserId());
    }
    if (Objects.nonNull(userSpecialId.getExpiredTime())) {
      update.set("expiredTime", userSpecialId.getExpiredTime());
    }
    if (Objects.nonNull(userSpecialId.getCustomizeField())) {
      update.set("customizeField", userSpecialId.getCustomizeField());
    }
    mongoTemplate.updateFirst(Query.query(
            getUniquenessCriteria(userSpecialId.getSysOrigin(), userSpecialId.getAccount())),
        update, UserSpecialId.class);
  }

  @Override
  public void remove(String id) {
    mongoTemplate.remove(Query.query(Criteria.where("id").is(id)), UserSpecialId.class);
  }

  @Override
  public void removeExpiredTime(String sysOrigin, String account) {
    mongoTemplate.updateFirst(Query.query(
            getUniquenessCriteria(sysOrigin, account)),
        new Update()
            .set("updateTime", TimestampUtils.now())
            .set("expiredTime", null),
        UserSpecialId.class);
  }

  @Override
  public void add(UserSpecialId userSpecialId) {
    userSpecialId.setId(
        getUniquenessId(userSpecialId.getSysOrigin(), userSpecialId.getAccount()));
    userSpecialId.setCreateTime(TimestampUtils.now());
    userSpecialId.setUpdateTime(TimestampUtils.now());
    mongoTemplate.save(userSpecialId);
  }


  @Override
  public boolean exists(String sysOrigin, String account) {
    return mongoTemplate
        .exists(Query.query(getUniquenessCriteria(sysOrigin, account)),
            UserSpecialId.class);
  }

  @Override
  public boolean existsUserId(Long userId) {
    return mongoTemplate
        .exists(Query.query(Criteria.where("userId").is(userId)),
            UserSpecialId.class);
  }

  @Override
  public UserSpecialId getById(String Id) {
    return mongoTemplate
        .findOne(Query.query(Criteria.where("id").is(Id)),
            UserSpecialId.class);
  }


  @Override
  public UserSpecialId getByUserId(Long userId) {
    return mongoTemplate
        .findOne(Query.query(Criteria.where("userId").is(userId)),
            UserSpecialId.class);
  }

  @Override
  public UserSpecialId getUserSpecialId(String sysOrigin, String account) {
    if (StringUtils.isBlank(sysOrigin) || Objects.isNull(account)) {
      return null;
    }
    return mongoTemplate
        .findOne(Query.query(Criteria.where("sysOrigin").is(sysOrigin)
                .and("account").is(account)),
            UserSpecialId.class);
  }

  @Override
  public List<UserSpecialId> listUserSpecialId(String sysOrigin, Set<String> accounts) {
    if (StringUtils.isBlank(sysOrigin) || CollectionUtils.isEmpty(accounts)) {
      return null;
    }
    return mongoTemplate
        .find(Query.query(Criteria.where("sysOrigin").is(sysOrigin)
                .and("account").in(accounts)),
            UserSpecialId.class);
  }

  @Override
  public List<UserSpecialId> listByAccount(String account) {
    if (StringUtils.isBlank(account)) {
      return null;
    }
    return mongoTemplate
        .find(Query.query(Criteria.where("account").is(account)),
            UserSpecialId.class);
  }

  @Override
  public Map<Long, UserSpecialId> mapByUserIds(Set<Long> userIds) {
    if (CollectionUtils.isEmpty(userIds)) {
      return Maps.newHashMap();
    }
    Criteria criteria = Objects.equals(userIds.size(), 1)
        ? Criteria.where("userId").is(userIds.iterator().next())
        : Criteria.where("userId").in(userIds);
    List<UserSpecialId> userSpecialIds = mongoTemplate
        .find(Query.query(criteria), UserSpecialId.class);
    if (CollectionUtils.isEmpty(userSpecialIds)) {
      return Maps.newHashMap();
    }
    return userSpecialIds.stream()
        .collect(Collectors.toMap(UserSpecialId::getUserId, Function.identity(), (k, v) -> v));
  }

  @Override
  public List<UserSpecialId> flowQuerySelective(String sysOrigin, Long userId, String account,
      Long lastId) {
    Criteria criteria = new Criteria();
    if (StringUtils.isNotBlank(sysOrigin)) {
      criteria.and("sysOrigin").is(sysOrigin);
    }
    if (Objects.nonNull(userId)) {
      criteria.and("userId").is(userId);
    }
    if (StringUtils.isNotBlank(account)) {
      criteria.and("account").is(account);
    }
    if (Objects.nonNull(lastId)) {
      criteria.and("timeId").lt(lastId);
    }
    return mongoTemplate.find(Query.query(criteria)
            .with(Sort.by(Sort.Order.desc("timeId")))
            .limit(PageConstant.DEFAULT_LIMIT_SIZE),
        UserSpecialId.class);
  }

  private Criteria getUniquenessCriteria(String sysOrigin, String account) {
    return Criteria.where("id").is(getUniquenessId(sysOrigin, account));
  }

  public String getUniquenessId(String sysOrigin, String account) {
    return sysOrigin + "_" + account;
  }
}
