package top.tqx.demo_1.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;
import top.tqx.demo_1.common.PageResult;
import top.tqx.demo_1.config.AiApiConfig;
import top.tqx.demo_1.constant.CommonConstant;
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.entity.User;
import top.tqx.demo_1.enums.FileProcessStepStatus;
import top.tqx.demo_1.mapper.FileMapper;
import top.tqx.demo_1.mapper.KnowledgeBasePathMapper;
import top.tqx.demo_1.mapper.UserMapper;
import top.tqx.demo_1.service.AsyncVectorizeService;
import top.tqx.demo_1.service.CollectionFileService;
import top.tqx.demo_1.service.DepartmentService;
import top.tqx.demo_1.service.FileService;
import top.tqx.demo_1.service.KnowledgeBasePathService;
import top.tqx.demo_1.service.UserService;
import top.tqx.demo_1.vo.FileVO;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
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.stream.Collectors;

@Slf4j
@Service
public class FileServiceImpl extends ServiceImpl<FileMapper, File> implements FileService {

    @Autowired
    private DepartmentService departmentService;

    @Autowired
    private AsyncVectorizeService asyncVectorizeService;

    @Autowired
    private KnowledgeBasePathMapper knowledgeBasePathMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private CollectionFileService collectionFileService;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private AiApiConfig aiApiConfig;

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

    private final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public PageResult<FileVO> pageFiles(Integer pageNum, Integer pageSize, String fileName, top.tqx.demo_1.entity.User user) {
        Page<File> page = new Page<>(pageNum, pageSize);
        LambdaQueryWrapper<File> wrapper = new LambdaQueryWrapper<>();

        if (fileName != null && !fileName.isEmpty()) {
            wrapper.like(File::getFileName, fileName);
        }

        Integer userType = user.getUserType();
        Long currentUserId = user.getId();
        Long currentDeptId = user.getDeptId();

        if (userType.equals(CommonConstant.RoleType.SUPER_ADMIN)) {
            wrapper.eq(File::getStatus, CommonConstant.STATUS_ENABLE);
        } else if (userType.equals(CommonConstant.RoleType.DEPT_ADMIN)) {
            wrapper.eq(File::getStatus, CommonConstant.STATUS_ENABLE);
            wrapper.and(w -> w.eq(File::getDeptId, currentDeptId)
                    .or().eq(File::getUploadUserId, currentUserId));
        } else if (userType.equals(CommonConstant.RoleType.NORMAL_USER)) {
            wrapper.eq(File::getStatus, CommonConstant.STATUS_ENABLE);
            wrapper.ne(File::getAuditStatus, 0);
            wrapper.and(w -> w.eq(File::getDeptId, currentDeptId)
                    .or().eq(File::getUploadUserId, currentUserId));
        }

        wrapper.orderByDesc(File::getCreateTime);

        Page<File> filePage = this.page(page, wrapper);

        List<FileVO> voList = filePage.getRecords().stream()
                .map(this::convertToVO)
                .collect(Collectors.toList());

        return PageResult.of(voList, filePage.getTotal(), filePage.getSize(), filePage.getCurrent());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public FileVO uploadFile(MultipartFile file, Long userId, Long deptId, Integer userType, Long targetDeptId, Boolean isPublic) {
        String originalFilename = file.getOriginalFilename();
        if (originalFilename == null || originalFilename.isEmpty()) {
            throw new RuntimeException("文件名不能为空");
        }

        try {
            byte[] fileContent = file.getBytes();

            String fileNameWithoutExt = originalFilename.contains(".")
                    ? originalFilename.substring(0, originalFilename.lastIndexOf("."))
                    : originalFilename;
            String extension = originalFilename.contains(".")
                    ? originalFilename.substring(originalFilename.lastIndexOf("."))
                    : "";

            boolean needAudit = shouldRequireAudit(userType, isPublic, targetDeptId);

            Path basePath = determineUploadPath(userType, userId, deptId, targetDeptId, fileNameWithoutExt, isPublic, needAudit);

            if (!Files.exists(basePath)) {
                Files.createDirectories(basePath);
            }

            int version = findAvailableVersion(basePath);
            Path versionPath = basePath.resolve("v" + version);
            Files.createDirectories(versionPath);

            Path filePath = versionPath.resolve(originalFilename);
            Files.write(filePath, fileContent);

            String relativePath = buildRelativePath(filePath);

            File fileEntity = new File();
            fileEntity.setFileName(originalFilename);
            fileEntity.setFilePath(relativePath);
            fileEntity.setFileSize((long) fileContent.length);
            String contentType = file.getContentType();
            if (contentType != null && contentType.length() > 50) {
                contentType = contentType.substring(0, 50);
            }
            fileEntity.setFileType(contentType);
            fileEntity.setExtension(extension);
            fileEntity.setFileMd5(null);
            fileEntity.setUploadUserId(userId);

            Long finalDeptId = determineDeptId(userType, deptId, targetDeptId);
            fileEntity.setDeptId(finalDeptId);

            Integer isPublicValue = determineIsPublicValue(userType, isPublic, needAudit);
            fileEntity.setIsPublic(isPublicValue);

            String fileCategory = determineFileCategory(userType, targetDeptId, isPublic);
            fileEntity.setFileCategory(fileCategory);

            fileEntity.setStatus(CommonConstant.STATUS_ENABLE);

            Integer auditStatus = needAudit ? 0 : 1;
            fileEntity.setAuditStatus(auditStatus);

            fileEntity.setCreateTime(LocalDateTime.now());
            fileEntity.setProcessStatus("PENDING");
            fileEntity.setExamStatus("UNGENERATED");
            fileEntity.setProcessStepStatus(FileProcessStepStatus.UPLOADED.getCode());
            fileEntity.setProcessStepMessage(FileProcessStepStatus.UPLOADED.getDescription());

            boolean saved = this.save(fileEntity);
            if (!saved) {
                throw new RuntimeException("文件记录保存到数据库失败");
            }

            log.info("文件上传成功: fileName={}, userId={}, deptId={}, isPublic={}, category={}, auditStatus={}, path={}",
                    originalFilename, userId, finalDeptId, isPublicValue, fileCategory, auditStatus, relativePath);

            if (!needAudit) {
                String collectionName = determineCollectionName(userType, finalDeptId, isPublic);
                if (collectionName != null) {
                    checkAndDeleteOldVectorization(originalFilename, finalDeptId, collectionName);
                    
                    log.info("文件上传完成，准备触发异步向量化: fileId={}, collection={}", fileEntity.getId(), collectionName);
                    asyncVectorizeService.vectorizeFileAsync(collectionName, fileEntity.getId(), userId);
                } else {
                    log.warn("无法确定collection名称，跳过向量化: fileId={}, userType={}, deptId={}, isPublic={}",
                            fileEntity.getId(), userType, finalDeptId, isPublic);
                }
            } else {
                log.info("文件需要审核，暂不向量化: fileId={}", fileEntity.getId());
            }

            return convertToVO(fileEntity);
        } catch (IOException e) {
            log.error("文件IO操作失败", e);
            throw new RuntimeException("文件上传失败: " + e.getMessage(), e);
        } catch (Exception e) {
            log.error("文件上传异常", e);
            throw new RuntimeException("文件上传失败: " + e.getMessage(), e);
        }
    }

    private void checkAndDeleteOldVectorization(String fileName, Long deptId, String collectionName) {
        try {
            LambdaQueryWrapper<File> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(File::getFileName, fileName)
                   .eq(File::getDeptId, deptId)
                   .eq(File::getStatus, CommonConstant.STATUS_ENABLE)
                   .ne(File::getVectorDbAddress, (Object) null)
                   .orderByDesc(File::getCreateTime);
            
            List<File> oldFiles = this.list(wrapper);
            
            if (oldFiles != null && !oldFiles.isEmpty()) {
                for (File oldFile : oldFiles) {
                    log.info("发现同名旧文件，准备删除其向量化数据: fileId={}, fileName={}, vectorDbAddress={}",
                            oldFile.getId(), oldFile.getFileName(), oldFile.getVectorDbAddress());
                    deleteDocumentFromRAG(oldFile.getVectorDbAddress());
                    log.info("已删除旧文件向量化数据: fileId={}", oldFile.getId());
                }
            }
        } catch (Exception e) {
            log.error("检查并删除旧文件向量化数据失败: fileName={}, deptId={}", fileName, deptId, e);
        }
    }

    private boolean shouldRequireAudit(Integer userType, Boolean isPublic, Long targetDeptId) {
        if (userType == 3 && isPublic != null && isPublic && targetDeptId == null) {
            return true;
        }
        return false;
    }

    private Path determineUploadPath(Integer userType, Long userId, Long deptId, Long targetDeptId,
                                     String fileNameWithoutExt, Boolean isPublic, boolean needAudit) {
        if (userType == 1 || userType == 2) {
            Long finalDeptId = targetDeptId != null ? targetDeptId : deptId;
            String fullPath = departmentService.getDepartmentFullPath(finalDeptId);
            if (fullPath != null && !fullPath.isEmpty()) {
                return Paths.get(uploadPath, "resources", "rule", fullPath, fileNameWithoutExt);
            } else {
                return Paths.get(uploadPath, "resources", "rule", "默认部门", fileNameWithoutExt);
            }
        } else {
            String username = getUserUsername(userId);
            if (needAudit) {
                return Paths.get(uploadPath, "resources", "user", username, "pending", fileNameWithoutExt);
            }
            String privacyPath = (isPublic != null && isPublic) ? "public" : "private";
            return Paths.get(uploadPath, "resources", "user", username, privacyPath, fileNameWithoutExt);
        }
    }

    private Integer determineIsPublicValue(Integer userType, Boolean isPublic, boolean needAudit) {
        if (needAudit) {
            return 0;
        }
        return (isPublic != null && isPublic) ? 1 : 0;
    }

    private int findAvailableVersion(Path basePath) {
        int version = 1;
        while (Files.exists(basePath.resolve("v" + version))) {
            version++;
        }
        return version;
    }

    private String buildRelativePath(Path filePath) {
        String relativePath = filePath.toString().replace(uploadPath, "");
        if (relativePath.startsWith(java.io.File.separator)) {
            relativePath = relativePath.substring(1);
        }
        if (relativePath.startsWith("/")) {
            relativePath = relativePath.substring(1);
        }
        if (relativePath.startsWith("\\")) {
            relativePath = relativePath.substring(1);
        }
        return relativePath.replace("\\", "/");
    }

    private Long determineDeptId(Integer userType, Long deptId, Long targetDeptId) {
        if (userType == 1 || userType == 2) {
            return targetDeptId != null ? targetDeptId : deptId;
        }
        return deptId;
    }

    private String determineFileCategory(Integer userType, Long targetDeptId, Boolean isPublic) {
        if (targetDeptId != null) {
            return "rule";
        }
        if (userType == 3 && Boolean.TRUE.equals(isPublic)) {
            return "rule";
        }
        return "personal";
    }

    private Department getDepartmentByIdSafe(Long deptId) {
        if (deptId == null) {
            return null;
        }
        try {
            return departmentService.getDepartmentById(deptId);
        } catch (Exception e) {
            log.warn("获取部门信息失败: deptId={}", deptId, e);
            return null;
        }
    }

    private String getUserUsername(Long userId) {
        try {
            User user = userMapper.selectById(userId);
            if (user != null && user.getUsername() != null && !user.getUsername().isEmpty()) {
                log.debug("获取用户名成功: userId={}, username={}", userId, user.getUsername());
                return user.getUsername();
            }
        } catch (Exception e) {
            log.warn("获取用户名失败，使用默认格式: userId={}", userId, e);
        }
        return "user_" + userId;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public FileVO uploadStreamFromBackend(InputStream inputStream, String fileName, String fileType, Long userId, Long deptId) throws Exception {
        if (fileName == null || fileName.isEmpty()) {
            throw new RuntimeException("文件名不能为空");
        }

        String fullPath;
        if (deptId != null) {
            fullPath = departmentService.getDepartmentFullPath(deptId);
            if (fullPath == null || fullPath.isEmpty()) {
                throw new RuntimeException("部门路径获取失败");
            }
        } else {
            fullPath = "默认部门";
        }

        try {
            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
            byte[] data = new byte[8192];
            int nRead;
            long totalSize = 0;

            while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
                buffer.write(data, 0, nRead);
                totalSize += nRead;
            }
            buffer.flush();

            byte[] fileContent = buffer.toByteArray();

            String fileNameWithoutExt = fileName.contains(".") ? fileName.substring(0, fileName.lastIndexOf(".")) : fileName;
            String extension = fileName.contains(".") ? fileName.substring(fileName.lastIndexOf(".")) : "";

            Path basePath = Paths.get(uploadPath, "resources", "rule", fullPath, fileNameWithoutExt);

            if (!Files.exists(basePath)) {
                Files.createDirectories(basePath);
            }

            int version = 1;
            while (Files.exists(basePath.resolve("v" + version))) {
                version++;
            }

            Path versionPath = basePath.resolve("v" + version);
            Files.createDirectories(versionPath);
            Path filePath = versionPath.resolve(fileName);
            Files.write(filePath, fileContent);

            String relativePath = buildRelativePath(filePath);

            File fileEntity = new File();
            fileEntity.setFileName(fileName);
            fileEntity.setFilePath(relativePath);
            fileEntity.setFileSize(totalSize);
            fileEntity.setFileType(fileType);
            fileEntity.setExtension(extension);
            fileEntity.setUploadUserId(userId);
            fileEntity.setDeptId(deptId);
            fileEntity.setIsPublic(1);
            fileEntity.setFileCategory("rule");
            fileEntity.setStatus(CommonConstant.STATUS_ENABLE);
            fileEntity.setAuditStatus(1);
            fileEntity.setCreateTime(LocalDateTime.now());
            fileEntity.setProcessStatus("INDEXED");
            fileEntity.setExamStatus("UNGENERATED");

            boolean saved = this.save(fileEntity);
            if (!saved) {
                throw new RuntimeException("文件记录保存到数据库失败");
            }

            log.info("流式文件上传成功: {}, 大小: {} bytes", fileName, totalSize);
            return convertToVO(fileEntity);
        } catch (IOException e) {
            log.error("流式文件IO操作失败", e);
            throw new RuntimeException("流式文件上传失败: " + e.getMessage(), e);
        } catch (Exception e) {
            log.error("流式文件上传异常", e);
            throw e;
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteFile(Long id) {
        top.tqx.demo_1.entity.File file = this.getById(id);
        if (file == null) {
            throw new RuntimeException("文件不存在");
        }

        try {
            if (file.getVectorDbAddress() != null && !file.getVectorDbAddress().isEmpty()) {
                log.info("文件已向量化，准备删除向量库中的文档: fileId={}, vectorDbAddress={}", id, file.getVectorDbAddress());
                deleteDocumentFromRAG(file.getVectorDbAddress(), id);
            }
        } catch (Exception e) {
            log.error("删除向量库文档失败，但继续删除本地文件: fileId={}, error={}", id, e.getMessage(), e);
        }

        try {
            Path filePath = Paths.get(uploadPath, file.getFilePath());
            Files.deleteIfExists(filePath);
        } catch (IOException e) {
            throw new RuntimeException("删除文件失败", e);
        }

        file.setStatus(CommonConstant.STATUS_DISABLE);
        file.setUpdateTime(LocalDateTime.now());
        this.updateById(file);
        
        log.info("文件删除成功: fileId={}, fileName={}", id, file.getFileName());
    }

    private void deleteDocumentFromRAG(String vectorDbAddress) {
        deleteDocumentFromRAG(vectorDbAddress, null);
    }

    private void deleteDocumentFromRAG(String vectorDbAddress, Long fileId) {
        try {
            String[] parts = vectorDbAddress.split("/");
            if (parts.length < 2) {
                log.warn("向量库地址格式错误: {}", vectorDbAddress);
                return;
            }
            
            String collectionName = parts[0];
            String filename = parts[1];
            
            String url = aiApiConfig.getFullUrl(aiApiConfig.getDocument().getDelete());
            url = url + "/" + collectionName + "/" + filename;
            
            if (fileId != null) {
                url = url + "?fileId=" + fileId;
            }
            
            log.info("调用RAG服务删除文档: collection={}, filename={}, fileId={}, url={}", collectionName, filename, fileId, url);
            
            ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.DELETE, null, String.class);
            
            if (response.getStatusCode().is2xxSuccessful()) {
                log.info("向量库文档删除成功: collection={}, filename={}, fileId={}", collectionName, filename, fileId);
            } else {
                String errorMsg = response.getBody() != null ? response.getBody() : "";
                log.warn("向量库文档删除失败: status={}, body={}", response.getStatusCode(), errorMsg);
            }
        } catch (Exception e) {
            log.error("删除向量库文档异常: vectorDbAddress={}, fileId={}", vectorDbAddress, fileId, e);
        }
    }

    @Override
    public FileVO getFileById(Long id, top.tqx.demo_1.entity.User user) {
        top.tqx.demo_1.entity.File file = this.getById(id);
        if (file == null) {
            throw new RuntimeException("文件不存在");
        }

        checkFilePermission(file, user);

        return convertToVO(file);
    }

    @Override
    public byte[] getFileContent(Long id, top.tqx.demo_1.entity.User user) {
        top.tqx.demo_1.entity.File file = this.getById(id);
        if (file == null) {
            throw new RuntimeException("文件不存在");
        }

        checkFilePermission(file, user);

        try {
            String dbFilePath = file.getFilePath();

            if (dbFilePath == null || dbFilePath.isEmpty()) {
                log.error("文件路径为空: fileId={}", id);
                throw new RuntimeException("文件路径为空");
            }

            String cleanedPath = cleanFilePath(dbFilePath);

            Path fullPath = Paths.get(uploadPath, cleanedPath).normalize();

            log.info("尝试读取文件: fileId={}, fileName={}, dbPath={}, cleanedPath={}, uploadPath={}, fullPath={}",
                    id, file.getFileName(), dbFilePath, cleanedPath, uploadPath, fullPath.toAbsolutePath());

            if (!Files.exists(fullPath)) {
                log.error("文件物理路径不存在: fileId={}, fileName={}, dbFilePath={}, cleanedPath={}, fullPath={}",
                        id, file.getFileName(), dbFilePath, cleanedPath, fullPath.toAbsolutePath());

                Path parentDir = fullPath.getParent();
                if (parentDir != null && Files.exists(parentDir)) {
                    log.error("父目录存在，列出目录内容: {}", parentDir.toAbsolutePath());
                    try {
                        Files.list(parentDir).forEach(p -> log.error("  - {}", p.getFileName()));
                    } catch (Exception e) {
                        log.error("无法列出目录内容", e);
                    }
                } else {
                    log.error("父目录也不存在: {}", parentDir != null ? parentDir.toAbsolutePath() : "null");
                }

                throw new RuntimeException("文件不存在: " + file.getFileName());
            }

            long fileSize = Files.size(fullPath);
            if (fileSize > 50 * 1024 * 1024) {
                throw new RuntimeException("文件过大，无法预览（最大50MB）");
            }

            byte[] content = Files.readAllBytes(fullPath);
            log.info("读取文件成功: fileId={}, fileName={}, size={} bytes",
                    id, file.getFileName(), content.length);
            return content;
        } catch (RuntimeException e) {
            throw e;
        } catch (IOException e) {
            log.error("读取文件内容失败: fileId={}, filePath={}", id, file.getFilePath(), e);
            throw new RuntimeException("读取文件内容失败: " + e.getMessage(), e);
        }
    }

    private String cleanFilePath(String filePath) {
        if (filePath == null || filePath.isEmpty()) {
            return filePath;
        }

        String cleaned = filePath;

        cleaned = cleaned.replaceFirst("^\\.\\\\", "");
        cleaned = cleaned.replaceFirst("^\\./", "");
        cleaned = cleaned.replaceFirst("^\\\\", "");
        cleaned = cleaned.replaceFirst("^/", "");

        cleaned = cleaned.replace("\\\\", "\\");
        cleaned = cleaned.replace("//", "/");

        cleaned = cleaned.replace("\\", "/");

        if (cleaned.startsWith("uploads/") || cleaned.startsWith("uploads\\")) {
            cleaned = cleaned.substring(8);
        }

        if (cleaned.contains("..") || cleaned.startsWith("/") || cleaned.matches("^[a-zA-Z]:.*")) {
            log.error("检测到非法路径遍历攻击: original={}, cleaned={}", filePath, cleaned);
            throw new RuntimeException("文件路径包含非法字符");
        }

        return cleaned;
    }

    private void checkFilePermission(top.tqx.demo_1.entity.File file, top.tqx.demo_1.entity.User user) {
        Integer userType = user.getUserType();
        Long currentUserId = user.getId();
        Long currentDeptId = user.getDeptId();

        if (userType.equals(CommonConstant.RoleType.SUPER_ADMIN)) {
            return;
        }

        if (userType.equals(CommonConstant.RoleType.DEPT_ADMIN)) {
            if (!file.getDeptId().equals(currentDeptId)) {
                throw new RuntimeException("无权限查看此文件");
            }
            return;
        }

        if (userType.equals(CommonConstant.RoleType.NORMAL_USER)) {
            if (!file.getDeptId().equals(currentDeptId)) {
                throw new RuntimeException("无权限查看此文件");
            }
            if (!file.getUploadUserId().equals(currentUserId)) {
                throw new RuntimeException("无权限查看此文件");
            }
            if (file.getAuditStatus() != null && file.getAuditStatus() == 0) {
                throw new RuntimeException("文件待审核，暂时无法查看");
            }
        }
    }

    private FileVO convertToVO(top.tqx.demo_1.entity.File file) {
        FileVO vo = new FileVO();
        BeanUtils.copyProperties(file, vo);

        if (file.getUploadUserId() != null) {
            try {
                User user = userMapper.selectById(file.getUploadUserId());
                if (user != null && user.getUsername() != null) {
                    vo.setUploadUserName(user.getUsername());
                } else {
                    vo.setUploadUserName("user_" + file.getUploadUserId());
                }
            } catch (Exception e) {
                log.warn("获取上传用户名称失败: userId={}", file.getUploadUserId());
                vo.setUploadUserName("user_" + file.getUploadUserId());
            }
        }

        if (file.getDeptId() != null) {
            try {
                Department department = departmentService.getDepartmentById(file.getDeptId());
                if (department != null) {
                    vo.setDeptName(department.getDeptName());
                }
            } catch (Exception e) {
                log.warn("获取部门名称失败: deptId={}", file.getDeptId());
            }
        }

        return vo;
    }

    @Override
    public void updateFileDescription(Long fileId, String description) {
        top.tqx.demo_1.entity.File file = this.getById(fileId);
        if (file != null) {
            file.setDescription(description);
            file.setUpdateTime(LocalDateTime.now());
            this.updateById(file);
        }
    }

    @Override
    public top.tqx.demo_1.entity.File getFileEntityById(Long id) {
        return this.getById(id);
    }

    @Override
    public void updateFileEntity(top.tqx.demo_1.entity.File file) {
        this.updateById(file);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void approveFile(Long fileId, Long auditorId) {
        top.tqx.demo_1.entity.File file = this.getById(fileId);
        if (file == null) {
            throw new RuntimeException("文件不存在");
        }

        if (file.getAuditStatus() == null) {
            throw new RuntimeException("文件审核状态异常");
        }

        if (file.getAuditStatus() == 1) {
            log.warn("文件已审核通过，执行更新操作: fileId={}, auditorId={}", fileId, auditorId);
        }

        String oldPath = file.getFilePath();
        String newPath = moveFileToPublic(file);
        
        file.setFilePath(newPath);
        log.info("审核通过，文件已移动到公开目录: {} -> {}", oldPath, newPath);

        file.setIsPublic(1);
        file.setAuditStatus(1);
        file.setUpdateTime(LocalDateTime.now());
        
        if ("personal".equals(file.getFileCategory())) {
            file.setFileCategory("rule");
            log.info("审核通过，文件分类已从 personal 更新为 rule: fileId={}", fileId);
        }
        
        this.updateById(file);

        log.info("文件审核通过: fileId={}, auditorId={}", fileId, auditorId);

        String collectionName = determineCollectionName(3, file.getDeptId(), true);
        if (collectionName != null) {
            log.info("审核通过，准备触发异步向量化: fileId={}, collection={}", fileId, collectionName);
            asyncVectorizeService.vectorizeFileAsync(collectionName, fileId, auditorId);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void rejectFile(Long fileId, Long auditorId) {
        top.tqx.demo_1.entity.File file = this.getById(fileId);
        if (file == null) {
            throw new RuntimeException("文件不存在");
        }

        if (file.getAuditStatus() == null) {
            throw new RuntimeException("文件审核状态异常");
        }

        if (file.getAuditStatus() == 2) {
            log.warn("文件已被拒绝，执行更新操作: fileId={}, auditorId={}", fileId, auditorId);
        }

        file.setAuditStatus(2);
        file.setStatus(CommonConstant.STATUS_DISABLE);
        file.setUpdateTime(LocalDateTime.now());
        this.updateById(file);

        log.info("文件审核拒绝: fileId={}, auditorId={}", fileId, auditorId);
    }

    private String moveFileToPublic(top.tqx.demo_1.entity.File file) {
        try {
            String fileNameWithoutExt = file.getFileName().contains(".")
                    ? file.getFileName().substring(0, file.getFileName().lastIndexOf("."))
                    : file.getFileName();

            String username = getUserUsername(file.getUploadUserId());
            
            String filePath = file.getFilePath();
            if (filePath.startsWith("./uploads/") || filePath.startsWith("/uploads/") || filePath.startsWith("uploads/")) {
                filePath = filePath.replaceFirst("^\\.?/?uploads/?", "");
                log.warn("检测到路径包含uploads前缀，已清理: original={}, cleaned={}", file.getFilePath(), filePath);
            }
            
            Path oldPath = Paths.get(uploadPath, filePath);
            
            log.info("开始移动文件: fileId={}, oldPath={}, fullPath={}", 
                    file.getId(), filePath, oldPath.toString());
            
            if (!Files.exists(oldPath)) {
                log.error("原文件不存在: fileId={}, fileName={}, filePath={}, fullPath={}", 
                        file.getId(), file.getFileName(), filePath, oldPath.toString());
                
                String newPath = "resources/user/" + username + "/public/" + fileNameWithoutExt + "/v1/" + file.getFileName();
                Path newFullPath = Paths.get(uploadPath, newPath);
                
                if (Files.exists(newFullPath)) {
                    log.warn("目标文件已存在，跳过移动: newPath={}", newPath);
                    return newPath;
                }
                
                throw new RuntimeException("原文件不存在: " + oldPath.toString());
            }
            
            Path newBasePath = Paths.get(uploadPath, "resources", "user", username, "public", fileNameWithoutExt);

            int version = 1;
            while (Files.exists(newBasePath.resolve("v" + version))) {
                version++;
            }

            Path newVersionPath = newBasePath.resolve("v" + version);
            Files.createDirectories(newVersionPath);
            Path newPath = newVersionPath.resolve(file.getFileName());

            Files.createDirectories(newPath.getParent());
            Files.move(oldPath, newPath);

            String relativePath = newPath.toString().replace(uploadPath, "");
            if (relativePath.startsWith(java.io.File.separator)) {
                relativePath = relativePath.substring(1);
            }
            if (relativePath.startsWith("/")) {
                relativePath = relativePath.substring(1);
            }
            
            log.info("文件移动成功: {} -> {}", oldPath, newPath);
            return relativePath;
        } catch (IOException e) {
            log.error("移动文件失败: fileId={}, filePath={}", file.getId(), file.getFilePath(), e);
            throw new RuntimeException("移动文件失败: " + e.getMessage(), e);
        }
    }

    private String determineCollectionName(Integer userType, Long deptId, Boolean isPublic) {
        try {
            if (userType == 1 || userType == 2) {
                if (deptId != null) {
                    String deptName = getDepartmentName(deptId);
                    
                    LambdaQueryWrapper<KnowledgeBasePath> wrapper = new LambdaQueryWrapper<>();
                    wrapper.eq(KnowledgeBasePath::getDepartment, deptName)
                           .or()
                           .eq(KnowledgeBasePath::getName, "dept_" + deptId + "_kb")
                           .last("LIMIT 1");
                    KnowledgeBasePath kb = knowledgeBasePathMapper.selectOne(wrapper);
                    if (kb != null) {
                        return kb.getName();
                    }
                    return "dept_" + deptId + "_kb";
                }
                return "public_kb";
            } else {
                if (Boolean.TRUE.equals(isPublic)) {
                    return "public_kb";
                } else {
                    return "private_kb";
                }
            }
        } catch (Exception e) {
            log.error("确定collection名称失败，使用默认值", e);
            return Boolean.TRUE.equals(isPublic) ? "public_kb" : "private_kb";
        }
    }

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

    @Override
    public PageResult<FileVO> getPendingFiles(Integer pageNum, Integer pageSize) {
        Page<top.tqx.demo_1.entity.File> page = new Page<>(pageNum, pageSize);
        LambdaQueryWrapper<top.tqx.demo_1.entity.File> wrapper = new LambdaQueryWrapper<>();
        
        wrapper.eq(top.tqx.demo_1.entity.File::getAuditStatus, 0);
        wrapper.eq(top.tqx.demo_1.entity.File::getStatus, CommonConstant.STATUS_ENABLE);
        wrapper.orderByDesc(top.tqx.demo_1.entity.File::getCreateTime);
        
        Page<top.tqx.demo_1.entity.File> filePage = this.page(page, wrapper);
        
        List<FileVO> voList = filePage.getRecords().stream()
                .map(this::convertToVO)
                .collect(Collectors.toList());
        
        return PageResult.of(voList, filePage.getTotal(), filePage.getSize(), filePage.getCurrent());
    }
}

