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

import com.mongodb.client.model.Filters;
import com.red.circle.component.mongodb.config.MongoPrimaryService;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.domain.model.user.UserProfile;
import com.red.circle.other.infra.database.cache.service.user.UserCacheService;
import com.red.circle.other.infra.database.mongo.entity.activity.WeekKingQueenCount;
import com.red.circle.other.infra.database.mongo.service.activity.WeekKingQueenCountService;
import com.red.circle.other.inner.enums.activity.KingQueenType;
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.time.DayOfWeek;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

import lombok.RequiredArgsConstructor;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.domain.*;
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.aggregation.MatchOperation;
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/12/29
 */
@Service
@RequiredArgsConstructor
public class WeekKingQueenCountServiceImpl implements WeekKingQueenCountService {

  private final MongoTemplate mongoTemplate;
  private final MongoPrimaryService mongoPrimaryService;
  private final UserProfileGateway userProfileGateway;

  @Override
  public WeekKingQueenCount getUserThisWeekCount(String sysOrigin, KingQueenType type,
      Long userId) {
    return mongoTemplate
        .findOne(Query.query(getUniquenessCriteria(userId, type)
                .and("sysOrigin").is(sysOrigin))
            , WeekKingQueenCount.class);
  }

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

  @Override
  public WeekKingQueenCount getUserThisWeekCountWithRank(String sysOrigin, KingQueenType type, Long userId) {
    // 先获取用户的本周数据
    WeekKingQueenCount userCount = getUserThisWeekCount(sysOrigin, type, userId);
    if (userCount == null) {
      return null;
    }
    
    // 构建查询条件（根据业务规则过滤性别）
    Criteria criteria = Criteria.where("sysOrigin").is(sysOrigin)
        .and("group").is(getThisWeekDate())
        .and("type").is(type);
    
    // 根据类型过滤性别
    if (type == KingQueenType.CHARM) {
      // 魅力类型：只显示女性（0）
      criteria.and("userSex").is(0);
    } else if (type == KingQueenType.WEALTH) {
      // 财富类型：只显示男性（1）
      criteria.and("userSex").is(1);
    }
    
    // 统计比用户数量更高的人数（即排名）
    long rank = mongoTemplate.count(
        Query.query(criteria.and("quantity").gt(userCount.getQuantity())),
        WeekKingQueenCount.class
    ) + 1;
    
    // 设置排名并返回
    userCount.setRank((int) rank);
    return userCount;
  }

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

    // 给结果赋值 rank
    for (int i = 0; i < results.size(); i++) {
      results.get(i).setRank(i + 1);  // rank 从 1 开始
    }

    return results;
  }

  @Override
  public List<WeekKingQueenCount> listLastWeekTop(String sysOrigin, KingQueenType type,
      Integer size) {
    List<WeekKingQueenCount> results = mongoTemplate
        .find(Query.query(Criteria.where("sysOrigin").is(sysOrigin)
                .and("group").is(getLastWeekDate())
                .and("type").is(type))
            .with(Sort.by(Sort.Order.desc("quantity")))
            .limit(size), WeekKingQueenCount.class);

    // 给结果赋值 rank
    for (int i = 0; i < results.size(); i++) {
      results.get(i).setRank(i + 1);  // rank 从 1 开始
    }
    return results;
  }

  @Override
  public Page<WeekKingQueenCount> pageWeekTop(String sysOrigin, KingQueenType type,
                                              Integer weekType, Pageable pageable) {
    // 根据weekType确定查询的周期
    String weekGroup = getWeekDateByType(weekType);

    // 构建查询条件
    Query query = Query.query(Criteria.where("sysOrigin").is(sysOrigin)
            .and("group").is(weekGroup)
            .and("type").is(type))
        .with(Sort.by(Sort.Order.desc("quantity")));

    // 查询总数
    long total = mongoTemplate.count(query, WeekKingQueenCount.class);

    // 分页查询
    query.with(pageable);
    List<WeekKingQueenCount> results = mongoTemplate.find(query, WeekKingQueenCount.class);

    // 计算rank（考虑分页偏移量）
    int startRank = (int) (pageable.getOffset() + 1);
    for (int i = 0; i < results.size(); i++) {
      results.get(i).setRank(startRank + i);
    }

    return new PageImpl<>(results, pageable, total);
  }

  @Override
  public Page<WeekKingQueenCount> pageWeekQuery(String sysOrigin, KingQueenType type, String group, Long current, Long size) {
    return pageWeekQuery(sysOrigin, type, group, null, current, size);
  }

  @Override
  public Page<WeekKingQueenCount> pageWeekQuery(String sysOrigin, KingQueenType type, String group, 
                                               Integer userSex, Long current, Long size) {
    // 构建查询条件
    Criteria criteria = Criteria.where("sysOrigin").is(sysOrigin)
            .and("group").is(group)
            .and("type").is(type);
    
    // 根据业务规则过滤性别：魅力（女）、财富（男）
    if (userSex != null) {
      criteria.and("userSex").is(userSex);
    } else {
      // 如果没有指定性别，根据类型默认过滤
      if (type == KingQueenType.CHARM) {
        // 魅力类型：只显示女性（0）
        criteria.and("userSex").is(0);
      } else if (type == KingQueenType.WEALTH) {
        // 财富类型：只显示男性（1）
        criteria.and("userSex").is(1);
      }
    }
    
    Query query = Query.query(criteria)
        .with(Sort.by(Sort.Order.desc("quantity")));

    // 查询总数
    long total = mongoTemplate.count(query, WeekKingQueenCount.class);

    PageRequest pageRequest = PageRequest.of(current.intValue() - 1, size.intValue());
    // 分页查询
    query.with(pageRequest);
    List<WeekKingQueenCount> results = mongoTemplate.find(query, WeekKingQueenCount.class);

    // 计算rank（考虑分页偏移量）
    int startRank = (int) (pageRequest.getOffset() + 1);
    for (int i = 0; i < results.size(); i++) {
      results.get(i).setRank(startRank + i);
    }

    return new PageImpl<>(results, pageRequest, total);
  }

  public List<WeekKingQueenCount> listLastThreeMonthsTop1(String sysOrigin) {
    // 获取近三个月的分组列表，比如 20250601 ~ 20250901
    List<String> lastThreeMonthGroups = getLastThreeMonthGroups(); // 自己实现返回 List<String>，格式如 "20250818"

    List<WeekKingQueenCount> top1List = new ArrayList<>();

    for (KingQueenType type : Arrays.asList(KingQueenType.WEALTH, KingQueenType.CHARM)) {
      Criteria criteria = Criteria.where("sysOrigin").is(sysOrigin)
          .and("type").is(type)
          .and("group").in(lastThreeMonthGroups);

      // 如果是财富榜，增加 userSex=1 查询条件
      if (type == KingQueenType.WEALTH) {
        criteria.and("userSex").is(1);
      }
      if (type == KingQueenType.CHARM) {
        criteria.and("userSex").is(0);
      }

      // 聚合查询每周 Top1
      Aggregation aggregation = Aggregation.newAggregation(
          Aggregation.match(criteria),
          Aggregation.sort(Sort.by(Sort.Order.desc("quantity"))),  // 按数量降序
          Aggregation.group("group")                                // 按周分组
              .first(Aggregation.ROOT).as("top1"),           // 每组取第一个文档
          Aggregation.replaceRoot("top1")                           // 展平
      );

      AggregationResults<WeekKingQueenCount> results = mongoTemplate.aggregate(
          aggregation, "week_king_queen_count", WeekKingQueenCount.class
      );

      top1List.addAll(results.getMappedResults());
    }

    // 给结果赋值 rank=1
    top1List.forEach(w -> w.setRank(1));

    // 日期倒排
    top1List.sort(Comparator.comparing(WeekKingQueenCount::getGroup).reversed());
    return top1List;
  }

  public List<String> getLastThreeMonthGroups() {
    List<String> groups = new ArrayList<>();
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");

    LocalDate today = LocalDate.now();
    LocalDate threeMonthsAgo = today.minusMonths(3);

    // 调整到三个月前的周一
    LocalDate current = threeMonthsAgo.with(DayOfWeek.MONDAY);

    // 循环每周的周一
    while (!current.isAfter(today)) {
      groups.add(current.format(formatter));
      current = current.plusWeeks(1);
    }

    return groups;
  }


  /**
   * 根据周类型获取对应的日期组
   * @param weekType 0-本周，1-上周
   * @return 周期标识
   */
  private String getWeekDateByType(Integer weekType) {
    if (weekType != null && weekType == 1) {
      return getLastWeekDate();
    }
    return getThisWeekDate();
  }

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

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

    WeekKingQueenCount count = mongoTemplate
        .findOne(Query.query(Criteria.where("id").is(getWeekUniqueId(userId, type))),
            WeekKingQueenCount.class);
    if (Objects.nonNull(count)) {
      mongoTemplate.updateFirst(Query.query(Criteria.where("id").is(count.getId())),
          new Update().set("quantity",
              count.getQuantity() > quantity ? count.getQuantity() - quantity : 0),
          WeekKingQueenCount.class);
    }


  }

  @Override
  public void removeByUserId(Long userId) {
    mongoTemplate.remove(Query.query(Criteria.where("userId").is(userId)),
        WeekKingQueenCount.class);
  }

  private void addThisWeek(String sysOrigin, Long userId, KingQueenType type, Long quantity) {
    try {
      UserProfile byUserId = userProfileGateway.getByUserId(userId);
      mongoTemplate.save(new WeekKingQueenCount()
          .setId(getWeekUniqueId(userId, type))
          .setSysOrigin(sysOrigin)
          .setType(type)
          .setUserId(userId)
          .setQuantity(quantity)
          .setUserSex(byUserId.getUserSex())
          .setGroup(getThisWeekDate())
          .setCreateTime(TimestampUtils.now())
          .setExpiredTime(TimestampUtils.nowPlusDays(15))
      );
    } catch (DuplicateKeyException ex) {
      // ignore
    }
  }

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

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


  private boolean existsThisWeekPrimary(Long userId, KingQueenType type) {
    return mongoPrimaryService.exists("week_king_queen_count",
        Filters.eq("_id", getWeekUniqueId(userId, type)));
  }

  private Criteria getUniquenessCriteria(Long userId, KingQueenType type) {
    return Criteria.where("id").is(getWeekUniqueId(userId, type));
  }

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

}
