package org.xyzh.file.service.impl;

import org.apache.dubbo.config.annotation.DubboService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
// import org.springframework.beans.BeanUtils; // 不再需要BeanUtils
import org.springframework.util.DigestUtils;
import org.springframework.web.multipart.MultipartFile;
import org.xyzh.api.file.dto.TbSysFileDTO;
import org.xyzh.api.file.service.FileService;
import org.xyzh.common.auth.utils.LoginUtil;
import org.xyzh.common.core.domain.LoginDomain;
import org.xyzh.common.core.domain.ResultDomain;
import org.xyzh.file.config.MinioConfig;
import org.xyzh.file.mapper.FileMapper;
import org.xyzh.file.util.MinioUtil;

import java.io.InputStream;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * @description 文件服务实现类
 * @filename FileServiceImpl.java
 * @author yslg
 * @copyright yslg
 * @since 2025-12-09
 */
@DubboService(
    version = "1.0.0",
    group = "file",
    timeout = 3000,
    retries = 0
)
public class FileServiceImpl implements FileService {
    
    private static final Logger logger = LoggerFactory.getLogger(FileServiceImpl.class);
    
    @Autowired
    private MinioConfig minioConfig;
    
    @Autowired
    private MinioUtil minioUtil;
    
    @Autowired
    private FileMapper fileMapper;
    
    @Override
    public ResultDomain<TbSysFileDTO> uploadFile(MultipartFile file, String module, String businessId) {
        try {
            if (file == null || file.isEmpty()) {
                return ResultDomain.failure("文件不能为空");
            }
            return uploadFileBytesWithUser(file.getBytes(), file.getOriginalFilename(), file.getContentType(), module, businessId, getCurrentUserId());
        } catch (Exception e) {
            logger.error("文件上传失败", e);
            return ResultDomain.failure("文件上传失败: " + e.getMessage());
        }
    }
    
    @Override
    public ResultDomain<TbSysFileDTO> batchUploadFiles(MultipartFile[] files, String module, String businessId, String uploader) {
        try {
            if (files == null || files.length == 0) {
                return ResultDomain.failure("文件列表不能为空");
            }
            
            List<TbSysFileDTO> uploadedFiles = new ArrayList<>();
            
            for (MultipartFile file : files) {
                ResultDomain<TbSysFileDTO> result = uploadFile(file, module, businessId);
                if (result.getSuccess()) {
                    TbSysFileDTO fileDTO = result.getData();
                    fileDTO.setUploader(uploader);
                    uploadedFiles.add(fileDTO);
                } else {
                    // 如果有文件上传失败，记录日志但继续处理其他文件
                    logger.warn("文件上传失败: {}, 原因: {}", file.getOriginalFilename(), result.getMessage());
                }
            }
            
            if (uploadedFiles.isEmpty()) {
                return ResultDomain.failure("所有文件上传失败");
            }
            
            // 返回上传成功的文件列表（这里简化处理，实际可能需要返回详细结果）
            TbSysFileDTO resultDTO = new TbSysFileDTO();
            // 可以在这里设置批量上传的汇总信息
            
            logger.info("批量文件上传完成，成功: {}, 总数: {}", uploadedFiles.size(), files.length);
            return ResultDomain.success("批量文件上传成功", resultDTO);
            
        } catch (Exception e) {
            logger.error("批量文件上传失败", e);
            return ResultDomain.failure("批量文件上传失败: " + e.getMessage());
        }
    }
    
    /**
     * @description    软删除文件（只标记数据库记录为已删除，不删除minio文件）
     * @param    fileId 文件ID
     * @return    ResultDomain<Boolean> 删除结果
     * @author    yslg
     * @since 2025-12-18
     */
    @Override
    public ResultDomain<Boolean> deleteFile(String fileId) {
        try {
            // 从数据库获取文件信息
            TbSysFileDTO sysFile = fileMapper.selectByFileId(fileId);
            if (sysFile == null) {
                return ResultDomain.failure("文件不存在");
            }
            
            // 删除MinIO中的文件
            // minioUtil.deleteFile(sysFile.getBucketName(), sysFile.getObjectName());
            
            // 逻辑删除数据库记录
            int result = fileMapper.deleteByFileId(fileId, "system");
            
            if (result > 0) {
                logger.info("文件软删除成功: {}", fileId);
                return ResultDomain.success("文件删除成功", true);
            } else {
                return ResultDomain.failure("文件删除失败");
            }
            
        } catch (Exception e) {
            logger.error("文件删除失败: {}", fileId, e);
            return ResultDomain.failure("文件删除失败: " + e.getMessage());
        }
    }
    
    @Override
    public ResultDomain<TbSysFileDTO> batchDeleteFiles(String[] fileIds) {
        try {
            if (fileIds == null || fileIds.length == 0) {
                return ResultDomain.failure("文件ID列表不能为空");
            }
            
            int successCount = 0;
            for (String fileId : fileIds) {
                ResultDomain<Boolean> result = deleteFile(fileId);
                if (result.getSuccess()) {
                    successCount++;
                }
            }
            
            TbSysFileDTO resultDTO = new TbSysFileDTO();
            // 可以在这里设置批量删除的结果统计
            
            logger.info("批量文件删除完成，成功: {}, 总数: {}", successCount, fileIds.length);
            return ResultDomain.success("批量文件删除完成", resultDTO);
            
        } catch (Exception e) {
            logger.error("批量文件删除失败", e);
            return ResultDomain.failure("批量文件删除失败: " + e.getMessage());
        }
    }
    
    @Override
    public ResultDomain<byte[]> downloadFile(String fileId) {
        try {
            // 从数据库获取文件信息
            TbSysFileDTO sysFile = fileMapper.selectByFileId(fileId);
            if (sysFile == null) {
                return ResultDomain.failure("文件不存在");
            }
            
            // 从MinIO下载文件
            InputStream inputStream = minioUtil.downloadFile(sysFile.getBucketName(), sysFile.getObjectName());
            if (inputStream == null) {
                return ResultDomain.failure("文件下载失败");
            }
            
            // 将输入流转换为字节数组
            byte[] data = inputStream.readAllBytes();
            inputStream.close();
            
            logger.info("文件下载成功: {}", fileId);
            return ResultDomain.success("文件下载成功", data);
            
        } catch (Exception e) {
            logger.error("文件下载失败: {}", fileId, e);
            return ResultDomain.failure("文件下载失败: " + e.getMessage());
        }
    }
    
    @Override
    public ResultDomain<TbSysFileDTO> getFileById(String fileId) {
        try {
            TbSysFileDTO sysFile = fileMapper.selectByFileId(fileId);
            if (sysFile == null) {
                return ResultDomain.failure("文件不存在");
            }
            
            return ResultDomain.success("获取文件信息成功", sysFile);
            
        } catch (Exception e) {
            logger.error("查询文件失败: {}", fileId, e);
            return ResultDomain.failure("查询文件失败: " + e.getMessage());
        }
    }
    
    @Override
    public ResultDomain<TbSysFileDTO> saveTempFile(MultipartFile file, String module, String businessId) {
        try {
            // 临时文件上传逻辑与普通上传相同，但可以添加特殊标识
            ResultDomain<TbSysFileDTO> result = uploadFile(file, module, businessId);
            if (result.getSuccess()) {
                TbSysFileDTO fileDTO = result.getData();
                fileDTO.setStatus("TEMP"); // 标记为临时文件
                
                // 更新数据库中的状态
                fileMapper.updateByFileId(fileDTO);
            }
            
            return result;
            
        } catch (Exception e) {
            logger.error("临时文件保存失败", e);
            return ResultDomain.failure("临时文件保存失败: " + e.getMessage());
        }
    }

    @Override
    public ResultDomain<TbSysFileDTO> uploadFileVersion(MultipartFile file, String module, String businessId, String fileRootId) {
        try {
            if (file == null || file.isEmpty()) {
                return ResultDomain.failure("文件不能为空");
            }
            if (fileRootId == null || fileRootId.isEmpty()) {
                return ResultDomain.failure("文件根ID不能为空");
            }
            return uploadFileBytesVersionWithUser(file.getBytes(), file.getOriginalFilename(), file.getContentType(), module, businessId, fileRootId, getCurrentUserId());
        } catch (Exception e) {
            logger.error("新版本文件上传失败", e);
            return ResultDomain.failure("文件上传失败: " + e.getMessage());
        }
    }

    @Override
    public ResultDomain<TbSysFileDTO> uploadFileBytes(byte[] fileBytes, String fileName, String contentType, String module, String businessId) {
        return uploadFileBytesWithUser(fileBytes, fileName, contentType, module, businessId, getCurrentUserId());
    }

    @Override
    public ResultDomain<TbSysFileDTO> uploadFileBytesWithUser(byte[] fileBytes, String fileName, String contentType, String module, String businessId, String uploaderUserId) {
        try {
            if (fileBytes == null || fileBytes.length == 0) {
                return ResultDomain.failure("文件不能为空");
            }

            String extension = getFileExtension(fileName);
            long size = fileBytes.length;
            String objectName = generateObjectName(fileName, module);
            String md5Hash = calculateMD5(fileBytes);

            String bucketName = minioConfig.getBucketName();
            java.io.ByteArrayInputStream inputStream = new java.io.ByteArrayInputStream(fileBytes);
            boolean uploadSuccess = minioUtil.uploadFile(bucketName, objectName, inputStream, size, contentType);

            if (!uploadSuccess) {
                return ResultDomain.failure("文件上传到MinIO失败");
            }

            TbSysFileDTO fileDTO = new TbSysFileDTO();
            String fileId = UUID.randomUUID().toString().replace("-", "");
            fileDTO.setOptsn(UUID.randomUUID().toString());
            fileDTO.setFileId(fileId);
            fileDTO.setName(fileName);
            fileDTO.setPath(objectName);
            fileDTO.setUrl(null);
            fileDTO.setSize(size);
            fileDTO.setMimeType(contentType);
            fileDTO.setExtension(extension);
            fileDTO.setMd5Hash(md5Hash);
            fileDTO.setModule(module);
            fileDTO.setBusinessId(businessId);
            fileDTO.setStorageType("MINIO");
            fileDTO.setObjectName(objectName);
            fileDTO.setBucketName(bucketName);
            fileDTO.setVersion(1);
            fileDTO.setFileRootId(fileId);
            fileDTO.setCreator(uploaderUserId);
            fileDTO.setUploader(uploaderUserId);
            fileDTO.setCreateTime(new java.util.Date());

            int result = fileMapper.insertFile(fileDTO);
            if (result <= 0) {
                minioUtil.deleteFile(bucketName, objectName);
                return ResultDomain.failure("文件信息保存失败");
            }

            logger.info("字节数组文件上传成功: {}, uploader: {}", fileName, uploaderUserId);
            return ResultDomain.success("文件上传成功", fileDTO);

        } catch (Exception e) {
            logger.error("字节数组文件上传失败", e);
            return ResultDomain.failure("文件上传失败: " + e.getMessage());
        }
    }

    @Override
    public ResultDomain<TbSysFileDTO> uploadFileBytesVersion(byte[] fileBytes, String fileName, String contentType, String module, String businessId, String fileRootId) {
        return uploadFileBytesVersionWithUser(fileBytes, fileName, contentType, module, businessId, fileRootId, getCurrentUserId());
    }

    @Override
    public ResultDomain<TbSysFileDTO> uploadFileBytesVersionWithUser(byte[] fileBytes, String fileName, String contentType, String module, String businessId, String fileRootId, String uploaderUserId) {
        try {
            if (fileBytes == null || fileBytes.length == 0) {
                return ResultDomain.failure("文件不能为空");
            }
            if (fileRootId == null || fileRootId.isEmpty()) {
                return ResultDomain.failure("文件根ID不能为空");
            }

            // 1. 获取当前最大版本号
            Integer maxVersion = fileMapper.selectMaxVersionByFileRootId(fileRootId);
            int newVersion = (maxVersion != null ? maxVersion : 0) + 1;

            // 2. 生成文件信息
            String extension = getFileExtension(fileName);
            long size = fileBytes.length;

            // 3. 生成唯一的对象名称
            String objectName = generateObjectName(fileName, module);

            // 4. 计算文件MD5
            String md5Hash = calculateMD5(fileBytes);

            // 5. 上传到MinIO
            String bucketName = minioConfig.getBucketName();
            java.io.ByteArrayInputStream inputStream = new java.io.ByteArrayInputStream(fileBytes);
            boolean uploadSuccess = minioUtil.uploadFile(
                bucketName,
                objectName,
                inputStream,
                size,
                contentType
            );

            if (!uploadSuccess) {
                return ResultDomain.failure("文件上传到MinIO失败");
            }
            
            // 6. 构建文件访问URL
            String fileUrl = minioConfig.buildFileUrl(objectName);

            // 7. 保存到数据库（新版本记录）
            TbSysFileDTO fileDTO = new TbSysFileDTO();
            fileDTO.setOptsn(UUID.randomUUID().toString());
            fileDTO.setFileId(UUID.randomUUID().toString());
            fileDTO.setFileRootId(fileRootId);
            fileDTO.setVersion(newVersion);
            fileDTO.setName(fileName);
            fileDTO.setPath(objectName);
            fileDTO.setSize(size);
            fileDTO.setType(extension);
            fileDTO.setStorageType("MINIO");
            fileDTO.setMimeType(contentType);
            fileDTO.setUrl(fileUrl);
            fileDTO.setStatus("NORMAL");
            fileDTO.setModule(module);
            fileDTO.setBusinessId(businessId);
            fileDTO.setObjectName(objectName);
            fileDTO.setBucketName(bucketName);
            fileDTO.setMd5Hash(md5Hash);
            fileDTO.setExtension(extension);
            fileDTO.setCreator(uploaderUserId);
            fileDTO.setUploader(uploaderUserId);
            fileDTO.setCreateTime(new java.util.Date());

            int result = fileMapper.insertFile(fileDTO);
            if (result <= 0) {
                minioUtil.deleteFile(bucketName, objectName);
                return ResultDomain.failure("文件信息保存失败");
            }

            logger.info("新版本文件上传成功(bytes): {}, version: {}, fileRootId: {}, uploader: {}", fileName, newVersion, fileRootId, uploaderUserId);
            return ResultDomain.success("文件上传成功", fileDTO);

        } catch (Exception e) {
            logger.error("新版本文件上传失败(bytes)", e);
            return ResultDomain.failure("文件上传失败: " + e.getMessage());
        }
    }
    
    /**
     * 生成唯一的对象名称
     */
    private String generateObjectName(String originalFilename, String module) {
        String extension = getFileExtension(originalFilename);
        String date = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
        String uuid = UUID.randomUUID().toString().replace("-", "");
        
        return String.format("%s/%s/%s%s", module, date, uuid, extension);
    }
    
    /**
     * 获取文件扩展名
     */
    private String getFileExtension(String filename) {
        if (filename == null || !filename.contains(".")) {
            return "";
        }
        return filename.substring(filename.lastIndexOf("."));
    }
    
    /**
     * 计算文件MD5值
     */
    private String calculateMD5(byte[] data) {
        return DigestUtils.md5DigestAsHex(data);
    }
    
    /**
     * 获取当前登录用户ID（支持未登录场景）
     */
    private String getCurrentUserId() {
        try {
            LoginDomain loginDomain = LoginUtil.getCurrentLogin();
            if (loginDomain != null && loginDomain.getUser() != null) {
                return loginDomain.getUser().getUserId();
            }
        } catch (Exception e) {
            logger.debug("获取当前登录用户失败: {}", e.getMessage());
        }
        return null;
    }
}
