package com.example.demo.service;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import com.example.demo.model.ImageToVideoTask;
import com.example.demo.repository.ImageToVideoTaskRepository;

/**
 * 图生视频服务类
 * 注意：不在类级别使用 @Transactional，因为某些方法需要禁用事务（如长时间运行的外部API调用）
 */
@Service
public class ImageToVideoService {

    private static final Logger logger = LoggerFactory.getLogger(ImageToVideoService.class);

    @Autowired
    private ImageToVideoTaskRepository taskRepository;

    @Autowired
    private RealAIService realAIService;

    @Autowired
    private TaskQueueService taskQueueService;
    
    @Autowired
    private UserWorkService userWorkService;
    
    @Autowired
    private UserService userService;
    
    @Autowired
    private CosService cosService;
    
    @Autowired
    private TaskStatusPollingService taskStatusPollingService;

    @Value("${app.upload.path:/uploads}")
    private String uploadPath;

    @Value("${app.video.output.path:/outputs}")
    private String outputPath;

    /**
     * 创建图生视频任务
     */
    @Transactional
    public ImageToVideoTask createTask(String username, MultipartFile firstFrame, 
                                     MultipartFile lastFrame, String prompt, 
                                     String aspectRatio, int duration, boolean hdMode) {
        
        try {
            // 检查用户所有类型任务的总数（统一检查）
            userWorkService.checkMaxConcurrentTasks(username);
            
            // 生成任务ID
            String taskId = generateTaskId();
            
            // 保存首帧图片
            String firstFrameUrl = saveImage(firstFrame, taskId, "first_frame");
            
            // 保存尾帧图片（如果提供）
            String lastFrameUrl = null;
            if (lastFrame != null && !lastFrame.isEmpty()) {
                lastFrameUrl = saveImage(lastFrame, taskId, "last_frame");
            }
            
            // 创建任务记录
            ImageToVideoTask task = new ImageToVideoTask(
                taskId, username, firstFrameUrl, prompt, aspectRatio, duration, hdMode
            );
            
            if (lastFrameUrl != null) {
                task.setLastFrameUrl(lastFrameUrl);
            }
            
            // 保存到数据库
            task = taskRepository.save(task);
            
            // 添加任务到队列
            taskQueueService.addImageToVideoTask(username, taskId);
            
            // 创建PROCESSING状态的UserWork，以便用户刷新页面后能恢复任务
            try {
                userWorkService.createProcessingImageToVideoWork(task);
            } catch (Exception e) {
                logger.warn("创建PROCESSING状态作品失败（不影响任务执行）: {}", taskId, e);
            }
            
            logger.info("创建图生视频任务成功: taskId={}, username={}", taskId, username);
            return task;
            
        } catch (Exception e) {
            logger.error("创建图生视频任务失败", e);
            throw new RuntimeException("创建任务失败: " + e.getMessage());
        }
    }

    /**
     * 通过图片URL创建图生视频任务（用于"做同款"功能）
     * 直接使用已有的图片URL，无需重新上传
     */
    @Transactional
    public ImageToVideoTask createTaskByUrl(String username, String imageUrl, String prompt,
                                           String aspectRatio, int duration, boolean hdMode) {
        try {
            // 检查用户所有类型任务的总数（统一检查）
            userWorkService.checkMaxConcurrentTasks(username);
            
            // 生成任务ID
            String taskId = generateTaskId();
            
            // 直接使用传入的图片URL作为首帧图片URL
            String firstFrameUrl = imageUrl;
            
            // 创建任务记录
            ImageToVideoTask task = new ImageToVideoTask(
                taskId, username, firstFrameUrl, prompt, aspectRatio, duration, hdMode
            );
            
            // 保存到数据库
            task = taskRepository.save(task);
            
            // 添加任务到队列
            taskQueueService.addImageToVideoTask(username, taskId);
            
            // 创建PROCESSING状态的UserWork，以便用户刷新页面后能恢复任务
            try {
                userWorkService.createProcessingImageToVideoWork(task);
            } catch (Exception e) {
                logger.warn("创建PROCESSING状态作品失败（不影响任务执行）: {}", taskId, e);
            }
            
            logger.info("通过URL创建图生视频任务成功: taskId={}, username={}, imageUrl={}", 
                taskId, username, imageUrl.substring(0, Math.min(50, imageUrl.length())));
            return task;
            
        } catch (Exception e) {
            logger.error("通过URL创建图生视频任务失败", e);
            throw new RuntimeException("创建任务失败: " + e.getMessage());
        }
    }

    /**
     * 获取用户任务列表
     */
    @Transactional(readOnly = true)
    public List<ImageToVideoTask> getUserTasks(String username, int page, int size) {
        // 验证参数
        if (username == null || username.trim().isEmpty()) {
            throw new IllegalArgumentException("用户名不能为空");
        }
        if (page < 0) {
            page = 0;
        }
        if (size <= 0) {
            size = 10; // 默认每页10条
        }
        // 移除size上限限制，允许获取全部历史记录
        
        Pageable pageable = PageRequest.of(page, size);
        Page<ImageToVideoTask> taskPage = taskRepository.findByUsernameOrderByCreatedAtDesc(username, pageable);
        return taskPage.getContent();
    }

    /**
     * 获取用户任务总数
     */
    @Transactional(readOnly = true)
    public long getUserTaskCount(String username) {
        if (username == null || username.trim().isEmpty()) {
            return 0;
        }
        return taskRepository.countByUsername(username);
    }

    /**
     * 根据任务ID获取任务
     */
    @Transactional(readOnly = true)
    public ImageToVideoTask getTaskById(String taskId) {
        if (taskId == null || taskId.trim().isEmpty()) {
            return null;
        }
        return taskRepository.findByTaskId(taskId).orElse(null);
    }


    /**
     * 使用真实API处理任务
     */
    @Async("taskExecutor")
    public CompletableFuture<Void> processTaskWithRealAPI(ImageToVideoTask task, MultipartFile firstFrame) {
        try {
            logger.info("开始使用真实API处理图生视频任务: {}", task.getTaskId());
            
            // 重新从数据库加载任务，确保获取最新状态
            ImageToVideoTask currentTask = taskRepository.findByTaskId(task.getTaskId())
                .orElseThrow(() -> new RuntimeException("任务不存在: " + task.getTaskId()));
            
            // 检查任务是否已经有 realTaskId，如果有，说明已经提交过了，不应该再次处理
            if (currentTask.getRealTaskId() != null && !currentTask.getRealTaskId().isEmpty()) {
                logger.warn("图生视频任务 {} 已经有 realTaskId（{}），说明已经提交过了，跳过处理", 
                    task.getTaskId(), currentTask.getRealTaskId());
                return CompletableFuture.completedFuture(null);
            }
            
            // 检查任务状态，如果已经不是PENDING，说明已经被其他线程处理了
            if (currentTask.getStatus() != ImageToVideoTask.TaskStatus.PENDING) {
                logger.warn("图生视频任务 {} 状态已不是PENDING（当前状态: {}），跳过处理，可能已被其他线程处理", 
                    task.getTaskId(), currentTask.getStatus());
                return CompletableFuture.completedFuture(null);
            }
            
            // 更新任务状态为处理中
            currentTask.updateStatus(ImageToVideoTask.TaskStatus.PROCESSING);
            taskRepository.save(currentTask);

            // 将图片转换为Base64
            String imageBase64 = realAIService.convertImageToBase64(
                firstFrame.getBytes(), 
                firstFrame.getContentType()
            );

            // 调用真实API提交任务
            Map<String, Object> apiResponse = realAIService.submitImageToVideoTask(
                currentTask.getPrompt(),
                imageBase64,
                currentTask.getAspectRatio(),
                currentTask.getDuration().toString(),
                currentTask.getHdMode()
            );

            // 从API响应中提取真实任务ID
            // 注意：根据真实API响应，任务ID可能在不同的位置
            // 这里先记录API响应，后续根据实际响应调整
            logger.info("API响应数据: {}", apiResponse);
            
            // 尝试从不同位置提取任务ID
            String realTaskId = null;
            if (apiResponse.containsKey("data")) {
                Object data = apiResponse.get("data");
                if (data instanceof Map) {
                    // 如果data是Map，尝试获取taskNo（API返回的字段名）
                    realTaskId = (String) ((Map<?, ?>) data).get("taskNo");
                    if (realTaskId == null) {
                        // 如果没有taskNo，尝试taskId（兼容性）
                        realTaskId = (String) ((Map<?, ?>) data).get("taskId");
                    }
                } else if (data instanceof List) {
                    // 如果data是List，检查第一个元素
                    List<?> dataList = (List<?>) data;
                    if (!dataList.isEmpty()) {
                        Object firstElement = dataList.get(0);
                        if (firstElement instanceof Map) {
                            Map<?, ?> firstMap = (Map<?, ?>) firstElement;
                            realTaskId = (String) firstMap.get("taskNo");
                            if (realTaskId == null) {
                                realTaskId = (String) firstMap.get("taskId");
                            }
                        }
                    }
                }
            }
            
            // 如果找到了真实任务ID，保存到数据库
            if (realTaskId != null) {
                // 重新加载任务以确保获取最新状态
                currentTask = taskRepository.findByTaskId(task.getTaskId())
                    .orElseThrow(() -> new RuntimeException("任务不存在: " + task.getTaskId()));
                currentTask.setRealTaskId(realTaskId);
                taskRepository.save(currentTask);
                logger.info("真实任务ID已保存: {} -> {}", task.getTaskId(), realTaskId);
            } else {
                // 如果没有找到任务ID，说明任务提交失败（尝试通过 task_status 触发级联）
                // 注意：积分返还由 updateTaskStatusWithCascade 自动处理
                logger.error("任务提交失败：未从API响应中获取到任务ID");
                boolean updated = taskStatusPollingService.markTaskFailed(task.getTaskId(), "任务提交失败：API未返回有效的任务ID");
                if (!updated) {
                    // 回退：直接更新业务表
                    currentTask = taskRepository.findByTaskId(task.getTaskId())
                        .orElseThrow(() -> new RuntimeException("任务不存在: " + task.getTaskId()));
                    currentTask.updateStatus(ImageToVideoTask.TaskStatus.FAILED);
                    currentTask.setErrorMessage("任务提交失败：API未返回有效的任务ID");
                    currentTask.setCompletedAt(LocalDateTime.now());
                    taskRepository.save(currentTask);
                    // 更新 UserWork
                    try {
                        userWorkService.updateWorkStatus(task.getTaskId(), 
                            com.example.demo.model.UserWork.WorkStatus.FAILED);
                    } catch (Exception e) {
                        logger.warn("回退更新UserWork状态失败: taskId={}", task.getTaskId());
                    }
                    // 回退时需要返还积分
                    returnFrozenPointsSafely(task.getTaskId());
                }
                return CompletableFuture.completedFuture(null); // 直接返回，不进行轮询
            }

            // 开始轮询真实任务状态
            pollRealTaskStatus(currentTask);
            
        } catch (Exception e) {
            logger.error("使用真实API处理图生视频任务失败: {}", task.getTaskId(), e);
            logger.error("异常详情: {}", e.getClass().getSimpleName() + ": " + e.getMessage());
            if (e.getCause() != null) {
                logger.error("异常原因: {}", e.getCause().getMessage());
            }
            
            try {
                // 尝试通过 task_status 触发级联更新
                // 注意：积分返还由 updateTaskStatusWithCascade 自动处理
                boolean updated = taskStatusPollingService.markTaskFailed(task.getTaskId(), e.getMessage());
                if (!updated) {
                    // 回退：直接更新业务表
                    ImageToVideoTask currentTask = taskRepository.findByTaskId(task.getTaskId()).orElse(null);
                    if (currentTask != null) {
                        currentTask.updateStatus(ImageToVideoTask.TaskStatus.FAILED);
                        currentTask.setErrorMessage(e.getMessage());
                        currentTask.setCompletedAt(LocalDateTime.now());
                        taskRepository.save(currentTask);
                        // 更新 UserWork
                        try {
                            userWorkService.updateWorkStatus(task.getTaskId(), 
                                com.example.demo.model.UserWork.WorkStatus.FAILED);
                        } catch (Exception ue) {
                            logger.warn("回退更新UserWork状态失败: taskId={}", task.getTaskId());
                        }
                        // 回退时需要返还积分
                        returnFrozenPointsSafely(task.getTaskId());
                    }
                }
            } catch (Exception saveException) {
                logger.error("保存失败状态时出错: {}", task.getTaskId(), saveException);
            }
        }
        
        return CompletableFuture.completedFuture(null);
    }

    /**
     * 轮询真实任务状态
     */
    private void pollRealTaskStatus(ImageToVideoTask task) {
        try {
            String realTaskId = task.getRealTaskId();
            if (realTaskId == null) {
                logger.error("真实任务ID为空，无法轮询状态: {}", task.getTaskId());
                return;
            }

            // 轮询任务状态
            int maxAttempts = 450; // 最大轮询次数（15分钟）
            int attempt = 0;
            
            while (attempt < maxAttempts) {
                // 检查任务是否已被取消
                ImageToVideoTask currentTask = taskRepository.findByTaskId(task.getTaskId()).orElse(null);
                if (currentTask != null && currentTask.getStatus() == ImageToVideoTask.TaskStatus.CANCELLED) {
                    logger.info("任务 {} 已被取消，停止轮询", task.getTaskId());
                    // 任务取消时返还冻结积分
                    returnFrozenPointsSafely(task.getTaskId());
                    return;
                }
                
                // 使用最新的任务状态
                if (currentTask != null) {
                    task = currentTask;
                }

                try {
                    // 查询真实任务状态
                    Map<String, Object> statusResponse = realAIService.getTaskStatus(realTaskId);
                    logger.info("任务状态查询响应: {}", statusResponse);
                    
                    // 处理状态响应
                    if (statusResponse != null && statusResponse.containsKey("data")) {
                        Object data = statusResponse.get("data");
                        Map<?, ?> taskData = null;
                        
                        // 处理不同的响应格式
                        if (data instanceof Map) {
                            taskData = (Map<?, ?>) data;
                        } else if (data instanceof List) {
                            List<?> dataList = (List<?>) data;
                            if (!dataList.isEmpty() && dataList.get(0) instanceof Map) {
                                taskData = (Map<?, ?>) dataList.get(0);
                            }
                        }
                        
                        if (taskData != null) {
                            String status = (String) taskData.get("status");
                            Integer progress = (Integer) taskData.get("progress");
                            String resultUrl = (String) taskData.get("resultUrl");
                            String errorMessage = (String) taskData.get("errorMessage");

                            // 更新任务状态（通过 task_status 表触发级联）
                            if ("completed".equals(status) || "success".equals(status)) {
                                // 尝试通过 task_status 触发级联更新
                                boolean updated = taskStatusPollingService.markTaskCompleted(task.getTaskId(), resultUrl);
                                if (!updated) {
                                    // 回退：直接更新业务表
                                    task.setResultUrl(resultUrl);
                                    task.updateStatus(ImageToVideoTask.TaskStatus.COMPLETED);
                                    task.updateProgress(100);
                                    task.setCompletedAt(LocalDateTime.now());
                                    taskRepository.save(task);
                                    // 更新 UserWork
                                    try {
                                        userWorkService.updateWorkOnComplete(task.getTaskId(), resultUrl, 
                                            com.example.demo.model.UserWork.WorkStatus.COMPLETED);
                                    } catch (Exception e) {
                                        logger.warn("回退更新UserWork状态失败: taskId={}", task.getTaskId());
                                    }
                                }
                                logger.info("图生视频任务完成: {}", task.getTaskId());
                                return;
                            } else if ("failed".equals(status) || "error".equals(status)) {
                                // 尝试通过 task_status 触发级联更新
                                // 注意：积分返还由 updateTaskStatusWithCascade 自动处理
                                boolean updated = taskStatusPollingService.markTaskFailed(task.getTaskId(), errorMessage);
                                if (!updated) {
                                    // 回退：直接更新业务表
                                    task.updateStatus(ImageToVideoTask.TaskStatus.FAILED);
                                    task.setErrorMessage(errorMessage);
                                    task.setCompletedAt(LocalDateTime.now());
                                    taskRepository.save(task);
                                    // 更新 UserWork
                                    try {
                                        userWorkService.updateWorkStatus(task.getTaskId(), 
                                            com.example.demo.model.UserWork.WorkStatus.FAILED);
                                    } catch (Exception e) {
                                        logger.warn("回退更新UserWork状态失败: taskId={}", task.getTaskId());
                                    }
                                    // 回退时需要返还积分
                                    returnFrozenPointsSafely(task.getTaskId());
                                }
                                logger.error("图生视频任务失败: {}", task.getTaskId());
                                return;
                            } else if ("processing".equals(status) || "pending".equals(status) || "running".equals(status)) {
                                // 更新进度
                                if (progress != null) {
                                    task.updateProgress(progress);
                                } else {
                                    // 根据轮询次数估算进度
                                    int estimatedProgress = Math.min(90, (attempt * 100) / maxAttempts);
                                    task.updateProgress(estimatedProgress);
                                }
                                taskRepository.save(task);
                            }
                        }
                    }
                    
                } catch (Exception e) {
                    logger.warn("查询任务状态失败，继续轮询: {}", e.getMessage());
                    logger.warn("异常详情: {}", e.getClass().getSimpleName() + ": " + e.getMessage());
                }

                attempt++;
                Thread.sleep(2000); // 每2秒轮询一次
            }
            
            // 超时处理（尝试通过 task_status 触发级联）
            // 注意：积分返还由 updateTaskStatusWithCascade 自动处理
            boolean updated = taskStatusPollingService.markTaskFailed(task.getTaskId(), "任务处理超时");
            if (!updated) {
                // 回退：直接更新业务表
                task.updateStatus(ImageToVideoTask.TaskStatus.FAILED);
                task.setErrorMessage("任务处理超时");
                task.setCompletedAt(LocalDateTime.now());
                taskRepository.save(task);
                // 更新 UserWork
                try {
                    userWorkService.updateWorkStatus(task.getTaskId(), 
                        com.example.demo.model.UserWork.WorkStatus.FAILED);
                } catch (Exception e) {
                    logger.warn("回退更新UserWork状态失败: taskId={}", task.getTaskId());
                }
                // 回退时需要返还积分
                returnFrozenPointsSafely(task.getTaskId());
            }
            logger.error("图生视频任务超时: {}", task.getTaskId());
            
        } catch (InterruptedException e) {
            logger.error("轮询任务状态被中断: {}", task.getTaskId(), e);
            // 线程中断时返还冻结积分
            returnFrozenPointsSafely(task.getTaskId());
            Thread.currentThread().interrupt();
        } catch (Exception e) {
            logger.error("轮询任务状态异常: {}", task.getTaskId(), e);
            logger.error("异常详情: {}", e.getClass().getSimpleName() + ": " + e.getMessage());
            if (e.getCause() != null) {
                logger.error("异常原因: {}", e.getCause().getMessage());
            }
            // 轮询异常时返还冻结积分
            returnFrozenPointsSafely(task.getTaskId());
        }
    }

    /**
     * 处理视频生成过程
     */
    private void simulateVideoGeneration(ImageToVideoTask task) throws InterruptedException {
        // 处理时间
        int totalSteps = 10;
        for (int i = 1; i <= totalSteps; i++) {
            // 检查任务是否已被取消
            ImageToVideoTask currentTask = taskRepository.findByTaskId(task.getTaskId()).orElse(null);
            if (currentTask != null && currentTask.getStatus() == ImageToVideoTask.TaskStatus.CANCELLED) {
                logger.info("任务 {} 已被取消，停止处理", task.getTaskId());
                return;
            }
            
            Thread.sleep(2000); // 处理时间
            
            // 更新进度
            int progress = (i * 100) / totalSteps;
            task.updateProgress(progress);
            taskRepository.save(task);
            
            logger.debug("任务 {} 进度: {}%", task.getTaskId(), progress);
        }
    }

    /**
     * 保存图片文件
     * 如果COS启用，会同时上传到COS并返回COS URL
     */
    private String saveImage(MultipartFile file, String taskId, String type) throws IOException {
        // 解析上传目录：如果配置的是相对路径，则相对于应用当前工作目录
        Path uploadDir = Paths.get(uploadPath == null ? "uploads" : uploadPath);
        if (!uploadDir.isAbsolute()) {
            uploadDir = Paths.get(System.getProperty("user.dir")).resolve(uploadDir).toAbsolutePath().normalize();
        }

        // 确保上传目录存在（创建所有父目录）
        if (!Files.exists(uploadDir)) {
            Files.createDirectories(uploadDir);
        }

        // 创建任务目录
        Path taskDir = uploadDir.resolve(taskId);
        if (!Files.exists(taskDir)) {
            Files.createDirectories(taskDir);
        }

        // 生成文件名
        String originalFilename = file.getOriginalFilename();
        String extension = getFileExtension(originalFilename);
        String filename = type + "_" + System.currentTimeMillis() + extension;

        // 保存文件到本地（覆盖同名文件的行为由 Files.copy 决定，此处不启用 REPLACE_EXISTING）
        Path filePath = taskDir.resolve(filename);
        try {
            Files.copy(file.getInputStream(), filePath);
        } catch (IOException e) {
            logger.error("保存上传图片失败: uploadDir={}, taskId={}, fileName={}", uploadDir, taskId, filename, e);
            throw e;
        }

        // 本地URL
        String localUrlPath = "/uploads/" + taskId + "/" + filename;
        
        // 检查COS状态并记录日志
        logger.info("检查COS状态: enabled={}, taskId={}", cosService.isEnabled(), taskId);
        
        // 如果COS启用，上传到COS并返回COS URL
        if (cosService.isEnabled()) {
            try {
                // 读取文件字节
                byte[] imageBytes = file.getBytes();
                String contentType = file.getContentType();
                if (contentType == null || contentType.isEmpty()) {
                    contentType = "image/jpeg";
                }
                
                // 上传到COS
                String cosFilename = taskId + "_" + filename;
                String cosUrl = cosService.uploadBase64Image(
                    "data:" + contentType + ";base64," + java.util.Base64.getEncoder().encodeToString(imageBytes),
                    cosFilename
                );
                
                if (cosUrl != null && !cosUrl.isEmpty()) {
                    logger.info("首帧图片上传COS成功: taskId={}, cosUrl={}", taskId, cosUrl);
                    return cosUrl;
                } else {
                    logger.warn("上传首帧图片到COS失败，使用本地URL: taskId={}", taskId);
                }
            } catch (Exception e) {
                logger.error("上传首帧图片到COS异常，使用本地URL: taskId={}", taskId, e);
            }
        }
        
        // 返回本地URL（COS未启用或上传失败时）
        return localUrlPath;
    }

    /**
     * 获取文件扩展名
     */
    private String getFileExtension(String filename) {
        if (filename == null || filename.isEmpty()) {
            return ".jpg";
        }
        
        int lastDotIndex = filename.lastIndexOf('.');
        if (lastDotIndex > 0) {
            return filename.substring(lastDotIndex);
        }
        
        return ".jpg";
    }

    /**
     * 生成任务ID
     */
    private String generateTaskId() {
        return "img2vid_" + UUID.randomUUID().toString().replace("-", "").substring(0, 16);
    }

    /**
     * 生成结果URL
     */
    private String generateResultUrl(String taskId) {
        return outputPath + "/" + taskId + "/video_" + System.currentTimeMillis() + ".mp4";
    }

    /**
     * 获取待处理任务列表
     */
    public List<ImageToVideoTask> getPendingTasks() {
        return taskRepository.findPendingTasks();
    }

    /**
     * 清理过期任务
     */
    public int cleanupExpiredTasks() {
        LocalDateTime expiredDate = LocalDateTime.now().minusDays(30);
        return taskRepository.deleteExpiredTasks(expiredDate);
    }
    
    /**
     * 检查并处理超时的图生视频任务
     * 超时时间：1小时（视频生成需要较长时间）
     * 注意：如果任务已经有resultUrl（视频已生成），即使超时也不标记为失败
     */
    @Transactional
    public int checkAndHandleTimeoutTasks() {
        try {
            // 计算超时时间点（1小时前）
            LocalDateTime timeoutTime = LocalDateTime.now().minusMinutes(60);
            
            // 查找超时的任务（状态为PROCESSING，创建时间超过1小时）
            List<ImageToVideoTask> timeoutTasks = taskRepository.findTimeoutTasks(
                ImageToVideoTask.TaskStatus.PROCESSING,
                timeoutTime
            );
            
            if (timeoutTasks.isEmpty()) {
                return 0;
            }
            
            logger.warn("发现 {} 个可能超时的图生视频任务，开始检查", timeoutTasks.size());
            
            int handledCount = 0;
            int skippedCount = 0;
            for (ImageToVideoTask task : timeoutTasks) {
                try {
                    // 检查任务是否已经有resultUrl（视频已生成）
                    // 如果有resultUrl，说明视频已经成功生成，不应该被标记为超时失败
                    if (task.getResultUrl() != null && !task.getResultUrl().isEmpty()) {
                        logger.debug("任务 {} 已有resultUrl，视频已生成，跳过超时标记", task.getTaskId());
                        skippedCount++;
                        continue;
                    }
                    
                    // 尝试通过 task_status 触发级联更新
                    // 注意：积分返还由 updateTaskStatusWithCascade 自动处理
                    boolean updated = taskStatusPollingService.markTaskFailed(task.getTaskId(), "图生视频任务超时：任务创建后超过1小时仍未完成");
                    if (!updated) {
                        // 回退：直接更新业务表
                        task.updateStatus(ImageToVideoTask.TaskStatus.FAILED);
                        task.setErrorMessage("图生视频任务超时：任务创建后超过1小时仍未完成");
                        task.setCompletedAt(LocalDateTime.now());
                        taskRepository.save(task);
                        
                        // 更新 UserWork
                        try {
                            userWorkService.updateWorkStatusByTaskId(task.getTaskId(), 
                                com.example.demo.model.UserWork.WorkStatus.FAILED);
                        } catch (Exception e) {
                            logger.warn("回退更新UserWork状态失败: taskId={}", task.getTaskId());
                        }
                        
                        // 回退时需要返还积分
                        returnFrozenPointsSafely(task.getTaskId());
                    }
                    
                    logger.warn("图生视频任务超时，已标记为失败: taskId={}", task.getTaskId());
                    
                    handledCount++;
                } catch (Exception e) {
                    logger.error("处理超时图生视频任务失败: taskId={}", task.getTaskId(), e);
                }
            }
            
            if (handledCount > 0 || skippedCount > 0) {
                logger.info("处理超时图生视频任务完成，失败: {}/{}，跳过（已生成）: {}", 
                    handledCount, timeoutTasks.size(), skippedCount);
            }
            return handledCount;
            
        } catch (Exception e) {
            logger.error("检查超时图生视频任务失败", e);
            return 0;
        }
    }

    /**
     * 删除任务
     * @param taskId 任务ID
     * @param username 用户名（用于权限验证）
     * @return 是否删除成功
     */
    @Transactional
    public boolean deleteTask(String taskId, String username) {
        try {
            // 查找任务
            ImageToVideoTask task = taskRepository.findByTaskId(taskId).orElse(null);
            
            if (task == null) {
                logger.warn("删除任务失败：任务不存在, taskId={}", taskId);
                return false;
            }
            
            // 验证权限
            if (!task.getUsername().equals(username)) {
                logger.warn("删除任务失败：无权限, taskId={}, taskUsername={}, requestUsername={}", 
                    taskId, task.getUsername(), username);
                return false;
            }
            
            // 删除任务
            taskRepository.delete(task);
            logger.info("图生视频任务删除成功: taskId={}, username={}", taskId, username);
            
            return true;
        } catch (Exception e) {
            logger.error("删除图生视频任务失败: taskId={}", taskId, e);
            throw new RuntimeException("删除任务失败: " + e.getMessage());
        }
    }
    
    /**
     * 安全返还冻结积分（捕获异常，避免影响主流程）
     */
    private void returnFrozenPointsSafely(String taskId) {
        try {
            userService.returnFrozenPoints(taskId);
            logger.info("已返还冻结积分: taskId={}", taskId);
        } catch (Exception e) {
            logger.warn("返还冻结积分失败（可能未冻结）: taskId={}, error={}", taskId, e.getMessage());
        }
    }
    
    /**
     * 重试失败的图生视频任务
     * 复用原task_id和已上传的图片，重新提交至外部API
     */
    @Transactional
    public ImageToVideoTask retryTask(String taskId, String username) {
        logger.info("重试任务: taskId={}, username={}", taskId, username);
        
        // 获取任务
        ImageToVideoTask task = taskRepository.findByTaskId(taskId)
            .orElseThrow(() -> new RuntimeException("任务不存在: " + taskId));
        
        // 验证权限
        if (!task.getUsername().equals(username)) {
            throw new RuntimeException("无权操作此任务");
        }
        
        // 允许重试：失败的任务、完成但没有结果的任务、或者正在处理中的任务（可能卡住了）
        boolean canRetry = task.getStatus() == ImageToVideoTask.TaskStatus.FAILED ||
            task.getStatus() == ImageToVideoTask.TaskStatus.COMPLETED ||
            task.getStatus() == ImageToVideoTask.TaskStatus.PROCESSING;
        
        if (!canRetry) {
            throw new RuntimeException("无法重试此任务，当前状态: " + task.getStatus());
        }
        
        // 验证图片URL存在
        if (task.getFirstFrameUrl() == null || task.getFirstFrameUrl().isEmpty()) {
            throw new RuntimeException("任务缺少首帧图片，无法重试");
        }
        
        logger.info("允许重试任务: taskId={}, 原状态={}, 原结果URL={}", 
            taskId, task.getStatus(), task.getResultUrl());
        
        // 重置任务状态为 PROCESSING（直接生成中）
        task.setStatus(ImageToVideoTask.TaskStatus.PROCESSING);
        task.setErrorMessage(null);
        task.setRealTaskId(null); // 清除旧的外部任务ID
        task.setResultUrl(null);  // 清除旧的结果URL
        task.setProgress(0);
        task.setUpdatedAt(java.time.LocalDateTime.now());
        task.setCompletedAt(null);
        taskRepository.save(task);
        
        // 重新添加到任务队列
        try {
            taskQueueService.addImageToVideoTask(username, taskId);
            logger.info("重试任务已添加到队列: taskId={}", taskId);
        } catch (Exception e) {
            logger.error("添加重试任务到队列失败: taskId={}", taskId, e);
            // 回滚任务状态
            task.setStatus(ImageToVideoTask.TaskStatus.FAILED);
            task.setErrorMessage("重试失败: " + e.getMessage());
            taskRepository.save(task);
            throw new RuntimeException("重试失败: " + e.getMessage());
        }
        
        // 更新UserWork状态为PROCESSING
        try {
            userWorkService.updateWorkStatus(taskId, com.example.demo.model.UserWork.WorkStatus.PROCESSING);
        } catch (Exception e) {
            logger.warn("更新UserWork状态失败: taskId={}", taskId, e);
        }
        
        // 更新task_status表状态
        try {
            taskStatusPollingService.updateTaskStatusWithCascade(
                taskId, 
                com.example.demo.model.TaskStatus.Status.PROCESSING, 
                null, 
                null
            );
        } catch (Exception e) {
            logger.warn("更新task_status状态失败: taskId={}", taskId, e);
        }
        
        return task;
    }
}
