package com.microservices.zone.task;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.microservices.common.redis.service.RedisService;
import com.microservices.zone.member.service.IZonePointsAccountService;
import com.microservices.zone.utils.ZoneConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * 专区积分异步任务定时处理器
 * 使用 Redis 队列替代 @Async + TransactionTemplate，解决异步线程中事务和用户上下文丢失问题
 *
 * @author otto
 */
@Component
public class ZonePointsTask {
    private static final Logger logger = LoggerFactory.getLogger(ZonePointsTask.class);

    @Autowired
    private IZonePointsAccountService zonePointsAccountService;

    @Autowired
    private RedisService redisService;

    /**
     * 每秒轮询 Redis 队列，处理积分异步任务
     * 在主线程执行，事务和用户上下文均正常生效
     */
    @Scheduled(fixedDelay = 1000)
    public void processPointsAsyncQueue() {
        try {
            int count = zonePointsAccountService.processPointsAsyncQueue();
            if (count > 0) {
                logger.info("处理积分异步任务完成，共处理 {} 个任务", count);
            }
        } catch (Exception e) {
            logger.error("处理积分异步任务异常，已在 Service 层记录任务详情。error={}", e.getMessage(), e);
        }
    }

    /**
     * 每分钟处理重试队列，将失败任务重新放回主队列
     */
    @Scheduled(fixedDelay = 60000)
    public void processRetryQueue() {
        int count = 0;
        Object obj = null;
        try {
            while (true) {
                obj = redisService.rightPop(ZoneConstants.ZONE_POINTS_ASYNC_QUEUE_RETRY_KEY, 5L, TimeUnit.SECONDS);
                if (obj == null) {
                    break;
                }

                JSONObject task = JSON.parseObject(obj.toString());
                int retryCount = task.getIntValue("retryCount", 0);
                Long lastRetryTime = task.getLongValue("lastRetryTime", 0L);

                if (retryCount >= ZoneConstants.ZONE_POINTS_MAX_RETRY_COUNT) {
                    // 超过最大重试次数，落库
                    zonePointsAccountService.saveFailLog(obj.toString(), "exceeded max retry count, lastRetryTime=" + lastRetryTime);
                    logger.warn("积分异步任务超过最大重试次数，已落库保存 task={}", obj.toString());
                    continue;
                }

                // 计算退避间隔
                long delay = ZoneConstants.ZONE_POINTS_RETRY_DELAYS[retryCount];
                // 检查是否到可重试时间
                if (lastRetryTime > 0 && (System.currentTimeMillis() - lastRetryTime) < delay) {
                    // 未到重试时间，放回重试队列等下一轮
                    redisService.leftPush(ZoneConstants.ZONE_POINTS_ASYNC_QUEUE_RETRY_KEY, JSON.toJSONString(task));
                    continue;
                }

                task.put("retryCount", retryCount + 1);
                task.put("lastRetryTime", System.currentTimeMillis());

                redisService.leftPush(ZoneConstants.ZONE_POINTS_ASYNC_QUEUE_KEY,  JSON.toJSONString(task));
                count++;
            }
            if (count > 0) {
                logger.info("重试队列任务已放回主队列，共处理 {} 个任务", count);
            }
        } catch (Exception e) {
            logger.error("处理积分重试队列异常", e);
            // 处理积分重试队列本身发生异常时，尝试落库
            zonePointsAccountService.saveFailLog(obj != null ? obj.toString() : null, "process retry queue exception: " + e.getMessage());
        }
    }
}