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

import com.red.circle.other.infra.common.user.WeekFriendshipCardCountUtils;
import com.red.circle.other.infra.database.mongo.entity.user.count.WeekFriendshipCardCount;
import com.red.circle.other.infra.database.mongo.service.user.count.WeekFriendshipCardCountService;
import com.red.circle.tool.core.date.DateFormatConstant;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils;
import com.red.circle.tool.core.date.ZonedDateTimeUtils;
import com.red.circle.tool.core.text.StringUtils;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.tuple.ImmutableTriple;
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 pengshigang on 2022/7/11
 */
@Service
@RequiredArgsConstructor
public class WeekFriendshipCardCountServiceImpl implements WeekFriendshipCardCountService {

  private final MongoTemplate mongoTemplate;

  @Override
  public void incrThisWeekQuantity(String sysOrigin, String cardType, Long userIdOne,
      Long userIdTwo, Long quantity) {
    if (existsThisWeek(userIdOne, userIdTwo, cardType)) {
      mongoTemplate.updateFirst(Query.query(getUniquenessCriteria(userIdOne, userIdTwo, cardType)),
          new Update().inc("quantity", quantity),
          WeekFriendshipCardCount.class);
      return;
    }
    addThisWeek(sysOrigin, cardType, userIdOne, userIdTwo, quantity);
  }

  private void addThisWeek(String sysOrigin, String cardType, Long userIdOne, Long userIdTwo,
      Long quantity) {
    mongoTemplate.save(new WeekFriendshipCardCount()
        .setId(getWeekUniqueId(userIdOne, userIdTwo, cardType))
        .setSysOrigin(sysOrigin)
        .setQuantity(quantity)
        .setGroup(getThisWeekDate())
        .setCreateTime(TimestampUtils.now())
        .setExpiredTime(TimestampUtils.nowPlusDays(15))
    );
  }

  @Override
  public List<WeekFriendshipCardCount> listThisWeekTop(String sysOrigin, Integer size) {
    return mongoTemplate
        .find(Query.query(Criteria.where("sysOrigin").is(sysOrigin)
                .and("group").is(getThisWeekDate()))
            .with(Sort.by(Sort.Order.desc("quantity")))
            .limit(size), WeekFriendshipCardCount.class);
  }

  @Override
  public List<WeekFriendshipCardCount> listLastWeekTop(String sysOrigin, Integer size) {

    return mongoTemplate.find(Query.query(Criteria.where("sysOrigin").is(sysOrigin)
            .and("group").is(getLastWeekDate()))
        .with(Sort.by(Sort.Order.desc("quantity")))
        .limit(size), WeekFriendshipCardCount.class);

  }

  @Override
  public void removeThisWeek(String cardType, Long userIdOne, Long userIdTwo) {
    mongoTemplate.remove(Query.query(getUniquenessCriteria(userIdOne, userIdTwo, cardType)
        .and("group").is(getThisWeekDate())), WeekFriendshipCardCount.class);
  }

  @Override
  public ImmutableTriple<Long, Long, String> parseUserIdPair(String id) {
    return WeekFriendshipCardCountUtils.parseUserIdPair(id);
  }

  private String getLastWeekDate() {
    return ZonedDateTimeUtils.format(ZonedDateTimeAsiaRiyadhUtils.getLastWeekMonday(),
        DateFormatConstant.yyyyMMdd);
  }

  private String getWeekUniqueId(Long userOne, Long userIdTwo, String cardType) {
    return StringUtils.sortAscNumberJoin("-", userOne, userIdTwo)
        .concat("-")
        .concat(cardType)
        .concat("-")
        .concat(getThisWeekDate());
  }

  private boolean existsThisWeek(Long userIdOne, Long userIdTwo, String cardType) {
    return mongoTemplate
        .exists(Query.query(getUniquenessCriteria(userIdOne, userIdTwo, cardType)),
            WeekFriendshipCardCount.class);
  }

  private Criteria getUniquenessCriteria(Long userIdOne, Long userIdTwo, String cardType) {
    return Criteria.where("id").is(getWeekUniqueId(userIdOne, userIdTwo, cardType));
  }

  private String getThisWeekDate() {
    return ZonedDateTimeUtils.format(ZonedDateTimeAsiaRiyadhUtils.nowWeekMonday(),
        DateFormatConstant.yyyyMMdd);
  }

}
