package com.dora.scheduler;

import com.dora.dto.provider.ProviderTaskResult;
import com.dora.dto.provider.ProviderTaskStatus;
import com.dora.entity.AiTask;
import com.dora.mapper.AiTaskMapper;
import com.dora.service.AIProviderService;
import com.dora.service.NotificationService;
import com.dora.service.PointsService;
import com.dora.service.ResultFileTransferService;
import com.dora.service.provider.AIProvider;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;

/**
 * RunningHub任务轮询调度器
 * 
 * <p>定期轮询RunningHub的processing任务，更新状态和结果
 * 
 * @author 1818AI
 * @since 2025-10-20
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class RunningHubPollingScheduler {

    private final AiTaskMapper aiTaskMapper;
    private final AIProviderService aiProviderService;
    private final PointsService pointsService;
    private final NotificationService notificationService;
    private final ResultFileTransferService resultFileTransferService;
    private final com.dora.service.RunningHubQueueService runningHubQueueService;
    
    /**
     * RunningHub 任务超时时间（分钟）
     * 考虑到视频生成可能需要较长时间，设置为 15 分钟
     */
    @Value("${ai.providers.runninghub.task-timeout-minutes:15}")
    private int runningHubTaskTimeoutMinutes;

    /**
     * 每10秒执行一次轮询（上一次执行完成后延迟10秒再执行）
     * 使用fixedDelay而非fixedRate，防止任务堆积
     */
    @Scheduled(fixedDelayString = "${ai.providers.runninghub.polling-interval:10000}")
    public void pollRunningHubTasks() {
        try {
            // 1. 查询所有RunningHub的processing任务
            List<AiTask> tasks = aiTaskMapper.findProcessingTasksByProvider("runninghub");
            
            if (tasks.isEmpty()) {
                return;
            }
            
            log.info("RunningHub轮询 - 发现{}个待处理任务: {}", tasks.size(), 
                     tasks.stream().map(t -> String.format("%s(providerTaskId:%s)", 
                                                           t.getTaskNo(), t.getProviderTaskId()))
                          .collect(java.util.stream.Collectors.joining(", ")));
            
            // 2. 获取RunningHub Provider
            AIProvider provider = aiProviderService.getProviderByType("runninghub");
            
            // 3. 逐个轮询任务状态
            for (AiTask task : tasks) {
                pollSingleTask(task, provider);
            }
            
        } catch (Exception e) {
            log.error("RunningHub轮询调度器执行失败", e);
        }
    }

    /**
     * 轮询单个任务
     */
    private void pollSingleTask(AiTask task, AIProvider provider) {
        try {
            String taskNo = task.getTaskNo();
            String providerTaskId = task.getProviderTaskId();
            
            log.info("【轮询开始】任务: {}, providerTaskId: {}, 模型: {}, 创建时间: {}", 
                     taskNo, providerTaskId, task.getModelName(), task.getCreateTime());
            
            // 0. 检查 providerTaskId 是否为空（异常情况）
            if (providerTaskId == null || providerTaskId.trim().isEmpty()) {
                // 给予30秒宽限期，避免在任务刚提交时误判为失败
                long secondsSinceCreation = Duration.between(task.getCreateTime(), LocalDateTime.now()).getSeconds();
                if (secondsSinceCreation < 30) {
                    log.info("RunningHub任务 {} 的providerTaskId为空，但任务刚创建{}秒，继续等待", taskNo, secondsSinceCreation);
                    return;
                }
                log.error("RunningHub任务 {} 的providerTaskId为空超过30秒，这是提交失败的任务，将标记为失败", taskNo);
                handleFailure(task, "任务提交失败：未获取到RunningHub任务ID");
                return;
            }
            
            // 1. 检查RunningHub任务是否超时（基于创建时间）
            if (isTaskTimedOut(task)) {
                log.warn("RunningHub任务 {} 已超时（创建于 {}），超过 {} 分钟", 
                         taskNo, task.getCreateTime(), runningHubTaskTimeoutMinutes);
                handleFailure(task, String.format("任务处理超时（超过%d分钟），已自动取消，本次不消耗积分", 
                                                  runningHubTaskTimeoutMinutes));
                return;
            }
            
            // 2. 查询任务状态
            log.info("【查询状态】任务: {}, 即将查询RunningHub状态...", taskNo);
            ProviderTaskStatus status = provider.queryTaskStatus(providerTaskId);
            log.info("【状态结果】任务: {}, RunningHub状态: {}, 映射状态: {}", 
                     taskNo, providerTaskId, status.getStatus());
            
            // 3. 根据状态处理
            switch (status.getStatus()) {
                case SUCCESS:
                    log.info("【任务成功】任务: {}, 开始获取结果", taskNo);
                    handleSuccess(task, provider);
                    break;
                    
                case FAILED:
                    log.warn("【任务失败】任务: {}, 错误: {}", taskNo, status.getErrorMessage());
                    handleFailure(task, status.getErrorMessage());
                    break;
                    
                case QUEUED:
                case RUNNING:
                    // 继续等待，更新update_time防止被通用超时检查器误判
                    // 同时清除之前可能被误设置的错误状态（比如首次轮询时providerTaskId还未写入）
                    
                    // 如果任务之前被标记为失败（有errorMessage或completeTime），需要重新扣除积分
                    if ((task.getErrorMessage() != null && !task.getErrorMessage().trim().isEmpty()) 
                        || task.getCompleteTime() != null) {
                        log.warn("【恢复任务】任务 {} 之前被误标记为失败，现在恢复为processing状态，需要重新扣除积分", taskNo);
                        try {
                            // 重新扣除积分（之前可能已经退还了）
                            pointsService.deductPoints(
                                task.getUserId(),
                                task.getPointsFrozen(),
                                task.getTaskNo(),
                                "任务恢复处理，重新扣除积分"
                            );
                            log.info("【积分重扣】任务: {}, 用户: {}, 金额: {}", 
                                     taskNo, task.getUserId(), task.getPointsFrozen());
                        } catch (Exception e) {
                            log.error("【积分重扣失败】任务: {}, 可能用户积分不足: {}", taskNo, e.getMessage());
                            // 如果积分不足，任务应该失败
                            handleFailure(task, "恢复任务时用户积分不足");
                            return;
                        }
                    }
                    
                    task.setStatus("processing");  // 确保状态为processing
                    task.setErrorMessage(null);     // 清除错误消息
                    task.setCompleteTime(null);     // 清除完成时间
                    task.setUpdateTime(LocalDateTime.now());
                    task.setProgressMessage("任务已提交到AI服务商，正在处理中...");
                    aiTaskMapper.update(task);
                    long runningMinutes = getTaskRunningMinutes(task);
                    log.info("【处理中】任务: {}, 状态: {}, 已运行 {} 分钟", 
                             taskNo, status.getStatus(), runningMinutes);
                    break;
                    
                case UNKNOWN:
                    log.warn("【状态未知】任务: {}, 错误: {}", taskNo, status.getErrorMessage());
                    break;
            }
            
        } catch (Exception e) {
            log.error("轮询任务失败: {}", task.getTaskNo(), e);
        }
    }
    
    /**
     * 检查任务是否超时（基于创建时间）
     */
    private boolean isTaskTimedOut(AiTask task) {
        if (task.getCreateTime() == null) {
            return false;
        }
        
        long minutesElapsed = Duration.between(task.getCreateTime(), LocalDateTime.now()).toMinutes();
        return minutesElapsed > runningHubTaskTimeoutMinutes;
    }
    
    /**
     * 获取任务已运行的分钟数
     */
    private long getTaskRunningMinutes(AiTask task) {
        if (task.getCreateTime() == null) {
            return 0;
        }
        return Duration.between(task.getCreateTime(), LocalDateTime.now()).toMinutes();
    }

    /**
     * 处理任务成功
     */
    private void handleSuccess(AiTask task, AIProvider provider) {
        try {
            log.info("【成功处理开始】任务: {}, providerTaskId: {}", 
                     task.getTaskNo(), task.getProviderTaskId());
            
            // 1. 获取结果
            log.info("【获取结果】任务: {}, 调用getTaskResult...", task.getTaskNo());
            ProviderTaskResult result = provider.getTaskResult(task.getProviderTaskId());
            
            if (!result.getSuccess() || result.getFiles() == null || result.getFiles().isEmpty()) {
                log.error("【获取结果失败】任务: {}, 错误: {}", task.getTaskNo(), result.getErrorMessage());
                handleFailure(task, "获取结果失败: " + result.getErrorMessage());
                return;
            }
            
            // 2. 取第一个文件的URL作为结果
            String resultUrl = result.getFiles().get(0).getFileUrl();
            log.info("【结果文件】任务: {}, 文件数量: {}, 主文件URL: {}", 
                     task.getTaskNo(), result.getFiles().size(), resultUrl);
            
            // 3. 转存文件到COS（避免临时URL过期）
            String permanentUrl = resultFileTransferService.transferToCos(resultUrl, task.getUserId(), task.getTaskNo());
            if (!resultUrl.equals(permanentUrl)) {
                log.info("【文件转存】任务: {}, 原URL: {}, COS URL: {}", task.getTaskNo(), resultUrl, permanentUrl);
                resultUrl = permanentUrl;
            }
            
            // 4. 更新任务状态
            task.setStatus("completed");
            task.setProgress(100);
            task.setProgressMessage("任务已完成");
            task.setResultUrl(resultUrl);
            task.setCompleteTime(LocalDateTime.now());
            task.setUpdateTime(LocalDateTime.now());
            task.setPointsConsumed(task.getPointsFrozen());  // 确认消耗积分
            
            aiTaskMapper.update(task);
            
            // 4. 发送WebSocket通知
            notificationService.notifyTaskCompleted(task.getUserId(), task.getTaskNo(), resultUrl);
            
            log.info("【任务完成】任务: {}, 结果URL: {}, 消耗积分: {}", 
                     task.getTaskNo(), resultUrl, task.getPointsConsumed());
            
            // 7. 通知队列服务任务已完成，处理等待队列
            runningHubQueueService.onTaskCompleted(task.getTaskNo());
            
        } catch (Exception e) {
            log.error("【处理成功状态异常】任务: {}", task.getTaskNo(), e);
        }
    }

    /**
     * 处理任务失败
     */
    private void handleFailure(AiTask task, String errorMessage) {
        try {
            log.warn("【失败处理开始】任务: {}, 错误: {}", task.getTaskNo(), errorMessage);
            
            // 1. 更新任务状态
            task.setStatus("failed");
            task.setErrorMessage(errorMessage != null ? errorMessage : "任务执行失败");
            task.setCompleteTime(LocalDateTime.now());
            task.setUpdateTime(LocalDateTime.now());
            
            aiTaskMapper.update(task);
            log.info("【状态已更新】任务: {}, 状态: failed", task.getTaskNo());
            
            // 2. 退还积分
            if (task.getPointsFrozen() != null && task.getPointsFrozen() > 0) {
                try {
                    log.info("【退还积分】任务: {}, 积分: {}", task.getTaskNo(), task.getPointsFrozen());
                    pointsService.refundPoints(
                            task.getUserId(),
                            task.getPointsFrozen(),
                            task.getTaskNo(),
                            "任务失败，积分退回"
                    );
                    log.info("【积分已退还】任务: {}, 用户: {}, 积分: {}", 
                             task.getTaskNo(), task.getUserId(), task.getPointsFrozen());
                } catch (Exception e) {
                    log.error("【退还积分失败】任务: {}", task.getTaskNo(), e);
                }
            }
            
            // 3. 发送WebSocket通知
            notificationService.notifyTaskFailed(
                    task.getUserId(), 
                    task.getTaskNo(), 
                    task.getErrorMessage()
            );
            
            // 4. 通知队列服务任务已完成（失败也要释放轮询位），处理等待队列
            runningHubQueueService.onTaskCompleted(task.getTaskNo());
            log.info("【失败处理完成】任务: {}, 已释放队列位置", task.getTaskNo());
            
        } catch (Exception e) {
            log.error("【处理失败状态异常】任务: {}", task.getTaskNo(), e);
        }
    }
}

