package com.qqchen.deploy.backend.framework.ssh.file;

import com.qqchen.deploy.backend.framework.exception.UploadCancelledException;
import com.qqchen.deploy.backend.framework.utils.FileUtils;
import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;

/**
 * 异步文件上传服务（Framework层）
 * 
 * 提供通用的异步上传、进度追踪、任务管理功能
 * 可被不同的文件上传场景（SSH、FTP、云存储等）复用
 */
@Service
@Slf4j
public class AsyncFileUploadService {
    
    private final SimpMessagingTemplate messagingTemplate;  // WebSocket，可选
    
    private final AsyncTaskExecutor executor;  // 虚拟线程池（来自SSHWebSocketConfig）
    
    // ========== 线程安全的任务存储 ==========
    
    // 按服务器分组存储任务（外层：serverId → 内层：taskId → Task）
    private final Map<Long, Map<String, FileUploadTask>> tasksByServer = new ConcurrentHashMap<>();
    
    // 反向索引：taskId → serverId（快速查找任务归属）
    private final Map<String, Long> taskToServer = new ConcurrentHashMap<>();
    
    // 分段锁：每个服务器一个锁，减少锁竞争
    private final ConcurrentHashMap<Long, Object> serverLocks = new ConcurrentHashMap<>();
    
    /**
     * 构造函数
     * 
     * @param messagingTemplate WebSocket消息模板（可选，如果没有配置WebSocket则为null）
     * @param executor 文件上传专用虚拟线程池
     */
    public AsyncFileUploadService(
            @Autowired(required = false) SimpMessagingTemplate messagingTemplate,
            @Qualifier("fileUploadExecutor") AsyncTaskExecutor executor) {
        this.messagingTemplate = messagingTemplate;
        this.executor = executor;
        
        if (messagingTemplate == null) {
            log.warn("SimpMessagingTemplate未配置，进度推送功能不可用（仅支持轮询查询）");
        } else {
            log.info("SimpMessagingTemplate已配置，支持WebSocket实时进度推送");
        }
        
        log.info("文件上传服务初始化完成，使用虚拟线程池");
    }
    
    /**
     * 提交上传任务
     * 
     * @param serverId 服务器ID
     * @param fileName 文件名
     * @param fileSize 文件大小
     * @param uploadAction 上传动作（接收进度回调，执行实际上传）
     * @return 任务ID
     */
    public String submitTask(Long serverId, String fileName, long fileSize, 
                            Consumer<ProgressCallback> uploadAction) {
        return submitTask(serverId, fileName, fileSize, null, uploadAction);
    }
    
    /**
     * 提交上传任务（带元数据，线程安全）
     * 
     * @param serverId 服务器ID
     * @param fileName 文件名
     * @param fileSize 文件大小
     * @param metadata 元数据（如目标路径、覆盖标记等）
     * @param uploadAction 上传动作（接收进度回调，执行实际上传）
     * @return 任务ID
     */
    public String submitTask(Long serverId, String fileName, long fileSize, Map<String, Object> metadata,
                            Consumer<ProgressCallback> uploadAction) {
        // 生成任务ID
        String taskId = UUID.randomUUID().toString();
        
        // 初始化 metadata（如果为null）
        if (metadata == null) {
            metadata = new HashMap<>();
        }
        
        // 确保 metadata 包含 serverId（如果没有则添加）
        if (!metadata.containsKey("serverId")) {
            metadata.put("serverId", serverId);
        }
        
        // 创建任务
        FileUploadTask task = FileUploadTask.builder()
            .taskId(taskId)
            .fileName(fileName)
            .fileSize(fileSize)
            .fileSizeFormatted(FileUtils.formatFileSize(fileSize))
            .uploadedSize(0L)
            .uploadedSizeFormatted("0 B")
            .progress(0)
            .status(TaskStatus.PENDING)
            .metadata(metadata)
            .createTime(new Date())
            .updateTime(new Date())
            .build();
        
        // 🔒 获取服务器锁，保证原子性
        Object lock = serverLocks.computeIfAbsent(serverId, k -> new Object());
        
        synchronized (lock) {
            // 同时更新两个 Map（原子操作）
            tasksByServer.computeIfAbsent(serverId, k -> new ConcurrentHashMap<>())
                         .put(taskId, task);
            
            taskToServer.put(taskId, serverId);
        }
        
        log.info("提交上传任务: serverId={}, taskId={}, fileName={}, size={}", 
            serverId, taskId, fileName, FileUtils.formatFileSize(fileSize));
        
        // 异步执行上传
        executor.submit(() -> executeUpload(serverId, taskId, task, uploadAction));
        
        return taskId;
    }
    
    /**
     * 执行上传（异步）
     */
    private void executeUpload(Long serverId, String taskId, FileUploadTask task, 
                              Consumer<ProgressCallback> uploadAction) {
        try {
            // 更新状态为上传中
            updateTaskStatus(task, TaskStatus.UPLOADING, 0);
            sendTaskUpdate(task);
            
            log.info("开始上传文件: taskId={}, fileName={}", taskId, task.getFileName());
            
            // 创建进度回调
            ProgressCallback progressCallback = (uploaded, total) -> {
                // ⚠️ 检查任务是否被取消
                if (task.isCancelled()) {
                    log.info("检测到取消标记，中断上传: taskId={}, progress={}%", 
                        taskId, (int) ((uploaded * 100) / total));
                    throw new UploadCancelledException();
                }
                
                // 计算进度
                int progress = (int) ((uploaded * 100) / total);
                task.setUploadedSize(uploaded);
                task.setUploadedSizeFormatted(FileUtils.formatFileSize(uploaded));
                task.setProgress(progress);
                task.setUpdateTime(new Date());
                
                // 发送进度更新
                sendProgressUpdate(taskId, progress, uploaded, total);
            };
            
            // 执行上传动作
            uploadAction.accept(progressCallback);
            
            // 上传成功
            updateTaskStatus(task, TaskStatus.SUCCESS, 100);
            task.setUploadedSize(task.getFileSize());
            task.setUploadedSizeFormatted(task.getFileSizeFormatted());
            sendTaskUpdate(task);
            
            log.info("文件上传成功: taskId={}, fileName={}", taskId, task.getFileName());
            
        } catch (Exception e) {
            // 判断是取消还是失败
            if (task.isCancelled()) {
                log.info("上传已取消: taskId={}, fileName={}", taskId, task.getFileName());
                task.setStatus(TaskStatus.CANCELLED);
                task.setErrorMessage("用户取消上传");
            } else {
                log.error("文件上传失败: taskId={}, fileName={}", taskId, task.getFileName(), e);
                task.setStatus(TaskStatus.FAILED);
                task.setErrorMessage(e.getMessage());
            }
            task.setUpdateTime(new Date());
            
            // 发送状态更新通知
            sendTaskUpdate(task);
        }
    }
    
    /**
     * 更新任务状态
     */
    private void updateTaskStatus(FileUploadTask task, TaskStatus status, int progress) {
        task.setStatus(status);
        task.setProgress(progress);
        task.setUpdateTime(new Date());
    }
    
    /**
     * 发送进度更新（通过WebSocket）
     */
    private void sendProgressUpdate(String taskId, int progress, long uploaded, long total) {
        if (messagingTemplate != null) {
            try {
                messagingTemplate.convertAndSend("/topic/upload/" + taskId, Map.of(
                    "taskId", taskId,
                    "status", TaskStatus.UPLOADING.name(),
                    "progress", progress,
                    "uploaded", uploaded,
                    "total", total,
                    "uploadedFormatted", FileUtils.formatFileSize(uploaded),
                    "totalFormatted", FileUtils.formatFileSize(total),
                    "timestamp", System.currentTimeMillis()
                ));
            } catch (Exception e) {
                log.warn("发送进度更新失败: taskId={}", taskId, e);
            }
        }
    }
    
    /**
     * 发送任务更新（通过WebSocket）
     */
    private void sendTaskUpdate(FileUploadTask task) {
        if (messagingTemplate != null) {
            try {
                messagingTemplate.convertAndSend("/topic/upload/" + task.getTaskId(), Map.of(
                    "taskId", task.getTaskId(),
                    "status", task.getStatus().name(),
                    "progress", task.getProgress(),
                    "uploaded", task.getUploadedSize(),
                    "total", task.getFileSize(),
                    "uploadedFormatted", task.getUploadedSizeFormatted(),
                    "totalFormatted", task.getFileSizeFormatted(),
                    "errorMessage", task.getErrorMessage() != null ? task.getErrorMessage() : "",
                    "timestamp", System.currentTimeMillis()
                ));
            } catch (Exception e) {
                log.warn("发送任务更新失败: taskId={}", task.getTaskId(), e);
            }
        }
    }
    
    /**
     * 查询任务状态（线程安全）
     * 
     * @param taskId 任务ID
     * @return 任务信息
     */
    public FileUploadTask getTaskStatus(String taskId) {
        Long serverId = taskToServer.get(taskId);
        if (serverId == null) {
            return null;
        }
        
        Map<String, FileUploadTask> serverTasks = tasksByServer.get(serverId);
        return serverTasks != null ? serverTasks.get(taskId) : null;
    }
    
    /**
     * 查询任务状态（带服务器验证，线程安全）
     * 
     * @param serverId 服务器ID
     * @param taskId 任务ID
     * @return 任务信息，如果任务不属于该服务器则返回null
     */
    public FileUploadTask getTaskStatus(Long serverId, String taskId) {
        // 验证任务归属
        Long actualServerId = taskToServer.get(taskId);
        if (actualServerId == null || !actualServerId.equals(serverId)) {
            return null;
        }
        
        Map<String, FileUploadTask> serverTasks = tasksByServer.get(serverId);
        return serverTasks != null ? serverTasks.get(taskId) : null;
    }
    
    /**
     * 取消任务（支持取消排队中和上传中的任务，带服务器验证，线程安全）
     * 
     * @param serverId 服务器ID
     * @param taskId 任务ID
     * @return 是否取消成功
     */
    public boolean cancelTask(Long serverId, String taskId) {
        // 验证任务归属
        Long actualServerId = taskToServer.get(taskId);
        if (actualServerId == null || !actualServerId.equals(serverId)) {
            log.warn("任务不属于该服务器: taskId={}, requestServerId={}, actualServerId={}", 
                taskId, serverId, actualServerId);
            return false;
        }
        
        Object lock = serverLocks.get(serverId);
        if (lock == null) {
            return false;
        }
        
        synchronized (lock) {
            Map<String, FileUploadTask> serverTasks = tasksByServer.get(serverId);
            if (serverTasks == null) {
                return false;
            }
            
            FileUploadTask task = serverTasks.get(taskId);
            if (task == null) {
                return false;
            }
            
            TaskStatus status = task.getStatus();
            
            // ✅ 支持取消 PENDING 和 UPLOADING 状态的任务
            if (status == TaskStatus.PENDING || status == TaskStatus.UPLOADING) {
                task.cancel();  // 设置取消标记
                
                // 如果是 PENDING 状态，直接更新为 CANCELLED
                if (status == TaskStatus.PENDING) {
                    task.setStatus(TaskStatus.CANCELLED);
                    task.setUpdateTime(new Date());
                    sendTaskUpdate(task);
                    log.info("任务已取消: serverId={}, taskId={}, status=PENDING", serverId, taskId);
                } else {
                    // UPLOADING 状态，会在下次进度回调时中断
                    log.info("任务已标记为取消: serverId={}, taskId={}, status=UPLOADING（将在下次进度回调时中断）", 
                        serverId, taskId);
                }
                
                return true;
            }
        }
        return false;
    }
    
    /**
     * 清理已完成的任务（定时清理，避免内存泄漏，线程安全）
     * 
     * @param olderThanHours 清理多少小时前的任务
     * @return 清理的任务数
     */
    public int cleanupCompletedTasks(int olderThanHours) {
        long cutoffTime = System.currentTimeMillis() - (olderThanHours * 60 * 60 * 1000L);
        int count = 0;
        
        // 遍历所有服务器
        for (Map.Entry<Long, Map<String, FileUploadTask>> serverEntry : tasksByServer.entrySet()) {
            Long serverId = serverEntry.getKey();
            Map<String, FileUploadTask> serverTasks = serverEntry.getValue();
            
            Object lock = serverLocks.get(serverId);
            if (lock == null) {
                continue;
            }
            
            synchronized (lock) {
                // 清理该服务器的旧任务
                serverTasks.entrySet().removeIf(taskEntry -> {
                    FileUploadTask task = taskEntry.getValue();
                    boolean shouldRemove = (task.getStatus() == TaskStatus.SUCCESS || 
                                           task.getStatus() == TaskStatus.FAILED || 
                                           task.getStatus() == TaskStatus.CANCELLED)
                                           && task.getUpdateTime().getTime() < cutoffTime;
                    if (shouldRemove) {
                        // 同时从反向索引中删除
                        taskToServer.remove(taskEntry.getKey());
                        log.debug("清理旧任务: serverId={}, taskId={}, status={}", 
                            serverId, taskEntry.getKey(), task.getStatus());
                    }
                    return shouldRemove;
                });
                
                count += serverTasks.size();
                
                // 如果服务器的任务列表空了，清理整个 Map
                if (serverTasks.isEmpty()) {
                    tasksByServer.remove(serverId);
                    serverLocks.remove(serverId);
                }
            }
        }
        
        return count;
    }
    
    /**
     * 获取当前任务统计（线程安全）
     */
    public Map<String, Integer> getTaskStatistics() {
        int pending = 0, uploading = 0, success = 0, failed = 0, cancelled = 0, total = 0;
        
        // 遍历所有服务器的任务
        for (Map<String, FileUploadTask> serverTasks : tasksByServer.values()) {
            for (FileUploadTask task : serverTasks.values()) {
                total++;
                switch (task.getStatus()) {
                    case PENDING -> pending++;
                    case UPLOADING -> uploading++;
                    case SUCCESS -> success++;
                    case FAILED -> failed++;
                    case CANCELLED -> cancelled++;
                }
            }
        }
        
        return Map.of(
            "pending", pending,
            "uploading", uploading,
            "success", success,
            "failed", failed,
            "cancelled", cancelled,
            "total", total
        );
    }
    
    /**
     * 关闭服务时的清理（虚拟线程池由Spring自动管理）
     */
    @PreDestroy
    public void shutdown() {
        int totalTasks = taskToServer.size();
        log.info("异步上传服务关闭，剩余任务: {}", totalTasks);
        // 虚拟线程池由Spring容器管理，无需手动关闭
    }
    
    /**
     * 进度回调接口
     */
    @FunctionalInterface
    public interface ProgressCallback {
        void onProgress(long uploaded, long total);
    }
    
    /**
     * 任务状态枚举
     */
    public enum TaskStatus {
        PENDING,    // 等待中
        UPLOADING,  // 上传中
        SUCCESS,    // 成功
        FAILED,     // 失败
        CANCELLED   // 已取消
    }
}
