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

import com.red.circle.framework.dto.PageResult;
import com.red.circle.other.infra.database.mongo.entity.activity.WeekStarGiftCount;
import com.red.circle.other.infra.database.mongo.service.activity.WeekStarGiftCountService;
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 java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
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 2021/8/13
 */
@Service
@RequiredArgsConstructor
public class WeekStarGiftCountServiceImpl implements WeekStarGiftCountService {

  private final MongoTemplate mongoTemplate;

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

  private boolean existsThisWeek(Long userId, Long giftId) {
    return mongoTemplate
        .exists(Query.query(getUniquenessCriteria(userId, giftId)),
            WeekStarGiftCount.class);
  }

  private void addThisWeek(String sysOrigin, Long userId, Long giftId, Long quantity) {
    mongoTemplate.save(new WeekStarGiftCount()
        .setId(getWeekUniqueId(userId, giftId))
        .setSysOrigin(sysOrigin)
        .setUserId(userId)
        .setGiftId(giftId)
        .setQuantity(quantity)
        .setGroup(getThisWeekDate())
        .setCreateTime(TimestampUtils.now())
        .setExpiredTime(TimestampUtils.nowPlusDays(15))
    );
  }

  @Override
  public WeekStarGiftCount getMyThisWeekRank(String sysOrigin, Long userId) {
    return getMyRankForWeek(sysOrigin, userId, getThisWeekDate());
  }

  @Override
  public WeekStarGiftCount getMyLastWeekRank(String sysOrigin, Long userId) {
    return getMyRankForWeek(sysOrigin, userId, getLastWeekDate());
  }

  @Override
  public List<WeekStarGiftCount> getHistoryTop1(String sysOrigin, Integer limit) {
    // 获取所有历史周数据，按周分组，每周只取TOP1
    Aggregation aggregation = Aggregation.newAggregation(
        // 筛选指定系统
        Aggregation.match(Criteria.where("sysOrigin").is(sysOrigin)),
        // 按group(周)和userId分组，汇总数量
        Aggregation.group("group", "userId")
            .first("sysOrigin").as("sysOrigin")
            .first("userId").as("userId")
            .first("group").as("group")
            .first("createTime").as("createTime")
            .first("expiredTime").as("expiredTime")
            .sum("quantity").as("quantity"),
        // 按group和quantity排序
        Aggregation.sort(Sort.by(
            Sort.Order.desc("group"),
            Sort.Order.desc("quantity")
        )),
        // 按group分组，只取每组的第一条（TOP1）
        Aggregation.group("group")
            .first("sysOrigin").as("sysOrigin")
            .first("userId").as("userId")
            .first("group").as("group")
            .first("createTime").as("createTime")
            .first("expiredTime").as("expiredTime")
            .first("quantity").as("quantity"),
        // 按group倒序排列（最新的周在前）
        Aggregation.sort(Sort.by(Sort.Order.desc("group"))),
        // 限制返回数量
        Aggregation.limit(limit != null ? limit : 20)
    );
    
    AggregationResults<WeekStarGiftCount> results = mongoTemplate
        .aggregate(aggregation, WeekStarGiftCount.class, WeekStarGiftCount.class);
    
    List<WeekStarGiftCount> resultList = results.getMappedResults();
    
    // 添加排名字段（所有TOP1的rank都是1）
    resultList.forEach(item -> item.setRank(1));
    
    return resultList;
  }

  /**
   * 获取用户在指定周的排名.
   */
  private WeekStarGiftCount getMyRankForWeek(String sysOrigin, Long userId, String weekDate) {
    // 先获取所有用户的聚合数据（按quantity倒序）
    Aggregation aggregation = Aggregation.newAggregation(
        Aggregation.match(Criteria.where("sysOrigin").is(sysOrigin)
            .and("group").is(weekDate)),
        Aggregation.group("userId")
            .first("sysOrigin").as("sysOrigin")
            .first("userId").as("userId")
            .first("group").as("group")
            .first("createTime").as("createTime")
            .first("expiredTime").as("expiredTime")
            .sum("quantity").as("quantity"),
        Aggregation.sort(Sort.by(Sort.Order.desc("quantity")))
    );
    
    AggregationResults<WeekStarGiftCount> results = mongoTemplate
        .aggregate(aggregation, WeekStarGiftCount.class, WeekStarGiftCount.class);
    
    List<WeekStarGiftCount> resultList = results.getMappedResults();
    
    // 查找指定用户并设置排名
    for (int i = 0; i < resultList.size(); i++) {
      WeekStarGiftCount item = resultList.get(i);
      if (userId.equals(item.getUserId())) {
        item.setRank(i + 1); // 排名从1开始
        return item;
      }
    }
    
    // 如果没有找到该用户的记录，返回null或默认值
    return null;
  }

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

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

  @Override
  public List<WeekStarGiftCount> listThisWeekTopV2(String sysOrigin, Integer size) {
    // 使用聚合查询，按userId分组，求和quantity，然后排序并添加rank字段
    Aggregation aggregation = Aggregation.newAggregation(
        Aggregation.match(Criteria.where("sysOrigin").is(sysOrigin)
            .and("group").is(getThisWeekDate())),
        Aggregation.group("userId")
            .first("sysOrigin").as("sysOrigin")
            .first("userId").as("userId")
            .first("group").as("group")
            .first("createTime").as("createTime")
            .first("expiredTime").as("expiredTime")
            .sum("quantity").as("quantity"),
        Aggregation.sort(Sort.by(Sort.Order.desc("quantity"))),
        Aggregation.limit(size)
    );
    
    AggregationResults<WeekStarGiftCount> results = mongoTemplate
        .aggregate(aggregation, WeekStarGiftCount.class, WeekStarGiftCount.class);
    
    List<WeekStarGiftCount> resultList = results.getMappedResults();
    
    // 添加rank字段
    AtomicInteger rank = new AtomicInteger(1);
    resultList.forEach(item -> item.setRank(rank.getAndIncrement()));
    
    return resultList;
  }

  @Override
  public List<WeekStarGiftCount> listLastWeekTopV2(String sysOrigin, Integer size) {
    // 使用聚合查询，按userId分组，求和quantity，然后排序并添加rank字段
    Aggregation aggregation = Aggregation.newAggregation(
        Aggregation.match(Criteria.where("sysOrigin").is(sysOrigin)
            .and("group").is(getLastWeekDate())),
        Aggregation.group("userId")
            .first("sysOrigin").as("sysOrigin")
            .first("userId").as("userId")
            .first("group").as("group")
            .first("createTime").as("createTime")
            .first("expiredTime").as("expiredTime")
            .sum("quantity").as("quantity"),
        Aggregation.sort(Sort.by(Sort.Order.desc("quantity"))),
        Aggregation.limit(size)
    );
    
    AggregationResults<WeekStarGiftCount> results = mongoTemplate
        .aggregate(aggregation, WeekStarGiftCount.class, WeekStarGiftCount.class);
    
    List<WeekStarGiftCount> resultList = results.getMappedResults();
    
    // 添加rank字段
    AtomicInteger rank = new AtomicInteger(1);
    resultList.forEach(item -> item.setRank(rank.getAndIncrement()));
    
    return resultList;
  }

  @Override
  public PageResult<WeekStarGiftCount> listThisWeekTopV2(String sysOrigin, Pageable pageable) {
    // 先获取总数量
    long totalCount = getTotalUserCount(sysOrigin, getThisWeekDate());
    
    // 使用聚合查询进行分页
    Aggregation aggregation = Aggregation.newAggregation(
        Aggregation.match(Criteria.where("sysOrigin").is(sysOrigin)
            .and("group").is(getThisWeekDate())),
        Aggregation.group("userId")
            .first("sysOrigin").as("sysOrigin")
            .first("userId").as("userId")
            .first("group").as("group")
            .first("createTime").as("createTime")
            .first("expiredTime").as("expiredTime")
            .sum("quantity").as("quantity"),
        Aggregation.sort(Sort.by(Sort.Order.desc("quantity"))),
        Aggregation.skip(pageable.getOffset()),
        Aggregation.limit(pageable.getPageSize())
    );
    
    AggregationResults<WeekStarGiftCount> results = mongoTemplate
        .aggregate(aggregation, WeekStarGiftCount.class, WeekStarGiftCount.class);
    
    List<WeekStarGiftCount> resultList = results.getMappedResults();
    
    // 添加rank字段（考虑分页的偏移量）
    AtomicInteger rank = new AtomicInteger((int) pageable.getOffset() + 1);
    resultList.forEach(item -> item.setRank(rank.getAndIncrement()));

    PageResult<WeekStarGiftCount> result = new PageResult<>();
    result.setTotal(totalCount);
    result.setRecords(resultList);
    result.setSize(pageable.getPageSize());
    return result;
  }

  @Override
  public PageResult<WeekStarGiftCount> listLastWeekTopV2(String sysOrigin, Pageable pageable) {
    // 先获取总数量
    long totalCount = getTotalUserCount(sysOrigin, getLastWeekDate());
    
    // 使用聚合查询进行分页
    Aggregation aggregation = Aggregation.newAggregation(
        Aggregation.match(Criteria.where("sysOrigin").is(sysOrigin)
            .and("group").is(getLastWeekDate())),
        Aggregation.group("userId")
            .first("sysOrigin").as("sysOrigin")
            .first("userId").as("userId")
            .first("group").as("group")
            .first("createTime").as("createTime")
            .first("expiredTime").as("expiredTime")
            .sum("quantity").as("quantity"),
        Aggregation.sort(Sort.by(Sort.Order.desc("quantity"))),
        Aggregation.skip(pageable.getOffset()),
        Aggregation.limit(pageable.getPageSize())
    );
    
    AggregationResults<WeekStarGiftCount> results = mongoTemplate
        .aggregate(aggregation, WeekStarGiftCount.class, WeekStarGiftCount.class);
    
    List<WeekStarGiftCount> resultList = results.getMappedResults();
    
    // 添加rank字段（考虑分页的偏移量）
    AtomicInteger rank = new AtomicInteger((int) pageable.getOffset() + 1);
    resultList.forEach(item -> item.setRank(rank.getAndIncrement()));

    PageResult<WeekStarGiftCount> result = new PageResult<>();
    result.setTotal(totalCount);
    result.setRecords(resultList);
    result.setSize(pageable.getPageSize());
    return result;
  }

  @Override
  public void correctionData(Long userId, Long quantity, Long giftId) {

    if (Objects.isNull(userId) || Objects.isNull(giftId) || Objects.isNull(quantity) ||
        quantity <= 0) {
      return;
    }

    WeekStarGiftCount count = mongoTemplate
        .findOne(Query.query(Criteria.where("userId").is(userId)
            .and("group").is(getThisWeekDate())
            .and("giftId").is(giftId)), WeekStarGiftCount.class);

    if (Objects.isNull(count)) {
      return;
    }
    mongoTemplate.updateFirst(Query.query(Criteria.where("id").is(count.getId())),
        new Update()
            .set("quantity", count.getQuantity() > quantity ? count.getQuantity() - quantity : 0L),
        WeekStarGiftCount.class);
  }

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

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

  private String getWeekUniqueId(Long userId, Long giftId) {
    return String.valueOf(userId)
        .concat("_")
        .concat(String.valueOf(giftId))
        .concat("_")
        .concat(getThisWeekDate());
  }

  private Criteria getUniquenessCriteria(Long userId, Long giftId) {
    return Criteria.where("userId").is(userId)
        .and("giftId").is(giftId)
        .and("group").is(getThisWeekDate());
  }

  /**
   * 获取指定时间组的用户总数（用于分页）.
   */
  private long getTotalUserCount(String sysOrigin, String groupDate) {
    Aggregation countAggregation = Aggregation.newAggregation(
        Aggregation.match(Criteria.where("sysOrigin").is(sysOrigin)
            .and("group").is(groupDate)),
        Aggregation.group("userId"),
        Aggregation.count().as("count")
    );
    
    AggregationResults<CountResult> countResults = mongoTemplate
        .aggregate(countAggregation, WeekStarGiftCount.class, CountResult.class);
    
    return countResults.getMappedResults().stream()
        .mapToLong(CountResult::getCount)
        .sum();
  }

  /**
   * 用于接收计数结果的内部类.
   */
  private static class CountResult {
    private long count;

    public long getCount() {
      return count;
    }

    public void setCount(long count) {
      this.count = count;
    }
  }

}
