package com.corewing.app.util;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.*;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Component
public class OSSUploadUtil {

    /**
     * 用户登录名称 oss@1563058769875871.onaliyun.com
     * AccessKey ID LTAI5tKUhXrGxZ5Gj3exWpkG
     * AccessKey Secret PaBTMp3BhcOUgLQhJWmOkhfJhTlzhV
     */
    private static final String OSS_COREWING = "oss.corewing.com";
    private static final String ENDPOINT = "oss-cn-shenzhen.aliyuncs.com"; // 地域节点
    private static final String ACCESS_KEY_ID = "LTAI5tKUhXrGxZ5Gj3exWpkG";     // 替换为你的 AccessKey ID
    private static final String ACCESS_KEY_SECRET = "PaBTMp3BhcOUgLQhJWmOkhfJhTlzhV"; // 替换为你的 AccessKey Secret
    private static final String BUCKET_NAME = "corewing-app";         // 替换为你的 Bucket 名称

    /**
     * 上传文件到 OSS
     * @param inputStream 文件输入流
     * @param objectName OSS 中存储的文件路径（如 "firmware/20231104/update.bin"）
     * @return 上传成功后的文件 URL
     */
    public static String uploadFile(InputStream inputStream, String objectName) {
        // 创建 OSS 客户端实例
        OSS ossClient = new OSSClientBuilder().build(ENDPOINT, ACCESS_KEY_ID, ACCESS_KEY_SECRET);

        try {
            // 设置文件元数据（可选，如 Content-Type）
            ObjectMetadata metadata = new ObjectMetadata();
            metadata.setContentType(getContentType(objectName)); // 自动识别文件类型

            // 上传文件
            PutObjectRequest request = new PutObjectRequest(BUCKET_NAME, objectName, inputStream, metadata);
            PutObjectResult result = ossClient.putObject(request);
            ossClient.setObjectAcl(
                    BUCKET_NAME,                          // Bucket 名称
                    objectName,                           // 刚上传的文件路径
                    CannedAccessControlList.PublicRead    // 公共可读权限
            );
            // 生成文件访问 URL（公网访问需 Bucket 设为公共读）
            return String.format("https://%s/%s", OSS_COREWING, objectName);
        } finally {
            // 关闭 OSS 客户端，释放资源
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

    /**
     * 上传本地文件到 OSS
     * @param localFilePath 本地文件路径（如 "D:/firmware/update.bin"）
     * @param objectName OSS 中存储的文件路径
     * @return 上传成功后的文件 URL
     */
    public static String uploadLocalFile(String localFilePath, String objectName) {
        OSS ossClient = new OSSClientBuilder().build(ENDPOINT, ACCESS_KEY_ID, ACCESS_KEY_SECRET);
        try {
            File file = new File(localFilePath);
            ossClient.putObject(BUCKET_NAME, objectName, file);
            ossClient.setObjectAcl(
                    BUCKET_NAME,                          // Bucket 名称
                    objectName,                           // 刚上传的文件路径
                    CannedAccessControlList.PublicRead    // 公共可读权限
            );
            return String.format("https://%s/%s", OSS_COREWING, objectName);
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

    /**
     * 从 OSS 下载文件到本地路径
     * @param objectName OSS 中存储的文件路径（如 "firmware/20231104/update.bin"）
     * @param localFilePath 本地保存路径（如 "D:/downloads/update.bin"）
     * @return 下载是否成功
     */
    public static boolean downloadFileToLocal(String objectName, String localFilePath) {
        OSS ossClient = new OSSClientBuilder().build(ENDPOINT, ACCESS_KEY_ID, ACCESS_KEY_SECRET);
        try {
            // 检查文件是否存在
            if (!ossClient.doesObjectExist(BUCKET_NAME, objectName)) {
                throw new FileNotFoundException("OSS 文件不存在：" + objectName);
            }

            // 下载文件到本地
            ossClient.getObject(new GetObjectRequest(BUCKET_NAME, objectName), new File(localFilePath));
            return true;
        } catch (OSSException | FileNotFoundException e) {
            return false;
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }


    /**
     * 从 OSS 获取文件输入流（需手动关闭流）
     * @param objectName OSS 中存储的文件路径
     * @return 文件输入流（使用后需调用 close() 关闭）
     * @throws IOException 当文件不存在或下载失败时抛出
     */
    public static InputStream downloadFileToStream(String objectName) throws IOException {
        OSS ossClient = new OSSClientBuilder().build(ENDPOINT, ACCESS_KEY_ID, ACCESS_KEY_SECRET);
        try {
            // 检查文件是否存在
            if (!ossClient.doesObjectExist(BUCKET_NAME, objectName)) {
                throw new FileNotFoundException("OSS 文件不存在：" + objectName);
            }

            // 获取 OSS 文件对象并返回输入流
            OSSObject ossObject = ossClient.getObject(BUCKET_NAME, objectName);
            return ossObject.getObjectContent();
        } catch (OSSException e) {
            throw new IOException("OSS 服务异常：" + e.getMessage(), e);
        } finally {
            // 注意：此处不能关闭 ossClient！否则输入流会被提前关闭
            // 需在调用方使用完输入流后，手动关闭 ossClient 和输入流
            // （或通过 try-with-resources 自动关闭）
        }
    }

    /**
     * 从 OSS 删除单个文件
     * @param objectName OSS 中存储的文件路径（如 "firmware/20231104/update.bin"）
     * @return 删除是否成功（文件不存在时返回 false）
     */
    public static boolean deleteFile(String objectName) {
        OSS ossClient = new OSSClientBuilder().build(ENDPOINT, ACCESS_KEY_ID, ACCESS_KEY_SECRET);
        try {
            // 先检查文件是否存在
            if (!ossClient.doesObjectExist(BUCKET_NAME, objectName)) {
                System.out.println("OSS 文件不存在，删除失败：" + objectName);
                return false;
            }

            // 执行删除操作
            ossClient.deleteObject(BUCKET_NAME, objectName);
            System.out.println("OSS 文件删除成功：" + objectName);
            return true;
        } catch (OSSException e) {
            System.err.println("OSS 服务异常，删除失败：" + e.getMessage());
            return false;
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }


    /**
     * 批量删除 OSS 文件（3.17.4 版本终极兼容方案）
     * 逻辑：先执行批量删除，再逐个校验文件是否存在，确认删除结果
     * @param objectNames OSS 文件路径列表（如 ["firmware/update1.bin", "firmware/update2.bin"]）
     * @return 批量删除结果（key：文件路径，value：是否删除成功）
     */
    public static Map<String, Boolean> batchDeleteFiles(List<String> objectNames) {
        Map<String, Boolean> deleteResult = new HashMap<>();
        if (objectNames == null || objectNames.isEmpty()) {
            System.out.println("批量删除失败：文件列表为空");
            return deleteResult;
        }

        OSS ossClient = new OSSClientBuilder().build(ENDPOINT, ACCESS_KEY_ID, ACCESS_KEY_SECRET);
        try {
            // 第一步：执行批量删除（忽略 SDK 返回结果，只保证执行删除操作）
            DeleteObjectsRequest request = new DeleteObjectsRequest(BUCKET_NAME);
            request.setKeys(objectNames); // 直接传入文件路径列表
            ossClient.deleteObjects(request); // 执行删除，不依赖返回结果

            // 第二步：逐个校验文件是否存在，确认删除结果
            for (String objectName : objectNames) {
                boolean isDeleted = !ossClient.doesObjectExist(BUCKET_NAME, objectName);
                deleteResult.put(objectName, isDeleted);
                if (isDeleted) {
                    System.out.println("删除成功：" + objectName);
                } else {
                    System.err.println("删除失败：文件仍存在 -> " + objectName);
                }
            }

        } catch (OSSException e) {
            System.err.printf("OSS 服务异常：错误码=%s，原因=%s%n",
                    e.getErrorCode(), e.getErrorMessage());
            // 服务异常时，所有文件标记为删除失败
            objectNames.forEach(name -> deleteResult.put(name, false));
        } catch (Exception e) {
            System.err.println("批量删除未知异常：" + e.getMessage());
            objectNames.forEach(name -> deleteResult.put(name, false));
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }

        return deleteResult;
    }


    /**
     * 根据文件名获取 Content-Type
     * @param fileName 文件名（如 "update.bin"）
     * @return Content-Type
     */
    private static String getContentType(String fileName) {
        String suffix = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
        switch (suffix) {
            case "bin": return "application/octet-stream";
            case "jpg": case "jpeg": return "image/jpeg";
            case "png": return "image/png";
            case "txt": return "text/plain";
            // 其他文件类型可自行扩展
            default: return "application/octet-stream";
        }
    }
}
