package com.erp.task.retry;

import com.erp.task.config.RetryConfig;
import com.erp.task.entity.ScheduledTask;
import com.erp.task.entity.TaskExecutionLog;
import com.erp.task.repository.ScheduledTaskRepository;
import com.erp.task.repository.TaskExecutionLogRepository;
import com.erp.task.service.TaskExecutionLogService;
import com.erp.task.service.TaskExecutor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;

/**
 * 任务失败重试处理器
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class RetryHandler {

    private final TaskExecutionLogRepository logRepository;
    private final ScheduledTaskRepository taskRepository;
    private final TaskExecutionLogService logService;
    private final TaskExecutor taskExecutor;
    private final RetryConfig retryConfig;
    private final StringRedisTemplate redisTemplate;

    private final Map<Long, ScheduledFuture<?>> retryTasks = new ConcurrentHashMap<>();

    private static final String RETRY_LOCK_PREFIX = "task:retry:lock:";
    private static final String RETRY_COUNT_PREFIX = "task:retry:count:";

    /**
     * 调度重试任务
     */
    public void scheduleRetry(ScheduledTask task, Long logId, int delaySeconds) {
        try {
            // 计算实际延迟（指数退避）
            long actualDelay = calculateDelay(delaySeconds);

            log.info("调度任务重试: taskId={}, logId={}, 延迟={}ms", task.getId(), logId, actualDelay);

            // 使用Redis实现分布式锁，避免多节点重复重试
            String lockKey = RETRY_LOCK_PREFIX + task.getId();
            Boolean acquired = redisTemplate.opsForValue().setIfAbsent(lockKey, String.valueOf(logId), Duration.ofSeconds(60));

            if (Boolean.TRUE.equals(acquired)) {
                // 标记为重试中
                logService.markRetrying(logId);

                // 延迟执行
                ScheduledFuture<?> future = org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler.builder()
                        .poolSize(1)
                        .threadNamePrefix("retry-")
                        .build()
                        .schedule(() -> executeRetry(task), java.time.Instant.now().plusMillis(actualDelay));

                retryTasks.put(task.getId(), future);
            } else {
                log.info("其他节点正在处理重试，跳过: taskId={}", task.getId());
            }
        } catch (Exception e) {
            log.error("调度重试失败: taskId={}", task.getId(), e);
        }
    }

    /**
     * 执行重试
     */
    private void executeRetry(ScheduledTask task) {
        try {
            // 重新从数据库获取最新任务信息
            ScheduledTask latestTask = taskRepository.findById(task.getId()).orElse(null);
            if (latestTask == null || latestTask.getStatus() != com.erp.task.enums.TaskStatus.RUNNING) {
                log.info("任务状态已变化，跳过重试: taskId={}", task.getId());
                return;
            }

            log.info("开始执行任务重试: taskId={}", task.getId());

            // 清理锁
            String lockKey = RETRY_LOCK_PREFIX + task.getId();
            redisTemplate.delete(lockKey);

            // 执行任务
            taskExecutor.execute(latestTask);

        } catch (Exception e) {
            log.error("重试执行失败: taskId={}", task.getId(), e);
        } finally {
            retryTasks.remove(task.getId());
        }
    }

    /**
     * 计算延迟时间（指数退避）
     */
    private long calculateDelay(int baseDelaySeconds) {
        if (!retryConfig.isExponentialBackoff()) {
            return baseDelaySeconds * 1000L;
        }

        // 从Redis获取当前重试次数
        // 简化处理，使用内存计数
        return (long) (baseDelaySeconds * 1000 * Math.pow(retryConfig.getMultiplier(), 0));
    }

    /**
     * 取消重试
     */
    public void cancelRetry(Long taskId) {
        ScheduledFuture<?> future = retryTasks.remove(taskId);
        if (future != null) {
            future.cancel(false);
            log.info("取消重试: taskId={}", taskId);
        }

        // 清理锁
        String lockKey = RETRY_LOCK_PREFIX + taskId;
        redisTemplate.delete(lockKey);
    }

    /**
     * 清理过期锁（定时任务）
     */
    @Scheduled(fixedRate = 60000)
    public void cleanupExpiredLocks() {
        try {
            // 清理超过10分钟的重试锁
            // 这是一个简化实现，实际可以使用Redis的SCAN命令
            log.debug("清理过期重试锁...");
        } catch (Exception e) {
            log.error("清理重试锁失败", e);
        }
    }

    /**
     * 定时检查需要重试的任务（处理系统重启后丢失的重试任务）
     */
    @Scheduled(fixedRate = 300000) // 5分钟
    public void checkPendingRetries() {
        try {
            List<TaskExecutionLog> retryableLogs = logRepository.findRetryableFailures(retryConfig.getMaxRetries());

            for (TaskExecutionLog logEntry : retryableLogs) {
                ScheduledTask task = taskRepository.findById(logEntry.getTaskId()).orElse(null);
                if (task != null && task.getStatus() == com.erp.task.enums.TaskStatus.RUNNING) {
                    log.info("发现待重试任务: taskId={}, logId={}", task.getId(), logEntry.getId());
                    scheduleRetry(task, logEntry.getId(), task.getRetryInterval());
                }
            }
        } catch (Exception e) {
            log.error("检查待重试任务失败", e);
        }
    }
}
