package com.njcn.oss.utils;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import com.aliyun.oss.OSS;
import com.njcn.ali.oss.config.AliYunOssConfig;
import com.njcn.ali.oss.util.AliYunOssUtils;
import com.njcn.common.config.GeneralInfo;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.huawei.obs.config.HuaweiObsProperties;
import com.njcn.huawei.obs.util.OBSUtil;
import com.njcn.minioss.bo.MinIoUploadResDTO;
import com.njcn.minioss.config.MinIossProperties;
import com.njcn.minioss.util.MinIoUtils;
import com.njcn.oss.constant.GeneralConstant;
import com.njcn.oss.constant.OssPath;
import com.njcn.oss.enums.OssResponseEnum;
import com.obs.services.ObsClient;
import com.obs.services.model.PutObjectRequest;
import io.minio.*;
import io.minio.messages.Item;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
 * @author hongawen
 * @version 1.0.0
 * @date 2023年03月07日 22:24
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class FileStorageUtil {

    @Resource
    private MinioClient instance;

    private final GeneralInfo generalInfo;

    /***
     * 华为文件服务器工具类
     * 若有方法需求或方法不满足，去私有仓库下载huawei-obs-springboot-starter模块进行二次开发
     * 开发完毕后需deploy到maven私有仓库
     */
    private final OBSUtil obsUtil;

    /***
     * 免费开源Minioss文件服务器工具类
     * 若有方法需求或方法不满足，去私有仓库下载minioss-springboot-starter模块进行二次开发
     * 开发完毕后需deploy到maven私有仓库
     */
    private final MinIoUtils minIoUtils;

    private final MinIossProperties minIossProperties;

    /**
     * 阿里云文件服务器
     */
    private final AliYunOssUtils aliYunOssUtils;

    private final HuaweiObsProperties huaweiObsProperties;

    private final OSS ossClient;
    private final AliYunOssConfig ossConfig;


    /***
     * 上传MultipartFile文件，
     * @author hongawen
     * @date 2023/3/7 22:48
     * @param multipartFile  文件源
     * @param dir 服务器文件存放路径
     */
    public String uploadMultipart(MultipartFile multipartFile, String dir) {
        String filePath;
        if (generalInfo.getBusinessFileStorage() == GeneralConstant.HUAWEI_OBS) {
            filePath = dir + minIoUtils.minFileName(multipartFile.getOriginalFilename());
            obsUtil.uploadMultipart(multipartFile, filePath);
        } else if (generalInfo.getBusinessFileStorage() == GeneralConstant.MINIO_OSS) {
            try {
                //把名称存入数据
                MinIoUploadResDTO minIoUploadResDTO = minIoUtils.upload(multipartFile, minIossProperties.getBucket(), dir);
                filePath = minIoUploadResDTO.getMinFileName();
            } catch (Exception e) {
                throw new BusinessException(OssResponseEnum.UPLOAD_FILE_ERROR);
            }
        } else if (generalInfo.getBusinessFileStorage() == GeneralConstant.AliYUN_OSS) {
            filePath = dir.endsWith("/")?dir+getFileNameWithoutPath(multipartFile):dir+"/"+getFileNameWithoutPath(multipartFile);
            if (filePath.charAt(0) == '/') {
                filePath=  filePath.substring(1);
            }
            aliYunOssUtils.uploadFile(filePath, multipartFile);
        } else {
            try {
                // 构建完整目录：基准目录 + dir子目录
                Path basePath = Paths.get(generalInfo.getLocalStorePath());
                // 将dir作为子目录添加
                Path uploadPath = basePath.resolve(dir);

                // 确保完整目录存在（包括dir子目录）
                if (!Files.exists(uploadPath)) {
                    Files.createDirectories(uploadPath);
                }

                // 生成本地文件名（处理扩展名，防止异常）
                String originalFilename = multipartFile.getOriginalFilename();
                String fileExtension = "";
                int extIndex = originalFilename.lastIndexOf(".");
                if (extIndex > 0 && extIndex < originalFilename.length() - 1) {
                    fileExtension = originalFilename.substring(extIndex);
                }
                String newFilename = IdUtil.simpleUUID().toUpperCase() + fileExtension;

                // 构建完整路径
                Path filePathPath = uploadPath.resolve(newFilename);

                // 保存文件
                multipartFile.transferTo(filePathPath.toFile());

                // 返回相对路径或完整路径
                filePath = dir + File.separator + newFilename;
            } catch (IOException e) {
                throw new BusinessException("本地文件上传失败: " + e.getMessage());
            }
        }
        return filePath;
    }

    /***
     * 上传MultipartFile文件，
     * @author hongawen
     * @date 2023/3/7 22:48
     * @param multipartFile  文件源
     * @param dir 服务器文件存放路径
     * @param flag 是否保留原始文件名
     */
    public String uploadMultipart(MultipartFile multipartFile, String dir, boolean flag) {
        String filePath;
        if (generalInfo.getBusinessFileStorage() == GeneralConstant.HUAWEI_OBS) {
            filePath = dir + minIoUtils.minFileName(multipartFile.getOriginalFilename());
            obsUtil.uploadMultipart(multipartFile, filePath);
        } else if (generalInfo.getBusinessFileStorage() == GeneralConstant.AliYUN_OSS) {
            filePath = dir.endsWith("/")?dir+getFileNameWithoutPath(multipartFile):dir+"/"+getFileNameWithoutPath(multipartFile);
            if (filePath.charAt(0) == '/') {
                filePath=  filePath.substring(1);
            }
            aliYunOssUtils.uploadFile(filePath, multipartFile);
        } else {
            try {
                //把名称存入数据
                MinIoUploadResDTO minIoUploadResDTO = minIoUtils.upload(multipartFile, minIossProperties.getBucket(), dir, flag);
                filePath = minIoUploadResDTO.getMinFileName();
            } catch (Exception e) {
                throw new BusinessException(OssResponseEnum.UPLOAD_FILE_ERROR);
            }
        }
        return filePath;
    }
    public static String getFileNameWithoutPath(MultipartFile file) {
        String originalFilename = file.getOriginalFilename();
        if (originalFilename == null) {
            return null;
        }
        // 统一分隔符为 '/'，再取最后一部分
        String normalized = originalFilename.replace('\\', '/');
        return normalized.substring(normalized.lastIndexOf('/') + 1);
    }

    /***
     * 上传InputStream流，
     * @author hongawen
     * @date 2023/3/7 22:48
     * @param inputStream  文件源
     * @param dir 服务器文件存放路径
     * @param fileName 原始文件名
     */
    public String uploadStream(InputStream inputStream, String dir, String fileName) {
        String filePath;
        if (generalInfo.getBusinessFileStorage() == GeneralConstant.HUAWEI_OBS) {
            filePath = dir + minIoUtils.minFileName(fileName);
            obsUtil.uploadStream(inputStream, filePath);
        } else if (generalInfo.getBusinessFileStorage() == GeneralConstant.MINIO_OSS) {
            try {
                //把名称存入数据
                MinIoUploadResDTO minIoUploadResDTO = minIoUtils.uploadStream(inputStream, minIossProperties.getBucket(), dir, minIoUtils.minFileName(fileName));
                filePath = minIoUploadResDTO.getMinFileName();
            } catch (Exception e) {
                throw new BusinessException(OssResponseEnum.UPLOAD_FILE_ERROR);
            }
        } else if (generalInfo.getBusinessFileStorage() == GeneralConstant.AliYUN_OSS) {
            filePath = dir + fileName;
            aliYunOssUtils.uploadFile(filePath, inputStream);
        } else {
            // 本地存储逻辑
            try {
                // 1. 获取本地存储路径
                String localStoragePath = generalInfo.getLocalStorePath();

                // 2. 确保目录存在（如果不存在则创建）
                File targetDir = new File(localStoragePath + File.separator + dir);
                if (!targetDir.exists()) {
                    boolean created = targetDir.mkdirs();
                    if (!created) {
                        throw new IOException("本地文档目录创建失败: " + targetDir.getAbsolutePath());
                    }
                }

                // 3. 生成最终文件路径
                String finalFileName = minIoUtils.minFileName(fileName); // 可选：是否重命名文件
                File targetFile = new File(targetDir, finalFileName);

                // 4. 写入文件（使用 try-with-resources 确保流关闭）
                Files.copy(inputStream, targetFile.toPath(), StandardCopyOption.REPLACE_EXISTING);

                // 5. 返回相对路径
                filePath = dir + finalFileName;
            } catch (IOException e) {
                throw new BusinessException(OssResponseEnum.UPLOAD_FILE_ERROR, e.getMessage());
            }
        }
        return filePath;
    }

    /**
     * 上传InputStream流,并指定文件的名称
     *
     * @author xuyang
     */
    public String uploadStreamSpecifyName(InputStream inputStream, String dir, String fileName) {
        String filePath;
        if (generalInfo.getBusinessFileStorage() == GeneralConstant.HUAWEI_OBS) {
            filePath = dir + minIoUtils.minFileName(fileName);
            obsUtil.uploadStream(inputStream, filePath);
        } else if (generalInfo.getBusinessFileStorage() == GeneralConstant.AliYUN_OSS) {
            filePath = dir + fileName;
            aliYunOssUtils.uploadFile(filePath, inputStream);
        } else {
            try {
                //把名称存入数据
                MinIoUploadResDTO minIoUploadResDTO = uploadStreamSpecifyName(inputStream, minIossProperties.getBucket(), dir, fileName);
                filePath = minIoUploadResDTO.getMinFileName();
            } catch (Exception e) {
                throw new BusinessException(OssResponseEnum.UPLOAD_FILE_ERROR);
            }
        }
        return filePath;
    }


    /***
     * 根据文件路径获取文件短期的一个url
     * @author hongawen
     * @date 2023/3/7 23:04
     * @param filePath 文件在服务器的路径
     */
    public String getFileUrl(String filePath) {
        String url;
        if (generalInfo.getBusinessFileStorage() == GeneralConstant.HUAWEI_OBS) {
            url = obsUtil.getFileUrl(filePath);
        } else if (generalInfo.getBusinessFileStorage() == GeneralConstant.AliYUN_OSS) {
            url = aliYunOssUtils.generatePresignedUrl(filePath, 3600);
        } else {
            url = minIoUtils.getObjectUrl(minIossProperties.getBucket(), filePath, 7 * 24 * 60 * 60);
        }
        return url;
    }


    /***
     * 根据文件路径获取文件流
     * @author hongawen
     * @date 2023/3/7 23:04
     * @param filePath 文件在服务器的路径
     */
    public InputStream getFileStream(String filePath) {
        InputStream inputStream;
        try {
            if (generalInfo.getBusinessFileStorage() == GeneralConstant.HUAWEI_OBS) {
                inputStream = obsUtil.downloadStream(filePath);
            } else if (generalInfo.getBusinessFileStorage() == GeneralConstant.MINIO_OSS) {
                inputStream = minIoUtils.downloadStream(minIossProperties.getBucket(), filePath);
            } else if (generalInfo.getBusinessFileStorage() == GeneralConstant.AliYUN_OSS) {
                inputStream = aliYunOssUtils.downloadStream(filePath);
            } else {
                Path path = Paths.get(generalInfo.getLocalStorePath() + File.separator + filePath);
                inputStream = Files.newInputStream(path);
            }
        } catch (Exception exception) {
            throw new BusinessException(OssResponseEnum.DOWNLOAD_FILE_STREAM_ERROR);
        }
        return inputStream;
    }

    /***
     * 根据文件路径获取文件流并下载
     * @author hongawen
     * @date 2023/3/7 23:04
     * @param filePath 文件在服务器的路径
     */
    public void downloadStream(HttpServletResponse response, String filePath) {
        InputStream inputStream;
        OutputStream toClient = null;
        try {
            if (generalInfo.getBusinessFileStorage() == GeneralConstant.HUAWEI_OBS) {
                inputStream = obsUtil.downloadStream(filePath);
            } else if (generalInfo.getBusinessFileStorage() == GeneralConstant.MINIO_OSS) {
                inputStream = minIoUtils.downloadStream(minIossProperties.getBucket(), filePath);
            } else if (generalInfo.getBusinessFileStorage() == GeneralConstant.AliYUN_OSS) {
                inputStream = aliYunOssUtils.downloadStream(filePath);
            } else {
                // 本地存储处理
                Path path = Paths.get(generalInfo.getLocalStorePath() + File.separator + filePath);
                inputStream = Files.newInputStream(path);

                // 设置下载文件名（从路径中提取）
                String fileName = path.getFileName().toString();
                response.setHeader("Content-Disposition",
                        "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
            }
        } catch (Exception exception) {
            throw new BusinessException(OssResponseEnum.DOWNLOAD_FILE_STREAM_ERROR);
        }
        String fileType = filePath.substring(filePath.lastIndexOf('.') + 1).toLowerCase();
        switch (fileType) {
            case "jpg":
            case "jpeg":
            case "png":
            case "gif":
                response.setContentType(MediaType.IMAGE_PNG_VALUE);
                break;
            case "svg":
                response.setContentType("image/svg+xml");
                break;
            case "pdf":
                response.setContentType("application/pdf");
                break;
            default:
                response.setContentType("application/octet-stream;charset=UTF-8");
                break;
        }
        try {
            toClient = new BufferedOutputStream(response.getOutputStream());
            //通过IOUtils对接输入输出流，实现文件下载
            IOUtils.copy(inputStream, toClient);
            toClient.flush();
        } catch (Exception e) {
            throw new BusinessException(OssResponseEnum.DOWNLOAD_FILE_STREAM_ERROR);
        } finally {
            IOUtils.closeQuietly(inputStream);
            IOUtils.closeQuietly(toClient);
        }

    }


    /***
     * 根据文件路径删除指定文件对象
     * @author hongawen
     * @date 2023/3/8 9:25
     * @param fileName  文件路径名
     */
    public void deleteFile(String fileName) {
        if (generalInfo.getBusinessFileStorage() == GeneralConstant.HUAWEI_OBS) {
            obsUtil.delete(fileName);
        } else if (generalInfo.getBusinessFileStorage() == GeneralConstant.MINIO_OSS) {
            minIoUtils.removeObject(minIossProperties.getBucket(), fileName);
        } else if (generalInfo.getBusinessFileStorage() == GeneralConstant.AliYUN_OSS) {
            aliYunOssUtils.deleteFile(fileName);
        } else if (generalInfo.getBusinessFileStorage() == GeneralConstant.LOCAL_DISK) {
            Path path = Paths.get(generalInfo.getLocalStorePath() + File.separator + fileName);
            try {
                Files.delete(path);
            } catch (IOException e) {
                log.info("删除本地文件{}失败失败原因{}", path, e.getMessage());
            }
        }
    }

    public MinIoUploadResDTO uploadStreamSpecifyName(InputStream inputStream, String bucketName, String directory, String fileName) throws Exception {
        if (!minIoUtils.bucketExists(bucketName)) {
            minIoUtils.makeBucket(bucketName);
        }
        directory = (String) Optional.ofNullable(directory).orElse("");
        String minFileName = directory + fileName;
        this.instance.putObject((PutObjectArgs) ((io.minio.PutObjectArgs.Builder) ((io.minio.PutObjectArgs.Builder) PutObjectArgs.builder().bucket(bucketName)).object(minFileName)).stream(inputStream, (long) inputStream.available(), -1L).build());
        inputStream.close();
        return new MinIoUploadResDTO(minFileName, minIoUtils.getObjectUrl(bucketName, minFileName, 60));
    }

    /**
     * @Description: getAllFile 获取minio指定文件夹下所有文件
     * @Param:
     * @return: java.lang.Iterable<io.minio.Result < io.minio.messages.Item>>
     * @Author: clam
     * @Date: 2024/9/27
     */
    public List<MultipartFile> getAllFile(String bucketName, String directory) throws Exception {
        List<MultipartFile> files = new ArrayList<>();
        Iterable<Result<Item>> results = this.instance.listObjects(
                ListObjectsArgs.builder().bucket(bucketName).prefix(directory).recursive(true).build());

        for (Result<Item> result : results) {
            Item item = null;

            item = result.get();
            InputStream stream = this.instance.getObject(
                    GetObjectArgs.builder().bucket(bucketName).object(item.objectName()).build());


            // 假设你有一个方法可以将InputStream转换为MultipartFile
            MultipartFile multipartFile = new MockMultipartFile(item.objectName(), item.objectName(), "text/plain", stream);
            files.add(multipartFile);
        }
        return files;
    }


    /**
     * 目前只给审计日志使用
     *
     * @author cdf
     * @date 2024/10/15
     */
    public File getLastFile(String bucketName, String directory) throws Exception {
        File file = null;
        if (generalInfo.getBusinessFileStorage() == GeneralConstant.MINIO_OSS) {
            Iterable<Result<Item>> results = this.instance.listObjects(
                    ListObjectsArgs.builder().bucket(bucketName).prefix(directory).recursive(true).build());
            Item lastItem = null;
            for (Result<Item> result : results) {
                Item item = result.get();
                if (lastItem == null) {
                    lastItem = item;
                } else {
                    if (item.lastModified().isAfter(lastItem.lastModified())) {
                        lastItem = item;
                    }
                }
            }
            InputStream stream = this.instance.getObject(
                    GetObjectArgs.builder().bucket(bucketName).object(lastItem.objectName()).build());

            file = File.createTempFile(lastItem.objectName(), "xlsx");
            FileUtil.writeFromStream(stream, file);
        } else if (generalInfo.getBusinessFileStorage() == GeneralConstant.HUAWEI_OBS) {
            file = obsUtil.getLastFile(OssPath.LOGBAK);
        } else if (generalInfo.getBusinessFileStorage() == GeneralConstant.AliYUN_OSS) {
            file = aliYunOssUtils.getLastFile(directory);
        }
        return file;
    }

    public void mkdir(String directoryPath) {
        if (generalInfo.getBusinessFileStorage() == GeneralConstant.HUAWEI_OBS) {
            this.createDirectoryHW(directoryPath);
        } else if (generalInfo.getBusinessFileStorage() == GeneralConstant.MINIO_OSS) {
            minIoUtils.createDirectory(minIossProperties.getBucket(), directoryPath);
        } else if (generalInfo.getBusinessFileStorage() == GeneralConstant.AliYUN_OSS) {
            this.createDirectoryAli(directoryPath);
        } else {

        }
    }

    /**
     * 华为文件服务器创建空目录
     *
     * @param dirPath
     */
    private void createDirectoryHW(String dirPath) {
        ObsClient obsClient = null;
        try {
            obsClient = this.huaweiObsProperties.getInstance();
            String bucketName = this.huaweiObsProperties.getObs().getBucket();

            // 确保路径以 / 结尾
            String directoryKey = dirPath.endsWith("/") ? dirPath : dirPath + "/";

            // 创建空对象作为目录占位符
            InputStream emptyStream = new ByteArrayInputStream(new byte[0]);
            PutObjectRequest request = new PutObjectRequest(bucketName, directoryKey, emptyStream);
            obsClient.putObject(request);

            log.info("已创建目录占位符：" + directoryKey);
        } catch (Exception e) {
            throw new RuntimeException("创建目录失败" + dirPath, e);
        } finally {
            this.huaweiObsProperties.destroy(obsClient);
        }
    }


    /**
     * 阿里云文件服务器创建空目录
     *
     * @param dirPath
     */
    public void createDirectoryAli(String dirPath) {
        try {
            // 确保路径以 / 结尾，表示目录
            String directoryKey = dirPath.endsWith("/") ? dirPath : dirPath + "/";

            // 创建空输入流作为目录占位符
            ByteArrayInputStream emptyStream = new ByteArrayInputStream(new byte[0]);

            this.ossClient.putObject(this.ossConfig.getBucket(), directoryKey, emptyStream);
        } catch (Exception e) {
            throw new RuntimeException("创建目录失败：" + dirPath, e);
        }
    }
}
