package com.red.circle.other.app.scheduler;

import com.red.circle.component.redis.annotation.TaskCacheLock;
import com.red.circle.component.redis.service.RedisService;
import com.red.circle.framework.mybatis.constant.PageConstant;
import com.red.circle.other.infra.database.cache.service.other.DynamicCacheService;
import com.red.circle.other.infra.database.rds.entity.dynamic.DynamicComment;
import com.red.circle.other.infra.database.rds.service.dynamic.DynamicCommentService;
import com.red.circle.tool.core.collection.CollectionUtils;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

/**
 * 评论热度分数更新定时任务（增量更新）.
 * 每10分钟执行一次，优先更新有变化的评论
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class DynamicCommentHotScoreTask {

  private static final String PENDING_SET_KEY = "comment:hot_score:pending";

  private final RedisService redisService;
  private final DynamicCommentService dynamicCommentService;
  private final DynamicCacheService dynamicCacheService;

  /**
   * 每10分钟执行一次
   */
  @Scheduled(cron = "0 */10 * * * ?")
  @TaskCacheLock(key = "COMMENT_HOT_SCORE_UPDATE", expireSecond = 540)
  public void updateHotScore() {
    try {
      log.info("评论热度分数更新任务 - 开始执行");
      long startTime = System.currentTimeMillis();

      int incrementalUpdated = 0;
      int fallbackUpdated = 0;

      // 1. 优先更新有变化的评论（增量更新）
      Set<Long> pendingIds = getPendingCommentIds();
      if (CollectionUtils.isNotEmpty(pendingIds)) {
        incrementalUpdated = updatePendingComments(pendingIds);
      }

      // 2. 兜底更新最近24小时的评论（每小时整点执行）
      int currentMinute = LocalDateTime.now().getMinute();
      if (currentMinute == 0) {
        fallbackUpdated = updateRecentComments(pendingIds);
      }

      long costTime = System.currentTimeMillis() - startTime;
      if (fallbackUpdated > 0) {
        log.info("评论热度分数更新任务 - 执行完成，增量更新{}条，兜底更新{}条，总计{}条，耗时{}ms",
            incrementalUpdated, fallbackUpdated, incrementalUpdated + fallbackUpdated, costTime);
      } else {
        log.info("评论热度分数更新任务 - 执行完成，增量更新{}条，耗时{}ms",
            incrementalUpdated, costTime);
      }

    } catch (Exception e) {
      log.error("评论热度分数更新任务 - 执行失败", e);
    }
  }

  /**
   * 获取待更新的评论ID集合
   */
  private Set<Long> getPendingCommentIds() {
    try {
      Set<String> pendingStrSet = redisService.setGet(PENDING_SET_KEY).stream().map(String::valueOf).collect(Collectors.toSet());
      if (CollectionUtils.isEmpty(pendingStrSet)) {
        return new HashSet<>();
      }

      Set<Long> pendingIds = new HashSet<>();
      for (String idStr : pendingStrSet) {
        try {
          pendingIds.add(Long.parseLong(idStr));
        } catch (NumberFormatException e) {
          log.warn("无效的评论ID: {}", idStr);
        }
      }
      return pendingIds;
    } catch (Exception e) {
      log.error("获取待更新评论ID集合失败", e);
      return new HashSet<>();
    }
  }

  /**
   * 更新待处理的评论
   */
  private int updatePendingComments(Set<Long> pendingIds) {
    int updated = 0;
    List<String> successIds = new ArrayList<>();

    for (Long commentId : pendingIds) {
      try {
        DynamicComment comment = dynamicCommentService.getById(commentId);
        if (comment == null) {
          // 评论已被删除，从待处理集合移除
          successIds.add(commentId.toString());
          continue;
        }

        // 只更新根评论
        if (comment.getRootCommentId() == null) {
          updateCommentHotScore(comment);
          updated++;
        }

        // 更新成功，从待处理集合移除
        successIds.add(commentId.toString());

      } catch (Exception e) {
        log.error("更新评论热度失败，commentId={}", commentId, e);
        // 失败的不移除，下次继续尝试
      }
    }

    // 批量移除已处理的ID
    if (CollectionUtils.isNotEmpty(successIds)) {
      try {
        redisService.setRemove(PENDING_SET_KEY, successIds.toArray(new String[0]));
      } catch (Exception e) {
        log.error("移除已处理的评论ID失败", e);
      }
    }

    return updated;
  }

  /**
   * 兜底更新最近24小时的评论（应对时间衰减）
   */
  private int updateRecentComments(Set<Long> excludeIds) {
    int updated = 0;
    
    try {
      long oneDayAgo = System.currentTimeMillis() - (24 * 60 * 60 * 1000L);
      Long lastId = null;
      boolean hasMore = true;
      int batchSize = 500;

      while (hasMore && updated < 2000) { // 限制最多2000条，避免执行时间过长
        List<DynamicComment> comments = dynamicCommentService.query()
            .isNull(DynamicComment::getRootCommentId)
            .ge(DynamicComment::getCreateTime, new java.sql.Timestamp(oneDayAgo))
            .gt(lastId != null, DynamicComment::getId, lastId)
            .orderByAsc(DynamicComment::getId)
            .last(PageConstant.formatLimit(batchSize))
            .list();

        if (CollectionUtils.isEmpty(comments)) {
          hasMore = false;
          break;
        }

        for (DynamicComment comment : comments) {
          // 跳过已经在增量更新中处理过的
          if (excludeIds != null && excludeIds.contains(comment.getId())) {
            continue;
          }

          updateCommentHotScore(comment);
          updated++;
        }

        lastId = comments.get(comments.size() - 1).getId();
        hasMore = comments.size() >= batchSize;
      }

    } catch (Exception e) {
      log.error("兜底更新最近评论失败", e);
    }

    return updated;
  }

  /**
   * 更新单条根评论的热度分数
   */
  private void updateCommentHotScore(DynamicComment rootComment) {
    try {
      // 1. 获取根评论自身的点赞数
      Long rootLikeCount = Optional.ofNullable(
          dynamicCacheService.getCommentLikeQuantity(rootComment.getDynamicContentId(), rootComment.getId())
      ).orElse(0L);

      // 2. 查询该根评论下的所有子评论
      List<DynamicComment> childComments = dynamicCommentService.query()
          .eq(DynamicComment::getRootCommentId, rootComment.getId())
          .list();

      // 3. 汇总所有子评论的点赞数
      long totalChildLikes = 0L;
      if (CollectionUtils.isNotEmpty(childComments)) {
        for (DynamicComment childComment : childComments) {
          Long childLikeCount = Optional.ofNullable(
              dynamicCacheService.getCommentLikeQuantity(childComment.getDynamicContentId(), childComment.getId())
          ).orElse(0L);
          totalChildLikes += childLikeCount;
        }
      }

      // 4. 总点赞数 = 根评论点赞数 + 所有子评论点赞数
      long totalLikeCount = rootLikeCount + totalChildLikes;

      // 5. 计算热度分数
      double hotScore = calculateHotScore(rootComment, totalLikeCount);

      // 6. 更新到数据库
      dynamicCommentService.update()
          .set(DynamicComment::getHotScore, hotScore)
          .eq(DynamicComment::getId, rootComment.getId())
          .last(PageConstant.LIMIT_ONE)
          .execute();

      if (totalChildLikes > 0) {
        log.debug("更新评论热度 - 根评论ID:{}, 根评论点赞:{}, 子评论点赞:{}, 总点赞:{}, 热度分数:{}",
            rootComment.getId(), rootLikeCount, totalChildLikes, totalLikeCount, hotScore);
      }

    } catch (Exception e) {
      log.error("更新评论热度分数失败，rootCommentId={}", rootComment.getId(), e);
    }
  }

  /**
   * 计算热度分数
   * 公式: 总点赞数 / (时间差小时数 + 2)^1.5
   */
  private double calculateHotScore(DynamicComment comment, Long totalLikeCount) {
    long currentTime = System.currentTimeMillis();
    long ageMillis = currentTime - comment.getCreateTime().getTime();
    double ageHours = Math.max(ageMillis / (1000.0 * 60 * 60), 0.1);

    return totalLikeCount / Math.pow(ageHours + 2, 1.5);
  }

}
