package top.tqx.demo_1.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.*;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import top.tqx.demo_1.config.AiApiConfig;
import top.tqx.demo_1.entity.CollectionFile;
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.mapper.DepartmentMapper;
import top.tqx.demo_1.mapper.FileMapper;
import top.tqx.demo_1.mapper.KnowledgeBasePathMapper;
import top.tqx.demo_1.service.CollectionFileService;
import top.tqx.demo_1.service.AsyncVectorizeService;
import top.tqx.demo_1.vo.CollectionVO;
import top.tqx.demo_1.common.Result;

import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
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.*;

@Tag(name = "知识库管理", description = "向量库和文档的CRUD操作，与AI端同步")
@RestController
@RequestMapping("/collection")
@Slf4j
public class CollectionController extends BaseController {

    @Resource
    private AiApiConfig aiApiConfig;

    @Resource
    private RestTemplate restTemplate;

    @Resource
    private KnowledgeBasePathMapper knowledgeBasePathMapper;

    @Resource
    private DepartmentMapper departmentMapper;

    @Resource
    private FileMapper fileMapper;

    @Resource
    private CollectionFileService collectionFileService;

    @Resource
    private AsyncVectorizeService asyncVectorizeService;

    private void triggerSync() {
        try {
            String url = aiApiConfig.getFullUrl(aiApiConfig.getSync().getTrigger());
            log.info("触发AI端文档同步: {}", url);
            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());
        }
    }

    @org.springframework.beans.factory.annotation.Value("${file.upload.path:./uploads/}")
    private String uploadPath;

    @Operation(summary = "获取向量库列表", description = "从本地数据库获取所有向量库列表")
    @GetMapping
    public Result<List<CollectionVO>> getCollections(HttpServletRequest request) {
        try {
            Long userId = getCurrentUserId(request);
            if (userId == null) {
                return error("用户未登录");
            }

            log.info("从本地数据库获取向量库列表");

            List<KnowledgeBasePath> kbPaths = knowledgeBasePathMapper.selectList(null);
            List<CollectionVO> result = new ArrayList<>();

            for (KnowledgeBasePath kbPath : kbPaths) {
                CollectionVO vo = new CollectionVO();
                vo.setName(kbPath.getName());
                vo.setDisplayName(kbPath.getDisplayName() != null ? kbPath.getDisplayName() : kbPath.getName());
                vo.setDescription(kbPath.getDescription());
                vo.setDepartment(kbPath.getDepartment());
                vo.setDocumentCount(0);
                vo.setCreateTime(kbPath.getCreateTime());
                result.add(vo);
            }

            log.info("获取向量库列表成功，共 {} 个", result.size());
            return success(result);
        } catch (Exception e) {
            log.error("获取向量库列表失败", e);
            return error("获取向量库列表失败: " + e.getMessage());
        }
    }

    @Operation(summary = "创建向量库", description = "在AI端创建向量库并同步到knowledge_base_path表")
    @PostMapping
    public Result<CollectionVO> createCollection(
            @Parameter(description = "向量库名称", required = true) @RequestParam String name,
            @Parameter(description = "显示名称") @RequestParam(required = false) String displayName,
            @Parameter(description = "所属部门ID") @RequestParam(required = false) Long departmentId,
            @Parameter(description = "描述") @RequestParam(required = false) String description,
            HttpServletRequest request) {
        try {
            Long userId = getCurrentUserId(request);
            if (userId == null) {
                return error("用户未登录");
            }

            if (name == null || name.isEmpty()) {
                return error("向量库名称不能为空");
            }

            KnowledgeBasePath existingKb = getKnowledgeBasePathByName(name);
            if (existingKb != null) {
                return error("向量库已存在: " + name);
            }

            String departmentName = getDepartmentName(departmentId);

            String url = aiApiConfig.getFullUrl(aiApiConfig.getCollection().getCreate());
            log.info("调用RAG服务创建向量库: {}", url);

            Map<String, Object> requestBody = new HashMap<>();
            requestBody.put("name", name);
            requestBody.put("display_name", displayName != null ? displayName : name);
            if (departmentName != null) {
                requestBody.put("department", departmentName);
            }
            if (description != null) {
                requestBody.put("description", description);
            }

            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            HttpEntity<Map<String, Object>> entity = new HttpEntity<>(requestBody, headers);

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

            boolean aiSuccess = false;
            if (response.getStatusCode().is2xxSuccessful()) {
                aiSuccess = true;
            } else if (response.getStatusCode().value() == 400) {
                String body = response.getBody() != null ? response.getBody() : "";
                log.warn("AI服务返回400错误: {}", body);
                if (body.contains("已存在") || body.contains("exists") || body.contains("already")) {
                    log.warn("AI端向量库已存在，同步本地记录: {}", name);
                    aiSuccess = true;
                } else {
                    return error("创建向量库失败: " + response.getStatusCode() + " - " + body);
                }
            }

            if (!aiSuccess) {
                return error("创建向量库失败: " + response.getStatusCode());
            }

            KnowledgeBasePath knowledgeBase = new KnowledgeBasePath();
            knowledgeBase.setName(name);
            knowledgeBase.setDisplayName(displayName != null ? displayName : name);
            knowledgeBase.setPath("/knowledge/" + name);
            knowledgeBase.setDepartment(departmentName);
            knowledgeBase.setDescription(description);
            knowledgeBase.setUploader(userId.toString());
            knowledgeBase.setOwnerType(departmentName != null ? "department" : "personal");
            knowledgeBase.setVisibility("public");
            knowledgeBase.setCreateTime(LocalDateTime.now());
            knowledgeBase.setUpdateTime(LocalDateTime.now());

            knowledgeBasePathMapper.insert(knowledgeBase);

            CollectionVO vo = new CollectionVO();
            vo.setName(knowledgeBase.getName());
            vo.setDisplayName(knowledgeBase.getDisplayName());
            vo.setDepartment(knowledgeBase.getDepartment());
            vo.setDescription(knowledgeBase.getDescription());
            vo.setOwnerType(knowledgeBase.getOwnerType());
            vo.setVisibility(knowledgeBase.getVisibility());
            vo.setUploader(knowledgeBase.getUploader());
            vo.setCreateTime(knowledgeBase.getCreateTime());

            log.info("创建向量库成功: name={}, displayName={}, departmentName={}, departmentId={}, userId={}",
                    name, displayName, departmentName, departmentId, userId);

            return success(vo);
        } catch (Exception e) {
            log.error("创建向量库失败", e);
            return error("创建向量库失败: " + e.getMessage());
        }
    }

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

    @Operation(summary = "修改向量库", description = "更新AI端向量库信息并同步到knowledge_base_path表")
    @PutMapping("/{name}")
    public Result<CollectionVO> updateCollection(
            @Parameter(description = "向量库名称", required = true) @PathVariable String name,
            @Parameter(description = "显示名称") @RequestParam(required = false) String displayName,
            @Parameter(description = "所属部门") @RequestParam(required = false) String department,
            @Parameter(description = "描述") @RequestParam(required = false) String description,
            HttpServletRequest request) {
        try {
            Long userId = getCurrentUserId(request);
            if (userId == null) {
                return error("用户未登录");
            }

            KnowledgeBasePath knowledgeBase = getKnowledgeBasePathByName(name);
            if (knowledgeBase == null) {
                log.warn("本地数据库中未找到向量库: {}, 尝试从AI端同步", name);
                knowledgeBase = new KnowledgeBasePath();
                knowledgeBase.setName(name);
                knowledgeBase.setDisplayName(name);
                knowledgeBase.setPath("/knowledge/" + name);
                knowledgeBase.setDepartment(department);
                knowledgeBase.setDescription(description);
                knowledgeBase.setUploader(userId.toString());
                knowledgeBase.setOwnerType(department != null ? "department" : "personal");
                knowledgeBase.setVisibility("public");
                knowledgeBase.setCreateTime(LocalDateTime.now());
                knowledgeBase.setUpdateTime(LocalDateTime.now());
                knowledgeBasePathMapper.insert(knowledgeBase);
                log.info("已同步向量库到本地数据库: {}", name);
            }

            String url = aiApiConfig.getFullUrl(aiApiConfig.getCollection().getUpdate());
            url = url.replace("{name}", name);
            log.info("调用RAG服务修改向量库: {}", url);

            Map<String, Object> requestBody = new HashMap<>();
            if (displayName != null) {
                requestBody.put("display_name", displayName);
            }
            if (department != null) {
                requestBody.put("department", department);
            }
            if (description != null) {
                requestBody.put("description", description);
            }

            if (requestBody.isEmpty()) {
                return error("至少需要提供一个修改参数");
            }

            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            HttpEntity<Map<String, Object>> entity = new HttpEntity<>(requestBody, headers);

            ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.PUT, entity, String.class);

            if (!response.getStatusCode().is2xxSuccessful()) {
                String errorMsg = response.getBody() != null ? response.getBody() : "";
                log.error("AI服务返回错误: status={}, body={}", response.getStatusCode(), errorMsg);
                return error("修改向量库失败: " + response.getStatusCode());
            }

            if (displayName != null) {
                knowledgeBase.setDisplayName(displayName);
            }
            if (department != null) {
                knowledgeBase.setDepartment(department);
            }
            if (description != null) {
                knowledgeBase.setDescription(description);
            }
            knowledgeBase.setUpdateTime(LocalDateTime.now());

            knowledgeBasePathMapper.updateById(knowledgeBase);

            CollectionVO vo = new CollectionVO();
            vo.setName(knowledgeBase.getName());
            vo.setDisplayName(knowledgeBase.getDisplayName());
            vo.setDepartment(knowledgeBase.getDepartment());
            vo.setDescription(knowledgeBase.getDescription());
            vo.setOwnerType(knowledgeBase.getOwnerType());
            vo.setVisibility(knowledgeBase.getVisibility());
            vo.setUploader(knowledgeBase.getUploader());
            vo.setCreateTime(knowledgeBase.getCreateTime());

            log.info("修改向量库成功: name={}, userId={}", name, userId);

            return success(vo);
        } catch (Exception e) {
            log.error("修改向量库失败", e);
            return error("修改向量库失败: " + e.getMessage());
        }
    }

    @Operation(summary = "删除向量库", description = "删除AI端向量库并从knowledge_base_path表移除")
    @DeleteMapping("/{name}")
    public Result<Void> deleteCollection(
            @Parameter(description = "向量库名称", required = true) @PathVariable String name,
            HttpServletRequest request) {
        try {
            Long userId = getCurrentUserId(request);
            if (userId == null) {
                return error("用户未登录");
            }

            String url = aiApiConfig.getFullUrl(aiApiConfig.getCollection().getDelete());
            url = url.replace("{name}", name);
            log.info("调用RAG服务删除向量库: {}", url);

            ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.DELETE, null, String.class);

            if (!response.getStatusCode().is2xxSuccessful()) {
                String errorMsg = response.getBody() != null ? response.getBody() : "";
                log.error("AI服务返回错误: status={}, body={}", response.getStatusCode(), errorMsg);
                return error("删除向量库失败: " + response.getStatusCode());
            }

            KnowledgeBasePath knowledgeBase = getKnowledgeBasePathByName(name);
            if (knowledgeBase != null) {
                knowledgeBasePathMapper.deleteById(knowledgeBase.getId());
            }

            log.info("删除向量库成功: name={}, userId={}", name, userId);

            return success();
        } catch (Exception e) {
            log.error("删除向量库失败", e);
            return error("删除向量库失败: " + e.getMessage());
        }
    }

    @Operation(summary = "获取向量库切片列表", description = "获取指定向量库的所有文档切片")
    @GetMapping("/{name}/chunks")
    public Result<Map<String, Object>> getCollectionChunks(
            @Parameter(description = "向量库名称", required = true) @PathVariable String name,
            @Parameter(description = "每页数量") @RequestParam(defaultValue = "100") Integer limit,
            @Parameter(description = "偏移量") @RequestParam(defaultValue = "0") Integer offset,
            HttpServletRequest request) {
        try {
            Long userId = getCurrentUserId(request);
            if (userId == null) {
                return error("用户未登录");
            }

            String url = aiApiConfig.getFullUrl(aiApiConfig.getCollection().getChunks());
            url = url.replace("{name}", name) + "?limit=" + limit + "&offset=" + offset;
            log.info("调用RAG服务获取向量库切片: {}", url);

            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> body = objectMapper.readValue(response.getBody(), new com.fasterxml.jackson.core.type.TypeReference<Map<String, Object>>() {});
                log.info("获取向量库切片成功: collection={}", name);
                return success(body);
            }
            log.error("获取向量库切片失败: status={}, body={}", response.getStatusCode(), response.getBody());
            return error("获取向量库切片失败: " + response.getStatusCode());
        } catch (Exception e) {
            log.error("获取向量库切片失败", e);
            return error("获取向量库切片失败: " + e.getMessage());
        }
    }

    @Operation(summary = "文件向量化", description = "将已上传的文件解析并存储到向量库")
    @PostMapping("/{name}/vectorize")
    public Result<Map<String, Object>> vectorizeFile(
            @Parameter(description = "向量库名称", required = true) @PathVariable String name,
            @Parameter(description = "文件ID", required = true) @RequestParam Long fileId,
            HttpServletRequest request) {
        try {
            Long userId = getCurrentUserId(request);
            if (userId == null) {
                return error("用户未登录");
            }

            log.info("触发文件向量化: collection={}, fileId={}, userId={}", name, fileId, userId);

            File file = fileMapper.selectById(fileId);
            if (file == null) {
                log.warn("文件不存在: fileId={}", fileId);
                return error("文件不存在");
            }

            String filePath = file.getFilePath();
            if (filePath == null || filePath.isEmpty()) {
                log.warn("文件路径为空: fileId={}", fileId);
                return error("文件路径为空，请先上传文件");
            }

            Path path = Paths.get(uploadPath, filePath).normalize();
            if (!Files.exists(path)) {
                log.warn("文件不存在于路径: fullPath={}", path.toAbsolutePath());
                return error("文件不存在于服务器: " + filePath);
            }

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

            asyncVectorizeService.vectorizeFileAsync(name, fileId, userId);

            Map<String, Object> result = new HashMap<>();
            result.put("success", true);
            result.put("message", "向量化任务已提交，正在后台处理中");
            result.put("fileId", fileId);
            result.put("fileName", file.getFileName());
            result.put("collection", name);
            result.put("status", AsyncVectorizeService.VECTORIZE_STATUS_VECTORIZING);

            log.info("文件向量化任务已提交: collection={}, fileId={}, fileName={}",
                    name, fileId, file.getFileName());

            return success(result);
        } catch (Exception e) {
            log.error("触发文件向量化失败: collection={}, fileId={}", name, fileId, e);
            return error("触发文件向量化失败: " + e.getMessage());
        }
    }

    @Operation(summary = "批量文件向量化", description = "批量将已上传的文件解析并存储到向量库")
    @PostMapping("/{name}/vectorize/batch")
    public Result<Map<String, Object>> batchVectorizeFiles(
            @Parameter(description = "向量库名称", required = true) @PathVariable String name,
            @Parameter(description = "文件ID列表", required = true) @RequestParam List<Long> fileIds,
            HttpServletRequest request) {
        try {
            Long userId = getCurrentUserId(request);
            if (userId == null) {
                return error("用户未登录");
            }

            log.info("触发批量文件向量化: collection={}, fileIds={}, userId={}", name, fileIds, userId);

            List<Map<String, Object>> submittedFiles = new ArrayList<>();
            List<Map<String, Object>> failedFiles = new ArrayList<>();
            String batchId = "BATCH_" + System.currentTimeMillis();

            for (Long fileId : fileIds) {
                try {
                    File file = fileMapper.selectById(fileId);
                    if (file == null) {
                        Map<String, Object> failed = new HashMap<>();
                        failed.put("fileId", fileId);
                        failed.put("error", "文件不存在");
                        failedFiles.add(failed);
                        continue;
                    }

                    String filePath = file.getFilePath();
                    if (filePath == null || filePath.isEmpty()) {
                        Map<String, Object> failed = new HashMap<>();
                        failed.put("fileId", fileId);
                        failed.put("error", "文件路径为空");
                        failedFiles.add(failed);
                        continue;
                    }

                    Path path = Paths.get(uploadPath, filePath).normalize();
                    if (!Files.exists(path)) {
                        Map<String, Object> failed = new HashMap<>();
                        failed.put("fileId", fileId);
                        failed.put("error", "文件不存在于服务器");
                        failedFiles.add(failed);
                        continue;
                    }

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

                    asyncVectorizeService.batchVectorizeFilesAsync(name, fileId, userId, batchId);

                    Map<String, Object> submitted = new HashMap<>();
                    submitted.put("fileId", fileId);
                    submitted.put("fileName", file.getFileName());
                    submitted.put("collection", name);
                    submitted.put("status", AsyncVectorizeService.VECTORIZE_STATUS_VECTORIZING);
                    submitted.put("batchId", batchId);
                    submittedFiles.add(submitted);

                    log.info("批量向量化任务已提交: collection={}, fileId={}, fileName={}, batchId={}",
                            name, fileId, file.getFileName(), batchId);

                } catch (Exception e) {
                    log.error("提交文件向量化失败: fileId={}", fileId, e);
                    Map<String, Object> failed = new HashMap<>();
                    failed.put("fileId", fileId);
                    failed.put("error", e.getMessage());
                    failedFiles.add(failed);
                }
            }

            Map<String, Object> result = new HashMap<>();
            result.put("success", true);
            result.put("collection", name);
            result.put("batchId", batchId);
            result.put("submittedFiles", submittedFiles);
            result.put("failedFiles", failedFiles);
            result.put("total", fileIds.size());
            result.put("submittedCount", submittedFiles.size());
            result.put("failedCount", failedFiles.size());

            String message = String.format("批量向量化任务已提交: 提交 %d 个, 无效 %d 个（批次ID: %s）",
                    submittedFiles.size(), failedFiles.size(), batchId);
            log.info(message);

            return success(result);
        } catch (Exception e) {
            log.error("触发批量文件向量化失败: collection={}", name, e);
            return error("触发批量文件向量化失败: " + e.getMessage());
        }
    }

    @Operation(summary = "获取文档列表", description = "从本地数据库获取指定向量库中的文档列表")
    @GetMapping("/{name}/documents")
    public Result<List<Map<String, Object>>> getDocuments(
            @Parameter(description = "向量库名称", required = true) @PathVariable String name,
            HttpServletRequest request) {
        try {
            Long userId = getCurrentUserId(request);
            if (userId == null) {
                return error("用户未登录");
            }

            log.info("从本地数据库获取文档列表: collection={}", name);

            List<CollectionFile> collectionFiles = collectionFileService.findByCollectionName(name);
            List<Map<String, Object>> documents = new ArrayList<>();

            for (CollectionFile cf : collectionFiles) {
                Map<String, Object> doc = new HashMap<>();
                doc.put("name", cf.getDocName());
                doc.put("type", cf.getDocType());
                doc.put("path", cf.getFilePath());
                doc.put("fileId", cf.getFileId());
                doc.put("status", cf.getStatus());
                doc.put("createTime", cf.getCreateTime());
                documents.add(doc);
            }

            log.info("获取文档列表成功: collection={}, count={}", name, documents.size());
            return success(documents);
        } catch (Exception e) {
            log.error("获取文档列表失败", e);
            return error("获取文档列表失败: " + e.getMessage());
        }
    }

    @Operation(summary = "删除文档", description = "从向量库中删除指定文档，同步更新本地数据库")
    @DeleteMapping("/{name}/documents/{filename}")
    public Result<Void> deleteDocument(
            @Parameter(description = "向量库名称", required = true) @PathVariable String name,
            @Parameter(description = "文档文件名", required = true) @PathVariable String filename,
            @Parameter(description = "文件ID（优先使用，避免同名文件删除错误）") @RequestParam(required = false) Long fileId,
            HttpServletRequest request) {
        try {
            Long userId = getCurrentUserId(request);
            if (userId == null) {
                return error("用户未登录");
            }

            String url = aiApiConfig.getFullUrl(aiApiConfig.getDocument().getDelete());
            url = url + "/" + name + "/" + filename;
            log.info("调用RAG服务删除文档: {}", url);

            ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.DELETE, null, String.class);

            if (!response.getStatusCode().is2xxSuccessful()) {
                String errorMsg = response.getBody() != null ? response.getBody() : "";
                log.error("AI服务返回错误: status={}, body={}", response.getStatusCode(), errorMsg);
                return error("删除文档失败: " + response.getStatusCode());
            }

            CollectionFile cf = null;
            if (fileId != null) {
                cf = collectionFileService.findByFileId(fileId);
                log.info("通过fileId查找collection_file: fileId={}, result={}", fileId, cf != null ? "找到" : "未找到");
            }
            
            if (cf == null) {
                cf = collectionFileService.findByCollectionAndDocName(name, filename);
                log.info("通过collection+filename查找collection_file: name={}, filename={}, result={}", name, filename, cf != null ? "找到" : "未找到");
            }
            
            if (cf != null) {
                collectionFileService.delete(cf.getId());
                log.info("本地数据库文档关联已删除: collection={}, filename={}, fileId={}", name, filename, cf.getFileId());
            }

            log.info("删除文档成功: collection={}, filename={}, fileId={}, userId={}", name, filename, fileId, userId);

            triggerSync();

            return success();
        } catch (Exception e) {
            log.error("删除文档失败", e);
            return error("删除文档失败: " + e.getMessage());
        }
    }

    @Operation(summary = "废止文档", description = "将文档标记为废止状态，同步更新本地数据库")
    @PostMapping("/{name}/documents/{filename}/deprecate")
    public Result<Map<String, Object>> deprecateDocument(
            @Parameter(description = "向量库名称", required = true) @PathVariable String name,
            @Parameter(description = "文档文件名", required = true) @PathVariable String filename,
            @Parameter(description = "废止原因") @RequestParam(required = false) String reason,
            HttpServletRequest request) {
        try {
            Long userId = getCurrentUserId(request);
            if (userId == null) {
                return error("用户未登录");
            }

            String url = aiApiConfig.getFullUrl(aiApiConfig.getDocument().getDeprecate());
            url = url.replace("{name}", name).replace("{filename}", filename);
            log.info("调用RAG服务废止文档: {}", url);

            Map<String, Object> requestBody = new HashMap<>();
            if (reason != null && !reason.isEmpty()) {
                requestBody.put("reason", reason);
            }

            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            HttpEntity<Map<String, Object>> entity = new HttpEntity<>(requestBody, headers);

            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> body = objectMapper.readValue(response.getBody(), new com.fasterxml.jackson.core.type.TypeReference<Map<String, Object>>() {});
                
                CollectionFile cf = collectionFileService.findByCollectionAndDocName(name, filename);
                if (cf != null) {
                    cf.setStatus(0);
                    collectionFileService.saveOrUpdate(name, filename, cf.getFileId(), cf.getFilePath(), cf.getDocType());
                    log.info("本地数据库文档已标记为废止: collection={}, filename={}", name, filename);
                }
                
                log.info("废止文档成功: collection={}, filename={}, userId={}", name, filename, userId);
                return success(body);
            }
            String errorMsg = response.getBody() != null ? response.getBody() : "";
            log.error("AI服务返回错误: status={}, body={}", response.getStatusCode(), errorMsg);
            return error("废止文档失败: " + response.getStatusCode());
        } catch (Exception e) {
            log.error("废止文档失败", e);
            return error("废止文档失败: " + e.getMessage());
        }
    }

    @Operation(summary = "恢复已废止文档", description = "将已废止的文档恢复为正常状态，同步更新本地数据库")
    @PostMapping("/{name}/documents/{filename}/restore")
    public Result<Map<String, Object>> restoreDocument(
            @Parameter(description = "向量库名称", required = true) @PathVariable String name,
            @Parameter(description = "文档文件名", required = true) @PathVariable String filename,
            HttpServletRequest request) {
        try {
            Long userId = getCurrentUserId(request);
            if (userId == null) {
                return error("用户未登录");
            }

            String url = aiApiConfig.getFullUrl(aiApiConfig.getDocument().getRestore());
            url = url.replace("{name}", name).replace("{filename}", filename);
            log.info("调用RAG服务恢复文档: {}", url);

            ResponseEntity<String> response = restTemplate.postForEntity(url, null, String.class);

            if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
                com.fasterxml.jackson.databind.ObjectMapper objectMapper = new com.fasterxml.jackson.databind.ObjectMapper();
                Map<String, Object> body = objectMapper.readValue(response.getBody(), new com.fasterxml.jackson.core.type.TypeReference<Map<String, Object>>() {});
                
                CollectionFile cf = collectionFileService.findByCollectionAndDocName(name, filename);
                if (cf != null) {
                    cf.setStatus(1);
                    collectionFileService.saveOrUpdate(name, filename, cf.getFileId(), cf.getFilePath(), cf.getDocType());
                    log.info("本地数据库文档已恢复: collection={}, filename={}", name, filename);
                }
                
                log.info("恢复文档成功: collection={}, filename={}, userId={}", name, filename, userId);
                return success(body);
            }
            String errorMsg = response.getBody() != null ? response.getBody() : "";
            log.error("AI服务返回错误: status={}, body={}", response.getStatusCode(), errorMsg);
            return error("恢复文档失败: " + response.getStatusCode());
        } catch (Exception e) {
            log.error("恢复文档失败", e);
            return error("恢复文档失败: " + e.getMessage());
        }
    }

    @Operation(summary = "获取文档版本历史", description = "获取文档的版本历史记录")
    @GetMapping("/{name}/documents/{filename}/versions")
    public Result<Map<String, Object>> getDocumentVersions(
            @Parameter(description = "向量库名称", required = true) @PathVariable String name,
            @Parameter(description = "文档文件名", required = true) @PathVariable String filename,
            @Parameter(description = "返回数量限制") @RequestParam(defaultValue = "10") Integer limit,
            HttpServletRequest request) {
        try {
            Long userId = getCurrentUserId(request);
            if (userId == null) {
                return error("用户未登录");
            }

            String url = aiApiConfig.getFullUrl(aiApiConfig.getDocument().getVersions());
            url = url.replace("{name}", name).replace("{filename}", filename) + "?limit=" + limit;
            log.info("调用RAG服务获取文档版本历史: {}", url);

            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> body = objectMapper.readValue(response.getBody(), new com.fasterxml.jackson.core.type.TypeReference<Map<String, Object>>() {});
                log.info("获取文档版本历史成功: collection={}, filename={}", name, filename);
                return success(body);
            }
            log.error("获取文档版本历史失败: status={}, body={}", response.getStatusCode(), response.getBody());
            return error("获取文档版本历史失败: " + response.getStatusCode());
        } catch (Exception e) {
            log.error("获取文档版本历史失败", e);
            return error("获取文档版本历史失败: " + e.getMessage());
        }
    }

    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());

            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(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);

            HttpEntity<LinkedMultiValueMap<String, Object>> entity = new HttpEntity<>(body, headers);

            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;
    }

    @Operation(summary = "获取文档切片列表", description = "获取指定文档的所有切片内容")
    @GetMapping("/{name}/documents/{filename}/chunks")
    public Result<Map<String, Object>> getDocumentChunks(
            @Parameter(description = "向量库名称", required = true) @PathVariable String name,
            @Parameter(description = "文档文件名", required = true) @PathVariable String filename,
            @Parameter(description = "页码") @RequestParam(defaultValue = "1") Integer page,
            @Parameter(description = "每页数量") @RequestParam(defaultValue = "20") Integer pageSize,
            HttpServletRequest request) {
        try {
            Long userId = getCurrentUserId(request);
            if (userId == null) {
                return error("用户未登录");
            }

            String url = aiApiConfig.getFullUrl(aiApiConfig.getChunk().getDocumentChunks());
            url = url.replace("{path}", name + "/" + filename) + "?page=" + page + "&page_size=" + pageSize;
            log.info("调用RAG服务获取文档切片列表: {}", url);

            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> body = objectMapper.readValue(response.getBody(), new com.fasterxml.jackson.core.type.TypeReference<Map<String, Object>>() {});
                log.info("获取文档切片列表成功: collection={}, filename={}", name, filename);
                return success(body);
            }
            log.error("获取文档切片列表失败: status={}, body={}", response.getStatusCode(), response.getBody());
            return error("获取文档切片列表失败: " + response.getStatusCode());
        } catch (Exception e) {
            log.error("获取文档切片列表失败", e);
            return error("获取文档切片列表失败: " + e.getMessage());
        }
    }

    @Operation(summary = "新增切片", description = "在指定向量库中创建新的切片")
    @PostMapping("/{name}/chunks")
    public Result<Map<String, Object>> createChunk(
            @Parameter(description = "向量库名称", required = true) @PathVariable String name,
            @Parameter(description = "切片内容", required = true) @RequestParam String content,
            @Parameter(description = "章节信息") @RequestParam(required = false) String section,
            HttpServletRequest request) {
        try {
            Long userId = getCurrentUserId(request);
            if (userId == null) {
                return error("用户未登录");
            }

            if (content == null || content.isEmpty()) {
                return error("切片内容不能为空");
            }

            String url = aiApiConfig.getFullUrl(aiApiConfig.getChunk().getCreate());
            log.info("调用RAG服务新增切片: {}", url);

            Map<String, Object> requestBody = new HashMap<>();
            requestBody.put("collection", name);
            requestBody.put("content", content);
            if (section != null && !section.isEmpty()) {
                Map<String, Object> metadata = new HashMap<>();
                metadata.put("section", section);
                requestBody.put("metadata", metadata);
            }

            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            HttpEntity<Map<String, Object>> entity = new HttpEntity<>(requestBody, headers);

            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> body = objectMapper.readValue(response.getBody(), new com.fasterxml.jackson.core.type.TypeReference<Map<String, Object>>() {});
                log.info("新增切片成功: collection={}, userId={}", name, userId);
                return success(body);
            }
            String errorMsg = response.getBody() != null ? response.getBody() : "";
            log.error("AI服务返回错误: status={}, body={}", response.getStatusCode(), errorMsg);
            return error("新增切片失败: " + response.getStatusCode());
        } catch (Exception e) {
            log.error("新增切片失败", e);
            return error("新增切片失败: " + e.getMessage());
        }
    }

    @Operation(summary = "修改切片", description = "更新指定切片的内容或元数据")
    @PutMapping("/{name}/chunks/{chunkId}")
    public Result<Map<String, Object>> updateChunk(
            @Parameter(description = "向量库名称", required = true) @PathVariable String name,
            @Parameter(description = "切片ID", required = true) @PathVariable String chunkId,
            @Parameter(description = "新内容") @RequestParam(required = false) String content,
            @Parameter(description = "章节信息") @RequestParam(required = false) String section,
            HttpServletRequest request) {
        try {
            Long userId = getCurrentUserId(request);
            if (userId == null) {
                return error("用户未登录");
            }

            if (content == null && section == null) {
                return error("至少需要提供内容或章节信息");
            }

            String url = aiApiConfig.getFullUrl(aiApiConfig.getChunk().getUpdate());
            url = url.replace("{id}", chunkId);
            log.info("调用RAG服务修改切片: {}", url);

            Map<String, Object> requestBody = new HashMap<>();
            requestBody.put("collection", name);
            if (content != null && !content.isEmpty()) {
                requestBody.put("content", content);
            }
            if (section != null && !section.isEmpty()) {
                Map<String, Object> metadata = new HashMap<>();
                metadata.put("section", section);
                requestBody.put("metadata", metadata);
            }

            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            HttpEntity<Map<String, Object>> entity = new HttpEntity<>(requestBody, headers);

            ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.PUT, entity, String.class);

            if (response.getStatusCode().is2xxSuccessful()) {
                Map<String, Object> result = new HashMap<>();
                result.put("success", true);
                result.put("message", "切片更新成功");
                result.put("chunkId", chunkId);
                log.info("修改切片成功: chunkId={}, userId={}", chunkId, userId);
                return success(result);
            }
            String errorMsg = response.getBody() != null ? response.getBody() : "";
            log.error("AI服务返回错误: status={}, body={}", response.getStatusCode(), errorMsg);
            return error("修改切片失败: " + response.getStatusCode());
        } catch (Exception e) {
            log.error("修改切片失败", e);
            return error("修改切片失败: " + e.getMessage());
        }
    }

    @Operation(summary = "删除切片", description = "从向量库中删除指定切片")
    @DeleteMapping("/{name}/chunks/{chunkId}")
    public Result<Void> deleteChunk(
            @Parameter(description = "向量库名称", required = true) @PathVariable String name,
            @Parameter(description = "切片ID", required = true) @PathVariable String chunkId,
            HttpServletRequest request) {
        try {
            Long userId = getCurrentUserId(request);
            if (userId == null) {
                return error("用户未登录");
            }

            String url = aiApiConfig.getFullUrl(aiApiConfig.getChunk().getDelete());
            url = url.replace("{id}", chunkId);
            log.info("调用RAG服务删除切片: {}", url);

            ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.DELETE, null, String.class);

            if (response.getStatusCode().is2xxSuccessful()) {
                log.info("删除切片成功: chunkId={}, userId={}", chunkId, userId);
                return success();
            }
            String errorMsg = response.getBody() != null ? response.getBody() : "";
            log.error("AI服务返回错误: status={}, body={}", response.getStatusCode(), errorMsg);
            return error("删除切片失败: " + response.getStatusCode());
        } catch (Exception e) {
            log.error("删除切片失败", e);
            return error("删除切片失败: " + e.getMessage());
        }
    }

    private KnowledgeBasePath getKnowledgeBasePathByName(String name) {
        Map<String, Object> params = new HashMap<>();
        params.put("name", name);
        List<KnowledgeBasePath> list = knowledgeBasePathMapper.selectByMap(params);
        return list.isEmpty() ? null : list.get(0);
    }
}