package top.tqx.demo_1.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpEntity;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.core.io.ByteArrayResource;
import top.tqx.demo_1.config.AiApiConfig;
import top.tqx.demo_1.dto.ExamGenerateRequest;
import top.tqx.demo_1.entity.Department;
import top.tqx.demo_1.entity.File;
import top.tqx.demo_1.entity.KnowledgeBasePath;
import top.tqx.demo_1.enums.FileProcessStepStatus;
import top.tqx.demo_1.mapper.DepartmentMapper;
import top.tqx.demo_1.mapper.FileMapper;
import top.tqx.demo_1.mapper.KnowledgeBasePathMapper;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Service
public class AsyncVectorizeService {

    @Autowired
    private AiApiConfig aiApiConfig;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private FileMapper fileMapper;

    @Autowired
    private KnowledgeBasePathMapper knowledgeBasePathMapper;

    @Autowired
    private DepartmentMapper departmentMapper;

    @Autowired
    private CollectionFileService collectionFileService;

    @Autowired
    private AsyncExamService asyncExamService;

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

    public static final String VECTORIZE_STATUS_PENDING = "PENDING";
    public static final String VECTORIZE_STATUS_VECTORIZING = "VECTORIZING";
    public static final String VECTORIZE_STATUS_INDEXED = "INDEXED";
    public static final String VECTORIZE_STATUS_FAILED = "FAILED";

    @Async("vectorizeTaskExecutor")
    public void vectorizeFileAsync(String collection, Long fileId, Long userId) {
        log.info("========== 异步文件向量化任务开始 ==========");
        log.info("collection={}, fileId={}, userId={}, 当前线程: {}",
                collection, fileId, userId, Thread.currentThread().getName());

        long startTime = System.currentTimeMillis();

        try {
            File file = waitForFileExists(fileId, 5);
            if (file == null) {
                log.warn("文件不存在，跳过向量化: fileId={}", fileId);
                return;
            }

            ensureCollectionExists(collection, file.getDeptId(), userId);

            updateFileProcessStatus(fileId, VECTORIZE_STATUS_VECTORIZING, "正在向量化，请稍后...");

            String filePath = file.getFilePath();
            if (filePath == null || filePath.isEmpty()) {
                log.error("文件路径为空: fileId={}, fileName={}", fileId, file.getFileName());
                updateFileProcessStatus(fileId, VECTORIZE_STATUS_FAILED, "文件路径为空");
                return;
            }

            Path path = Paths.get(uploadPath, filePath).normalize();
            if (!Files.exists(path)) {
                log.error("文件不存在于服务器: fileId={}, fileName={}, path={}",
                        fileId, file.getFileName(), path.toAbsolutePath());
                updateFileProcessStatus(fileId, VECTORIZE_STATUS_FAILED, "文件不存在于服务器");
                return;
            }

            byte[] fileContent = Files.readAllBytes(path);
            String originalFilename = file.getFileName();

            Map<String, Object> uploadResult = uploadToRAG(collection, originalFilename, fileContent);

            Boolean uploadSuccess = (Boolean) uploadResult.get("success");
            if (uploadSuccess == null || !uploadSuccess) {
                String message = (String) uploadResult.get("message");
                throw new RuntimeException("文件上传到向量库失败: " + message);
            }

            if (file.getVectorDbAddress() == null || file.getVectorDbAddress().isEmpty()) {
                file.setVectorDbAddress(collection + "/" + originalFilename);
                file.setUpdateTime(LocalDateTime.now());
                fileMapper.updateById(file);
            }

            collectionFileService.saveOrUpdate(collection, originalFilename, fileId, file.getFilePath(), file.getExtension());

            long duration = System.currentTimeMillis() - startTime;
            String successMessage = String.format("向量化成功，耗时 %d 毫秒，生成 %d 个切片",
                    duration, uploadResult.get("chunkCount"));

            updateFileProcessStatus(fileId, VECTORIZE_STATUS_INDEXED, successMessage);

            log.info("文件向量化成功: collection={}, fileId={}, fileName={}, duration={}ms",
                    collection, fileId, originalFilename, duration);

            triggerSync();

            triggerExamGeneration(fileId, collection, originalFilename, userId);

        } catch (Exception e) {
            long duration = System.currentTimeMillis() - startTime;
            String errorType = e.getMessage() != null && (e.getMessage().contains("500") || e.getMessage().contains("INTERNAL SERVER ERROR")) 
                ? "[AI端错误]" : "[本地错误]";
            String errorMessage = String.format("向量化失败%s: %s (耗时 %d 毫秒)", errorType, e.getMessage(), duration);
            log.error("========== 异步文件向量化任务失败 ==========", e);

            updateFileProcessStatus(fileId, VECTORIZE_STATUS_FAILED, errorMessage);

            throw new RuntimeException(errorMessage, e);
        }
    }

    @Async("vectorizeTaskExecutor")
    public void batchVectorizeFilesAsync(String collection, Long fileId, Long userId, String requestBatchId) {
        log.info("========== 异步批量文件向量化子任务开始 ==========");
        log.info("collection={}, fileId={}, userId={}, requestBatchId={}, 当前线程: {}",
                collection, fileId, userId, requestBatchId, Thread.currentThread().getName());

        long startTime = System.currentTimeMillis();

        try {
            File file = waitForFileExists(fileId, 5);
            if (file == null) {
                log.warn("文件不存在，跳过向量化: fileId={}, batchId={}", fileId, requestBatchId);
                return;
            }

            ensureCollectionExists(collection, file.getDeptId(), userId);

            updateFileProcessStatus(fileId, VECTORIZE_STATUS_VECTORIZING,
                    String.format("正在向量化（批次ID: %s），请稍后...", requestBatchId));

            String filePath = file.getFilePath();
            if (filePath == null || filePath.isEmpty()) {
                log.error("文件路径为空: fileId={}, fileName={}, batchId={}",
                        fileId, file.getFileName(), requestBatchId);
                updateFileProcessStatus(fileId, VECTORIZE_STATUS_FAILED,
                        String.format("文件路径为空（批次ID: %s）", requestBatchId));
                return;
            }

            Path path = Paths.get(uploadPath, filePath).normalize();
            if (!Files.exists(path)) {
                log.error("文件不存在于服务器: fileId={}, fileName={}, path={}, batchId={}",
                        fileId, file.getFileName(), path.toAbsolutePath(), requestBatchId);
                updateFileProcessStatus(fileId, VECTORIZE_STATUS_FAILED,
                        String.format("文件不存在于服务器（批次ID: %s）", requestBatchId));
                return;
            }

            byte[] fileContent = Files.readAllBytes(path);
            String originalFilename = file.getFileName();

            Map<String, Object> uploadResult = uploadToRAG(collection, originalFilename, fileContent);

            Boolean uploadSuccess = (Boolean) uploadResult.get("success");
            if (uploadSuccess == null || !uploadSuccess) {
                String message = (String) uploadResult.get("message");
                throw new RuntimeException("文件上传到向量库失败: " + message);
            }

            if (file.getVectorDbAddress() == null || file.getVectorDbAddress().isEmpty()) {
                file.setVectorDbAddress(collection + "/" + originalFilename);
                file.setUpdateTime(LocalDateTime.now());
                fileMapper.updateById(file);
            }

            collectionFileService.saveOrUpdate(collection, originalFilename, fileId, file.getFilePath(), file.getExtension());

            long duration = System.currentTimeMillis() - startTime;
            String successMessage = String.format("向量化成功，耗时 %d 毫秒，生成 %d 个切片（批次ID: %s）",
                    duration, uploadResult.get("chunkCount"), requestBatchId);

            updateFileProcessStatus(fileId, VECTORIZE_STATUS_INDEXED, successMessage);

            log.info("文件向量化成功: collection={}, fileId={}, fileName={}, duration={}ms",
                    collection, fileId, originalFilename, duration);

        } catch (Exception e) {
            long duration = System.currentTimeMillis() - startTime;
            String errorType = e.getMessage() != null && (e.getMessage().contains("500") || e.getMessage().contains("INTERNAL SERVER ERROR")) 
                ? "[AI端错误]" : "[本地错误]";
            String errorMessage = String.format("向量化失败%s: %s (耗时 %d 毫秒，批次ID: %s)",
                    errorType, e.getMessage(), duration, requestBatchId);
            log.error("========== 异步批量文件向量化子任务失败 ==========", e);

            updateFileProcessStatus(fileId, VECTORIZE_STATUS_FAILED, errorMessage);

            throw new RuntimeException(errorMessage, e);
        }
    }

    private File waitForFileExists(Long fileId, int maxRetries) {
        for (int i = 0; i < maxRetries; i++) {
            File file = fileMapper.selectById(fileId);
            if (file != null) {
                if (i > 0) {
                    log.info("文件查询成功，重试次数: {}", i);
                }
                return file;
            }

            if (i < maxRetries - 1) {
                try {
                    long waitTime = 100 * (i + 1);
                    log.debug("文件暂未找到，等待 {}ms 后重试 ({}/{})", waitTime, i + 1, maxRetries);
                    Thread.sleep(waitTime);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    log.warn("等待被中断: fileId={}", fileId);
                    return null;
                }
            }
        }

        log.error("文件查询超时，已重试 {} 次: fileId={}", maxRetries, fileId);
        return null;
    }

    private void ensureCollectionExists(String collectionName, Long deptId, Long userId) {
        try {
            log.info("检查collection是否存在: collectionName={}", collectionName);
            
            String url = aiApiConfig.getFullUrl(aiApiConfig.getCollection().getList());
            ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
            
            if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
                com.fasterxml.jackson.databind.ObjectMapper objectMapper = new com.fasterxml.jackson.databind.ObjectMapper();
                Map<String, Object> responseBody = objectMapper.readValue(
                        response.getBody(),
                        new com.fasterxml.jackson.core.type.TypeReference<Map<String, Object>>() {});
                
                @SuppressWarnings("unchecked")
                java.util.List<Map<String, Object>> collections = 
                        (java.util.List<Map<String, Object>>) responseBody.get("collections");
                
                boolean exists = false;
                if (collections != null) {
                    for (Map<String, Object> coll : collections) {
                        if (collectionName.equals(coll.get("name"))) {
                            exists = true;
                            log.info("Collection已存在: {}", collectionName);
                            break;
                        }
                    }
                }
                
                if (!exists) {
                    log.info("Collection不存在，准备创建: collectionName={}, deptId={}", collectionName, deptId);
                    createCollection(collectionName, deptId, userId);
                }
            }
        } catch (org.springframework.web.client.ResourceAccessException e) {
            log.warn("RAG服务不可用，跳过collection检查: collectionName={}, 错误: {}", 
                    collectionName, e.getMessage());
            log.warn("请确保RAG服务已启动并在配置中指定正确的地址 (当前配置: http://{}:{})",
                    aiApiConfig.getHost(), aiApiConfig.getPort());
        } catch (Exception e) {
            log.error("检查或创建collection失败: collectionName={}", collectionName, e);
            throw new RuntimeException("确保collection存在失败: " + e.getMessage(), e);
        }
    }

    private void createCollection(String collectionName, Long deptId, Long userId) {
        try {
            String url = aiApiConfig.getFullUrl(aiApiConfig.getCollection().getCreate());
            
            String departmentName = getDepartmentName(deptId);
            
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            
            Map<String, Object> requestBody = new HashMap<>();
            requestBody.put("name", collectionName);
            requestBody.put("display_name", generateDisplayName(collectionName, deptId));
            requestBody.put("department", departmentName);
            requestBody.put("description", "自动创建的向量库 - " + collectionName);
            requestBody.put("uploader", userId != null ? userId.toString() : "system");
            requestBody.put("owner_type", deptId != null ? "department" : "personal");
            requestBody.put("visibility", "public");
            
            HttpEntity<Map<String, Object>> entity = new HttpEntity<>(requestBody, headers);
            
            ResponseEntity<String> response = restTemplate.postForEntity(url, entity, String.class);
            
            if (response.getStatusCode().is2xxSuccessful()) {
                log.info("Collection创建成功: collectionName={}, deptId={}, departmentName={}", collectionName, deptId, departmentName);
                
                saveKnowledgeBasePath(collectionName, deptId, userId);
            } else if (response.getStatusCode().value() == 400) {
                String body = response.getBody() != null ? response.getBody() : "";
                log.warn("RAG服务返回400错误，可能collection已存在: collectionName={}, body={}", collectionName, body);
                
                if (body.contains("已存在") || body.contains("exists") || body.contains("already")) {
                    log.info("Collection已存在，直接使用: {}", collectionName);
                    
                    KnowledgeBasePath existingKb = knowledgeBasePathMapper.selectOne(
                        new LambdaQueryWrapper<KnowledgeBasePath>().eq(KnowledgeBasePath::getName, collectionName)
                    );
                    
                    if (existingKb == null) {
                        log.info("本地记录不存在，创建知识库路径记录: {}", collectionName);
                        saveKnowledgeBasePath(collectionName, deptId, userId);
                    }
                } else {
                    log.error("Collection创建失败: status={}, body={}", response.getStatusCode(), body);
                    throw new RuntimeException("Collection创建失败: " + body);
                }
            } else {
                String errorMsg = response.getBody() != null ? response.getBody() : "";
                log.error("Collection创建失败: status={}, body={}", response.getStatusCode(), errorMsg);
                throw new RuntimeException("Collection创建失败: " + errorMsg);
            }
        } catch (org.springframework.web.client.ResourceAccessException e) {
            log.warn("RAG服务不可用，无法创建collection: collectionName={}, 错误: {}", 
                    collectionName, e.getMessage());
            log.warn("请手动在RAG服务中创建collection，或稍后重试");
        } catch (Exception e) {
            log.error("创建collection异常: collectionName={}, deptId={}", collectionName, deptId, e);
            throw new RuntimeException("创建collection失败: " + e.getMessage(), e);
        }
    }

    private String getDepartmentName(Long deptId) {
        if (deptId == null) {
            return "公共部门";
        }
        try {
            Department dept = departmentMapper.selectById(deptId);
            if (dept != null && dept.getDeptName() != null) {
                return dept.getDeptName();
            }
        } catch (Exception e) {
            log.warn("查询部门名称失败: deptId={}", deptId, e);
        }
        return "公共部门";
    }

    private String generateDisplayName(String collectionName, Long deptId) {
        if (collectionName.startsWith("dept_") && collectionName.endsWith("_kb")) {
            String deptIdStr = collectionName.substring(5, collectionName.length() - 3);
            return "部门" + deptIdStr + "知识库";
        } else if ("public_kb".equals(collectionName)) {
            return "公共知识库";
        } else if ("private_kb".equals(collectionName)) {
            return "个人私有知识库";
        }
        return collectionName;
    }

    private void saveKnowledgeBasePath(String collectionName, Long deptId, Long userId) {
        try {
            String departmentName = getDepartmentName(deptId);
            
            KnowledgeBasePath kbPath = new KnowledgeBasePath();
            kbPath.setName(collectionName);
            kbPath.setDisplayName(generateDisplayName(collectionName, deptId));
            kbPath.setPath("/knowledge/" + collectionName);
            kbPath.setDepartment(departmentName);
            kbPath.setUploader(userId != null ? userId.toString() : "system");
            kbPath.setOwnerType(deptId != null ? "department" : "personal");
            kbPath.setVisibility("public");
            kbPath.setDescription("自动创建的向量库");
            kbPath.setCreateTime(LocalDateTime.now());
            kbPath.setUpdateTime(LocalDateTime.now());

            knowledgeBasePathMapper.insert(kbPath);
            log.info("保存知识库路径记录成功: collectionName={}, departmentName={}", collectionName, departmentName);
        } catch (Exception e) {
            log.error("保存知识库路径记录失败: collectionName={}", collectionName, e);
        }
    }

    private Map<String, Object> uploadToRAG(String collection, String filename, byte[] content) {
        Map<String, Object> result = new HashMap<>();
        try {
            String url = aiApiConfig.getFullUrl(aiApiConfig.getDocument().getUpload());

            org.springframework.http.HttpHeaders headers = new org.springframework.http.HttpHeaders();
            headers.setContentType(org.springframework.http.MediaType.MULTIPART_FORM_DATA);

            ByteArrayResource resource = new ByteArrayResource(content) {
                @Override
                public String getFilename() {
                    return filename;
                }
            };

            LinkedMultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
            body.add("file", resource);
            body.add("collection", collection);

            org.springframework.http.HttpEntity<LinkedMultiValueMap<String, Object>> entity =
                    new org.springframework.http.HttpEntity<>(body, headers);

            org.springframework.http.ResponseEntity<String> response =
                    restTemplate.postForEntity(url, entity, String.class);

            if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
                com.fasterxml.jackson.databind.ObjectMapper objectMapper =
                        new com.fasterxml.jackson.databind.ObjectMapper();
                Map<String, Object> responseBody = objectMapper.readValue(
                        response.getBody(),
                        new com.fasterxml.jackson.core.type.TypeReference<Map<String, Object>>() {});
                result.putAll(responseBody);
                result.put("success", true);
            } else {
                String errorBody = response.getBody() != null ? response.getBody() : "";
                result.put("success", false);
                result.put("message", "RAG服务返回错误: " + response.getStatusCode() + " - " + errorBody);
            }
        } catch (Exception e) {
            result.put("success", false);
            result.put("message", e.getMessage());
            log.error("上传文件到RAG服务失败", e);
        }
        return result;
    }

    private void triggerSync() {
        try {
            String url = aiApiConfig.getFullUrl(aiApiConfig.getSync().getTrigger());
            log.info("触发AI端文档同步: {}", url);
            org.springframework.http.ResponseEntity<String> response =
                    restTemplate.postForEntity(url, null, String.class);
            if (response.getStatusCode().is2xxSuccessful()) {
                log.info("文档同步触发成功");
            } else {
                log.warn("文档同步触发失败: status={}, body={}",
                        response.getStatusCode(), response.getBody());
            }
        } catch (Exception e) {
            log.warn("触发文档同步时发生异常: {}", e.getMessage());
        }
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public void updateFileProcessStatus(Long fileId, String status, String message) {
        if (fileId == null) {
            log.warn("更新文件状态失败: fileId为null");
            return;
        }
        try {
            File file = fileMapper.selectById(fileId);
            if (file != null) {
                log.info("更新文件向量化状态: fileId={}, 从 {} 变为 {}", fileId, file.getProcessStatus(), status);
                file.setProcessStatus(status);
                if (message != null) {
                    file.setProcessMessage(message.length() > 500 ? message.substring(0, 500) : message);
                }
                
                // 更新流程状态
                if (VECTORIZE_STATUS_VECTORIZING.equals(status)) {
                    file.setProcessStepStatus(FileProcessStepStatus.VECTORIZING.getCode());
                    file.setProcessStepMessage(FileProcessStepStatus.VECTORIZING.getDescription());
                } else if (VECTORIZE_STATUS_INDEXED.equals(status)) {
                    file.setProcessStepStatus(FileProcessStepStatus.VECTORIZED.getCode());
                    file.setProcessStepMessage(FileProcessStepStatus.VECTORIZED.getDescription());
                } else if (VECTORIZE_STATUS_FAILED.equals(status)) {
                    file.setProcessStepStatus(FileProcessStepStatus.VECTORIZE_FAILED.getCode());
                    String stepMsg = FileProcessStepStatus.VECTORIZE_FAILED.getDescription() + ": " + (message != null ? message : "");
                    file.setProcessStepMessage(stepMsg.length() > 500 ? stepMsg.substring(0, 500) : stepMsg);
                }
                
                file.setUpdateTime(LocalDateTime.now());
                int result = fileMapper.updateById(file);
                if (result > 0) {
                    log.debug("文件状态更新成功: fileId={}", fileId);
                } else {
                    log.warn("文件状态更新失败: fileId={}, status={}, 未影响任何行", fileId, status);
                }
            } else {
                log.warn("更新文件状态失败: fileId={} 对应的文件不存在", fileId);
            }
        } catch (Exception e) {
            log.error("更新文件向量化状态失败: fileId={}, status={}, error={}", fileId, status, e.getMessage(), e);
            throw new RuntimeException("更新文件状态失败", e);
        }
    }

    private void triggerExamGeneration(Long fileId, String collection, String filename, Long userId) {
        try {
            log.info("触发出题任务: fileId={}, collection={}, filename={}", fileId, collection, filename);
            
            ExamGenerateRequest request = new ExamGenerateRequest();
            request.setFileId(fileId);
            request.setCollection(collection);
            request.setFilePath(collection + "/" + filename);
            request.setDifficulty(3);
            request.setRequestId("auto_gen_" + fileId + "_" + System.currentTimeMillis());
            
            asyncExamService.generateQuestionsAsync(request);
            
            log.info("出题任务已触发: requestId={}", request.getRequestId());
        } catch (Exception e) {
            log.error("触发出题任务失败: fileId={}", fileId, e);
        }
    }
}
