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

import com.mongodb.MongoBulkWriteException;
import com.red.circle.other.infra.database.mongo.entity.gift.GiftCountAnchorDailyRunningWater;
import com.red.circle.other.infra.database.mongo.service.gift.GiftCountAnchorDailyRunningWaterService;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.LocalDateTimeUtils;
import com.red.circle.tool.core.date.LocalDateUtils;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils;
import java.time.LocalDate;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.data.mongodb.BulkOperationException;
import org.springframework.data.mongodb.core.BulkOperations;
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.data.util.Pair;
import org.springframework.stereotype.Service;

/**
 * 用户每日礼物接收流水统计 实现.
 *
 * @author pengliang on 2022/7/25
 */
@Service
@RequiredArgsConstructor
public class GiftCountAnchorDailyRunningWaterServiceImpl implements
    GiftCountAnchorDailyRunningWaterService {

  private final MongoTemplate mongoTemplate;

  @Override
  public void incr(List<GiftCountAnchorDailyRunningWater> runningWaters) {

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

    runningWaters = runningWaters.stream()
        .filter(runningWater -> Objects.nonNull(runningWater.getQuantity())
            && runningWater.getQuantity() > 0).collect(Collectors.toList());

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

    try {
      mongoTemplate
          .bulkOps(BulkOperations.BulkMode.UNORDERED, GiftCountAnchorDailyRunningWater.class)
          .upsert(runningWaters.stream().filter(Objects::nonNull)
              .map(runningWater -> Pair.of(
                  Query.query(Criteria.where("id")
                      .is(getId(runningWater.getSendUserId(), runningWater.getAcceptUserId()))),
                  new Update()
                      .set("sendUserId", runningWater.getSendUserId())
                      .set("sendAnchor", runningWater.getSendAnchor())
                      .set("acceptUserId", runningWater.getAcceptUserId())
                      .set("acceptAnchor", runningWater.getAcceptAnchor())
                      .inc("quantity", runningWater.getQuantity())
                      .set("createTimeNumber", ZonedDateTimeAsiaRiyadhUtils.nowDateToInt())
                      .set("createTime", TimestampUtils.now())
                      .set("expiredTime",
                          // 过期时间更平均
                          TimestampUtils.nowPlusDays(120 - (LocalDate.now().getDayOfMonth() - 1)))
              )).collect(Collectors.toList()))
          .execute();
    } catch (MongoBulkWriteException ex) {
      // TODO 存在精度问题
    } catch (BulkOperationException ex) {
      // ignore
    }
  }

  @Override
  public List<GiftCountAnchorDailyRunningWater> listThisMonth(Long userId) {
    return mongoTemplate.find(Query.query(
            Criteria.where("createTimeNumber")
                .gte(Integer.valueOf(LocalDateTimeUtils.nowFirstDayOfMonth("yyyyMMdd")))
                .and("sendUserId").is(userId)),
        GiftCountAnchorDailyRunningWater.class);
  }

  @Override
  public void removeByIds(List<String> ids) {

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

    mongoTemplate.remove(Query.query(Criteria.where("id").in(ids)),
        GiftCountAnchorDailyRunningWater.class);

  }

  @Override
  public List<GiftCountAnchorDailyRunningWater> listByMonthByAcceptAnchor(Long sendUserId,
      Integer startDateNumber, Integer endDateNumber) {

    return mongoTemplate.find(Query.query(
            Criteria.where("sendUserId").is(sendUserId)
                .and("acceptAnchor").is(Boolean.TRUE)
                .and("quantity").gt(0)
                .andOperator(
                    Criteria.where("createTimeNumber").gte(startDateNumber),
                    Criteria.where("createTimeNumber").lte(endDateNumber))),
        GiftCountAnchorDailyRunningWater.class);

  }

  @Override
  public void deleteByTime(Long sendUserId, Integer startDateNumber, Integer endDateNumber) {

    mongoTemplate.remove(Query.query(Criteria.where("sendUserId").is(sendUserId)
            .and("acceptAnchor").is(Boolean.TRUE)
            .and("quantity").gt(0)
            .andOperator(
                Criteria.where("createTimeNumber").gte(startDateNumber),
                Criteria.where("createTimeNumber").lte(endDateNumber))),
        GiftCountAnchorDailyRunningWater.class);

  }

  private String getId(Long sendUserId, Long acceptUserId) {
    return sendUserId + '_' + LocalDateUtils.nowToInteger() + "_" + acceptUserId;
  }

}
