package com.ruoyi.dj.mqtt.utils;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.dj.mqtt.config.MinioConfig;
import io.minio.*;
import io.minio.credentials.AssumeRoleProvider;
import io.minio.credentials.Credentials;
import io.minio.credentials.StaticProvider;
import io.minio.http.Method;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.time.ZonedDateTime;
import java.util.*;

@Slf4j
public class StorageConfigUtil {

    /**
     * 获取临时凭证
     *
     * @param sn 设备序列号
     * @return 临时凭证
     * @throws Exception 异常信息
     */
//    private static Credentials getTemporaryCredentials(String sn) throws Exception {
//        OkHttpClient customHttpClient = new OkHttpClient();
//        AssumeRoleProvider assumeRoleProvider = new AssumeRoleProvider(
//                "http://zmkg.cqet.top:9999",
//                "admin",
//                "12345678",
//                3600,
//                null,
//                "cn-chengdu",
//                "arn:aws:s3:::drone-?/*".replace("?", sn),
//                "anysession",
//                null,
//                customHttpClient
//        );
//        return assumeRoleProvider.fetch();
//    }
    private static Credentials getTemporaryCredentials(String sn) throws Exception {
        OkHttpClient customHttpClient = new OkHttpClient();
        AssumeRoleProvider assumeRoleProvider = new AssumeRoleProvider(
                "http://192.168.110.65:9000",
                "minioadmin",
                "minioadmin",
                3600,
                null,
                "cn-chengdu",
                "arn:aws:s3:::drone-?/*".replace("?", sn),
                "anysession",
                null,
                customHttpClient
        );
        return assumeRoleProvider.fetch();
    }

    /**
     * 创建使用临时凭证的 MinioClient
     *
     * @param credentials 临时凭证
     * @return MinioClient 实例
     */
    private static MinioClient getMinioClientWithTemporaryCredentials(Credentials credentials) {
        return MinioClient.builder()
                .endpoint(MinioConfig.ENDPOINT)
                .credentialsProvider(new StaticProvider(
                        credentials.accessKey(),
                        credentials.secretKey(),
                        credentials.sessionToken()))
                .build();
    }

    /**
     * 上传文件到 Minio 指定桶和文件夹
     */
    public static void uploadFile(String sn, String folderName, File file) {
        String bucketName = "drone" + sn.toLowerCase().replace("_", "").replace("-", "");

        InputStream fileInputStream = null;
        try {
            // 获取临时凭证
            Credentials credentials = getTemporaryCredentials(sn);
            log.info("临时凭证创建成功: AccessKeyID: {}", credentials.accessKey());

            // 创建使用临时凭证的 MinioClient
            MinioClient minioClient = getMinioClientWithTemporaryCredentials(credentials);

            // 检查桶是否存在，如果不存在则创建
            if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
                log.info("已创建桶: {}", bucketName);
            } else {
                log.info("桶已存在: {}", bucketName);
            }

            // 获取文件输入流
            fileInputStream = new FileInputStream(file);

            // 构建文件路径 (对象键，文件夹 + 文件名)
            String objectName = folderName + "/" + file.getName();

            // 上传文件到 Minio
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .stream(fileInputStream, file.length(), -1)
                    .contentType("application/octet-stream") // 你可以根据需要修改文件类型
                    .build());

            log.info("文件上传成功，文件名: {}", file.getName());
        } catch (Exception e) {
            log.error("文件上传失败: ", e);
        } finally {
            try {
                // 确保关闭文件流
                if (fileInputStream != null) {
                    fileInputStream.close();
                }
            } catch (IOException e) {
                log.error("关闭文件输入流失败: ", e);
            }
        }
    }


    /**
     * 创建存储配置
     *
     * @param sn 设备序列号
     * @return 配置 Map
     */
    public static Map<String, Object> createStorageConfig(String sn) {
        String bucketName = "drone" + sn.toLowerCase().replace("_", "").replace("-", "");
        sn = sn.toLowerCase();

        try {
            // 获取临时凭证
            Credentials credentials = getTemporaryCredentials(sn);
            log.info("临时凭证创建成功: AccessKeyID: {}", credentials.accessKey());

            // 创建使用临时凭证的 MinioClient
            MinioClient minioClient = getMinioClientWithTemporaryCredentials(credentials);

            // 检查桶是否存在，如果不存在则创建
            if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
                log.info("已创建桶: {}", bucketName);
            } else {
                log.info("桶已存在: {}", bucketName);
            }

            // 返回配置 Map
            return createStorageConfigMap(bucketName, credentials, MinioConfig.ENDPOINT, "minio", MinioConfig.REGION, "/", MinioConfig.DURATION_SECONDS);
        } catch (Exception e) {
            log.error("创建临时凭证出错: ", e);
        }
        return null;
    }

    /**
     * 创建存储配置映射
     *
     * @param bucketName      桶名称
     * @param credentials     凭证
     * @param endpoint        端点
     * @param provider        提供商
     * @param region          地区
     * @param objectKeyPrefix 对象键前缀
     * @param durationSeconds 有效期（秒）
     * @return 配置映射
     */
    private static Map<String, Object> createStorageConfigMap(String bucketName, Credentials credentials, String endpoint, String provider, String region, String objectKeyPrefix, int durationSeconds) {
        try {
            Map<String, Object> credentialsMap = new HashMap<>();
            credentialsMap.put("access_key_id", credentials.accessKey());
            credentialsMap.put("access_key_secret", credentials.secretKey());
            credentialsMap.put("expire", durationSeconds);
            credentialsMap.put("security_token", credentials.sessionToken());

            Map<String, Object> outputMap = new HashMap<>();
            outputMap.put("bucket", bucketName);
            outputMap.put("credentials", credentialsMap);
            outputMap.put("endpoint", endpoint);
            outputMap.put("object_key_prefix", objectKeyPrefix);
            outputMap.put("provider", provider);
            outputMap.put("region", region);

            Map<String, Object> responseMap = new HashMap<>();
            responseMap.put("output", outputMap);
            responseMap.put("result", 0);

            return responseMap;
        } catch (Exception e) {
            log.error("创建存储配置映射出错: ", e);
        }
        return null;
    }

    /**
     * 获取 MinioClient 实例
     *
     * @return MinioClient
     */
    public static MinioClient getMinioClient() {
        return MinioClient.builder()
                .endpoint(MinioConfig.ENDPOINT)
                .credentials(MinioConfig.ACCESS_KEY, MinioConfig.SECRET_KEY)
                .build();
    }

    /**
     * 获取指定目录下的所有文件信息
     *
     * @param sn     设备序列号
     * @param taskId 任务ID
     * @return 文件信息列表，包括URL和文件名
     */
    public static List<Map<String, String>> getFiles(String sn, String taskId) {
        sn = sn.toLowerCase();
        String bucketName = "drone" + sn.replace("_", "").replace("-", "");
        String objectKeyPrefix = DigestUtils.md5Hex(sn) + "/" + taskId + "/";

        try {
            // 获取临时凭证
            Credentials credentials = getTemporaryCredentials(sn);

            // 创建使用临时凭证的 MinioClient
            MinioClient minioClient = getMinioClientWithTemporaryCredentials(credentials);

            // 列出指定前缀的对象
            Iterable<Result<Item>> results = minioClient.listObjects(
                    ListObjectsArgs.builder().bucket(bucketName).prefix(objectKeyPrefix).recursive(true).build());

            List<Map<String, String>> fileList = new ArrayList<>();
            for (Result<Item> result : results) {
                Item item = result.get();
                if (!item.isDir()) {
                    String objectName = item.objectName();
                    String fileUrl = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                            .method(Method.GET)
                            .bucket(bucketName)
                            .object(objectName)
                            .build());
                    Map<String, String> fileInfo = new HashMap<>();
                    fileInfo.put("filename", objectName);
                    fileInfo.put("url", fileUrl);
                    fileList.add(fileInfo);
                }
            }
            return fileList;
        } catch (Exception e) {
            log.error("获取文件列表出错: ", e);
        }
        return null;
    }

    public static String listDirectoryContents(String bucketName, String directoryName) throws Exception {
        MinioClient minioClient = getMinioClient();
        Map<String, Object> directoryStructure = new HashMap<>();

        // 确保目录名以 "/" 结尾
        if (directoryName == null || directoryName.isEmpty()) {
            directoryName = "";
        } else if (!directoryName.endsWith("/")) {
            directoryName += "/";
        }

        // 获取指定目录下的直接子项（不递归）
        Iterable<Result<Item>> immediateItems = minioClient.listObjects(
                ListObjectsArgs.builder().bucket(bucketName).prefix(directoryName).recursive(false).build());

        // 日期和大小格式化器
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        DecimalFormat sizeFormat = new DecimalFormat("#.##");

        // 遍历直接子项
        for (Result<Item> result : immediateItems) {
            Item item = result.get();
            String objectName = item.objectName();
            Map<String, Object> details = new HashMap<>();

            try {
                if (!item.isDir()) {
                    // 对于文件，直接使用 Item 中的信息
                    ZonedDateTime lastModified = item.lastModified();
                    Date lastModifiedDate = Date.from(lastModified.toInstant());
                    details.put("creation_time", dateFormat.format(lastModifiedDate));

                    long sizeInBytes = item.size();
                    double sizeInMB = sizeInBytes / (1024.0 * 1024.0);
                    details.put("size", sizeFormat.format(sizeInMB) + " MB");
                    details.put("length", sizeInBytes);

                    String downloadUrl = minioClient.getPresignedObjectUrl(
                            GetPresignedObjectUrlArgs.builder()
                                    .method(Method.GET)
                                    .bucket(bucketName)
                                    .object(objectName)
                                    .expiry(24 * 60 * 60)
                                    .build()
                    );
                    details.put("download_url", downloadUrl);
                } else {
                    // 对于文件夹，size 设置为 "N/A"
                    details.put("size", "N/A");

                    // 可以根据需要计算文件夹的总大小和长度
                    long totalSizeInBytes = calculateDirectorySize(minioClient, bucketName, objectName);
                    details.put("length", totalSizeInBytes);

                    // 获取文件夹的最早创建时间
                    Date directoryCreationTime = getDirectoryCreationTime(minioClient, bucketName, objectName);
                    details.put("creation_time", dateFormat.format(directoryCreationTime));

                    // 文件夹没有下载链接
                    details.put("download_url", "N/A");
                }
            } catch (Exception e) {
                details.put("creation_time", "N/A");
                details.put("size", "N/A");
                details.put("length", 0);
                details.put("download_url", "N/A");
            }

            directoryStructure.put(objectName, details);
        }
        directoryStructure = sortMap(directoryStructure);

        ObjectMapper objectMapper = new ObjectMapper();
        return objectMapper.writeValueAsString(directoryStructure);
    }


    // 确保 "视频录制/" 在最前面
    public static Map<String, Object> sortMap(Map<String, Object> data) {
        // 创建一个新的 LinkedHashMap 来保持排序
        List<Map.Entry<String, Object>> entries = new ArrayList<>(data.entrySet());

        // 排序：将 "视频录制/" 移到最前面
        entries.sort((entryA, entryB) -> {
            if ("视频录制/".equals(entryA.getKey())) return -1;  // "视频录制/" 排到最前面
            if ("视频录制/".equals(entryB.getKey())) return 1;
            return 0;  // 其他项保持原有顺序
        });

        // 将排序后的 entries 放入 LinkedHashMap 中，保持顺序
        Map<String, Object> sortedMap = new LinkedHashMap<>();
        for (Map.Entry<String, Object> entry : entries) {
            sortedMap.put(entry.getKey(), entry.getValue());
        }

        return sortedMap;
    }

    /**
     * 获取目录的创建时间
     *
     * @param minioClient Minio 客户端
     * @param bucketName  桶名称
     * @param objectName  对象名称
     * @return 目录创建时间
     * @throws Exception 异常信息
     */
    private static Date getDirectoryCreationTime(MinioClient minioClient, String bucketName, String objectName) throws Exception {
        Iterable<Result<Item>> subResults = minioClient.listObjects(
                ListObjectsArgs.builder().bucket(bucketName).prefix(objectName).recursive(true).build());

        Date earliestDate = null;

        for (Result<Item> subResult : subResults) {
            Item subItem = subResult.get();
            if (!subItem.isDir()) {
                ZonedDateTime lastModified = subItem.lastModified();
                Date itemDate = Date.from(lastModified.toInstant());
                if (earliestDate == null || itemDate.before(earliestDate)) {
                    earliestDate = itemDate;
                }
            }
        }

        // 如果没有找到文件，则使用当前时间
        return earliestDate != null ? earliestDate : new Date();
    }

    /**
     * 递归计算文件夹的总大小
     *
     * @param minioClient   Minio 客户端
     * @param bucketName    桶名称
     * @param directoryPath 目录路径
     * @return 总大小（字节）
     * @throws Exception 异常信息
     */
    private static long calculateDirectorySize(MinioClient minioClient, String bucketName, String directoryPath) throws Exception {
        long totalSize = 0;

        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder().bucket(bucketName).prefix(directoryPath).recursive(true).build());

        for (Result<Item> result : results) {
            Item item = result.get();
            if (!item.isDir()) {
                totalSize += item.size();
            }
        }

        return totalSize;
    }

    /**
     * 获取桶下所有图片和视频的统计信息
     *
     * @param bucketName 桶名称
     * @return 统计结果
     * @throws Exception 异常信息
     */
    public static Map<String, Object> getMediaStatistics(String bucketName) throws Exception {
        MinioClient minioClient = getMinioClient();
        Map<String, Object> statistics = new HashMap<>();

        long totalImageSize = 0;
        int totalImageCount = 0;
        long totalVideoSize = 0;
        int totalVideoCount = 0;

        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder().bucket(bucketName).recursive(true).build());

        for (Result<Item> result : results) {
            Item item = result.get();
            String objectName = item.objectName();

            if (!item.isDir() && isMediaFile(objectName)) {
                try {
                    // 直接使用 Item 对象的 size() 方法获取文件大小，避免调用 statObject
                    long fileSize = item.size();

                    if (isImageFile(objectName)) {
                        totalImageSize += fileSize;
                        totalImageCount++;
                    } else if (isVideoFile(objectName)) {
                        totalVideoSize += fileSize;
                        totalVideoCount++;
                    }
                } catch (Exception e) {
                    log.error("统计媒体文件出错: ", e);
                }
            }
        }

        DecimalFormat df = new DecimalFormat("#.##");
        statistics.put("totalImageSize", df.format(totalImageSize / (1024.0 * 1024.0)) + "MB");
        statistics.put("totalImageCount", totalImageCount);
        statistics.put("totalVideoSize", df.format(totalVideoSize / (1024.0 * 1024.0)) + "MB");
        statistics.put("totalVideoCount", totalVideoCount);

        return statistics;
    }


    /**
     * 判断文件是否是图片
     *
     * @param fileName 文件名
     * @return 是否是图片
     */
    private static boolean isImageFile(String fileName) {
        String[] imageExtensions = {".jpg", ".jpeg", ".png", ".gif", ".bmp", ".tiff"};
        for (String ext : imageExtensions) {
            if (fileName.toLowerCase().endsWith(ext)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断文件是否是视频
     *
     * @param fileName 文件名
     * @return 是否是视频
     */
    private static boolean isVideoFile(String fileName) {
        String[] videoExtensions = {".mp4", ".avi", ".mov", ".mkv", ".flv"};
        for (String ext : videoExtensions) {
            if (fileName.toLowerCase().endsWith(ext)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断文件是否是媒体文件（图片或视频）
     *
     * @param fileName 文件名
     * @return 是否是媒体文件
     */
    private static boolean isMediaFile(String fileName) {
        return isImageFile(fileName) || isVideoFile(fileName);
    }

    /**
     * 获取指定路径下的文件 URL 列表
     *
     * @param bucketName 桶名称
     * @param itemPath   路径
     * @return URL 列表
     */
    public static List<String> getUrlList(String bucketName, String itemPath) {
        List<String> urlList = new ArrayList<>();

        try {
            MinioClient minioClient = getMinioClient();

            if (itemPath == null || itemPath.isEmpty()) {
                itemPath = "";
            } else if (!itemPath.endsWith("/")) {
                itemPath += "/";
            }

            Iterable<Result<Item>> results = minioClient.listObjects(
                    ListObjectsArgs.builder().bucket(bucketName).prefix(itemPath).recursive(true).build());

            for (Result<Item> result : results) {
                Item item = result.get();
                if (!item.isDir()) {
                    String objectName = item.objectName();

                    String url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                            .method(Method.GET)
                            .bucket(bucketName)
                            .object(objectName)
                            .expiry(24 * 60 * 60)
                            .build());

                    urlList.add(url);
                }
            }

        } catch (Exception e) {
            log.error("获取 URL 列表出错: ", e);
        }
        return urlList;
    }

    // 删除指定的文件或目录
    public static void deleteFilesOrDirectories(List<String> itemPaths, String gateway) {
        MinioClient minioClient = getMinioClient();
        for (String itemPath : itemPaths) {
            try {
                // 判断是文件还是目录
                if (itemPath.endsWith("/")) {
                    // 目录，递归删除其中的所有文件
                    deleteDirectoryContents(minioClient, gateway, itemPath);
                } else {
                    // 文件，直接删除
                    deleteFile(minioClient, gateway, itemPath);
                }
            } catch (Exception e) {
                log.error("删除文件或目录失败: ", e);
            }
        }
    }

    // 删除单个文件
    private static void deleteFile(MinioClient minioClient, String gateway, String itemPath) throws Exception {
        String bucketName = "drone" + gateway.toLowerCase().replace("_", "").replace("-", "");
        minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(itemPath).build());
        log.info("文件删除成功: {}", itemPath);
    }

    // 删除目录中的所有文件
    private static void deleteDirectoryContents(MinioClient minioClient, String gateway, String directoryPath) throws Exception {
        String bucketName = "drone" + gateway.toLowerCase().replace("_", "").replace("-", "");

        // 获取指定目录下的所有文件
        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder().bucket(bucketName).prefix(directoryPath).recursive(true).build());

        // 遍历并删除文件
        for (Result<Item> result : results) {
            Item item = result.get();
            if (!item.isDir()) {
                String objectName = item.objectName();
                minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
                log.info("删除文件: {}", objectName);
            }
        }

        // 删除目录本身
        minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(directoryPath).build());
        log.info("目录删除成功: {}", directoryPath);
    }

    // 删除指定的文件或目录
    public static void deleteAiFilesOrDirectories(List<String> itemPaths, String gateway) {
        MinioClient minioClient = getMinioClient();
        for (String itemPath : itemPaths) {
            try {
                // 判断是文件还是目录
                if (itemPath.endsWith("/")) {
                    // 目录，递归删除其中的所有文件
                    deleteAiDirectoryContents(minioClient, gateway, itemPath);
                } else {
                    // 文件，直接删除
                    deleteAiFile(minioClient, gateway, itemPath);
                }
            } catch (Exception e) {
                log.error("删除文件或目录失败: ", e);
            }
        }
    }

    // 删除单个文件
    private static void deleteAiFile(MinioClient minioClient, String gateway, String itemPath) throws Exception {
        String bucketName = "ai" + gateway.toLowerCase().replace("_", "").replace("-", "");
        minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(itemPath).build());
        log.info("文件删除成功: {}", itemPath);
    }

    // 删除目录中的所有文件
    private static void deleteAiDirectoryContents(MinioClient minioClient, String gateway, String directoryPath) throws Exception {
        String bucketName = "ai" + gateway.toLowerCase().replace("_", "").replace("-", "");

        // 获取指定目录下的所有文件
        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder().bucket(bucketName).prefix(directoryPath).recursive(true).build());

        // 遍历并删除文件
        for (Result<Item> result : results) {
            Item item = result.get();
            if (!item.isDir()) {
                String objectName = item.objectName();
                minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
                log.info("删除文件: {}", objectName);
            }
        }

        // 删除目录本身
        minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(directoryPath).build());
        log.info("目录删除成功: {}", directoryPath);
    }
}
