package com.supServer.common.utils.file;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.internal.OSSHeaders;
import com.aliyun.oss.model.*;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.ResponseBody;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.InputStreamSource;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @ClassName AliOssUtil
 * @Description 阿里云oss相关工具类
 * @Date 2022/5/26 20:00
 * @Version 1.0
 */
@Component
@Slf4j
public class AliOssUtil {

    // 绑定的自有域名
    private static String DOMAIN = "https://files-gys.jiansheku.com/";
    // yourEndpoint填写Bucket所在地域对应的Endpoint。以华东1（杭州）为例，Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。
    private static String endpoint = "https://oss-cn-chengdu.aliyuncs.com";
    // 阿里云账号AccessKey拥有所有API的访问权限，风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维，请登录RAM控制台创建RAM用户。
    private static String accessKeyId = "LTAI5t87xwSHQUZPgAGgRG5x";
    private static String accessKeySecret = "GQdbYstgFJ3XFft2itLlnXcskZFHsg";
    // 填写Bucket名称，例如examplebucket。
    private static String bucketName = "gys-file";

    public static String uploadEnterpriseLogo (String fileUrl, String fileName) throws Exception {
        String ossPath = "file/image/logo/enterprise/";

        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        // 创建PutObjectRequest对象。
        PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, ossPath.concat(fileName), urlToFile(fileUrl));
        // 如果需要上传时设置存储类型和访问权限，请参考以下示例代码。
        ObjectMetadata metadata = new ObjectMetadata();
        metadata.setHeader(OSSHeaders.CONTENT_DISPOSITION, "inline");
        metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard);
        metadata.setObjectAcl(CannedAccessControlList.PublicRead);
        putObjectRequest.setMetadata(metadata);

        // 上传文件。
        ossClient.putObject(putObjectRequest);
        // 关闭ossClient
        ossClient.shutdown();

        return DOMAIN.concat(ossPath.concat(fileName));
    }

    /**
     * 上传excel
     *
     * @param workbook
     * @param dir       目录
     * @param excelName 文件名
     * @return
     */
    public static String uploadExcel (XSSFWorkbook workbook, String dir, String excelName) {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            workbook.write(bos);
        } catch (IOException e) {
        } finally {
            try {
                bos.close();
            } catch (IOException e) {
            }
        }
        byte[] brray = bos.toByteArray();
        InputStream is = new ByteArrayInputStream(brray);
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        try {
            // 创建PutObjectRequest对象。
            PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, dir + excelName, is);
            // 如果需要上传时设置存储类型和访问权限，请参考以下示例代码。
            ObjectMetadata metadata = new ObjectMetadata();
            metadata.setHeader(OSSHeaders.CONTENT_DISPOSITION, "inline");
            metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard);
            metadata.setObjectAcl(CannedAccessControlList.PublicRead);
            putObjectRequest.setMetadata(metadata);

            // 上传文件
            ossClient.putObject(putObjectRequest);
            // 关闭ossClient
            ossClient.shutdown();
            return DOMAIN.concat(dir.concat(excelName));

            //设置过期时间
//            LocalDateTime time = LocalDateTime.now().plusDays(5);
//            Date expiration = Date.from(time.atZone(ZoneId.systemDefault()).toInstant());
//            String s = ossClient.generatePresignedUrl(bucketName, dir + "/" + excelName, expiration).toString();
//            return s;
        } catch (OSSException oe) {
            log.error(oe.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
        return null;
    }

    public static void deleteExcel (String excelUrl) {
        if (StringUtils.isNotEmpty(excelUrl)) {
            OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
            try {
                boolean b = ossClient.doesObjectExist(bucketName, excelUrl);
                if (b) {
                    ossClient.deleteObject(bucketName, excelUrl);
                }
            } catch (OSSException oe) {
                log.error(oe.getMessage());
            } finally {
                if (null != ossClient) {
                    ossClient.shutdown();
                }
            }
        }

    }

    /**
     * 将文件上传到阿里OSS
     *
     * @param sourceFilePathName 本地文件
     * @param aimFilePathName    在阿里OSS中保存的可以包含路径的文件名
     * @return 返回上传后文件的访问路径
     * @throws FileNotFoundException
     */
    public static String upload (String sourceFilePathName, String aimFilePathName) throws FileNotFoundException {
        FileInputStream is = new FileInputStream(sourceFilePathName);

        if (aimFilePathName.startsWith("/")) {
            aimFilePathName = aimFilePathName.substring(1);
        }

        // 如果需要上传时设置存储类型与访问权限，请参考以下示例代码。
        ObjectMetadata metadata = new ObjectMetadata();
        int indexOfLastDot = aimFilePathName.lastIndexOf(".");
        String suffix = aimFilePathName.substring(indexOfLastDot);
        metadata.setContentType(getContentType(suffix));

        //避免文件覆盖
        aimFilePathName = aimFilePathName.substring(0, indexOfLastDot) + System.currentTimeMillis() + suffix;

        PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, aimFilePathName, is);
        //避免访问时将图片下载下来
        putObjectRequest.setMetadata(metadata);

        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        ossClient.putObject(putObjectRequest);

        Date expiration = new Date(System.currentTimeMillis() + 3600L * 1000 * 24 * 365 * 100);
        URL url = ossClient.generatePresignedUrl(bucketName, aimFilePathName, expiration);

        // 关闭ossClient
        ossClient.shutdown();

        return url.toString();
    }

    /**
     * 文件上传
     *
     * @param multipartFile
     * @return
     * @throws IOException
     */
    public static String uploadFile (MultipartFile multipartFile) throws IOException {

        // 获取上传的文件的输入流
        InputStream inputStream = multipartFile.getInputStream();
        // 获取文件名称
        String fileName = multipartFile.getOriginalFilename();

        // 避免文件覆盖
        int i = fileName.lastIndexOf(".");
        String suffix = fileName.substring(i);
        fileName = fileName.substring(0, i) + System.currentTimeMillis() + suffix;

        // 把文件按照日期进行分类
        // 获取当前日期
        String datePath = DateTimeFormatter.ISO_DATE.format(LocalDate.now());
        String oosPath = getPath(fileName.substring(fileName.lastIndexOf(".")));
        // 拼接fileName
        fileName = oosPath + datePath + "/" + fileName;

        // 如果需要上传时设置存储类型与访问权限
//        ObjectMetadata metadata = new ObjectMetadata();
//        metadata.setContentType(getUploadContentType(fileName.substring(fileName.lastIndexOf("."))));

        // 上传文件到OSS时需要指定包含文件后缀在内的完整路径，例如abc/efg/123.jpg。
        PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, fileName, inputStream);
//        putObjectRequest.setMetadata(metadata);

        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        ossClient.putObject(putObjectRequest);

        //文件访问路径
        Date expiration = new Date(System.currentTimeMillis() + 3600L * 1000 * 24 * 365 * 100);
        URL url = ossClient.generatePresignedUrl(bucketName, fileName, expiration);

        // 关闭ossClient
        ossClient.shutdown();
        // 把上传到oss的路径返回
        return url.toString();
    }

    // 根据文件类型存储到对应目录
    private static String getPath (String suffix) {
        if (suffix.equalsIgnoreCase(".gif") ||
                suffix.equalsIgnoreCase(".png") ||
                suffix.equalsIgnoreCase(".jpg") ||
                suffix.equalsIgnoreCase(".jpeg")
        ) {
            return "file/image/";
        }

        if (suffix.equalsIgnoreCase(".pdf")) {
            return "file/pdf/";
        }

        if (suffix.equalsIgnoreCase(".xls") ||
                suffix.equalsIgnoreCase(".xlsx")
        ) {
            return "file/excel/";
        }

        return "file/image/";

    }

    private static String getUploadContentType (String FileNameExtension) {
        if (FileNameExtension.equalsIgnoreCase(".bmp")) {
            return "image/bmp";
        }
        if (FileNameExtension.equalsIgnoreCase(".gif")) {
            return "image/gif";
        }
        if (FileNameExtension.equalsIgnoreCase(".jpeg") ||
                FileNameExtension.equalsIgnoreCase(".jpg") ||
                FileNameExtension.equalsIgnoreCase(".png")
        ) {
            return "image/jpg";
        }
        if (FileNameExtension.equalsIgnoreCase(".pdf")) {
            return "application/pdf";
        }
        return "";
    }

    /**
     * 网络实现上传头像到OSS
     *
     * @param multipartFile
     * @return
     */
    public static String upload (MultipartFile multipartFile) throws IOException {
        // 获取上传的文件的输入流
        InputStream inputStream = multipartFile.getInputStream();
        // 获取文件名称
        String fileName = multipartFile.getOriginalFilename();

        // 避免文件覆盖
        int i = fileName.lastIndexOf(".");
        String suffix = fileName.substring(i);
        fileName = fileName.substring(0, i) + System.currentTimeMillis() + suffix;

        // 把文件按照日期进行分类
        // 获取当前日期
        String datePath = DateTimeFormatter.ISO_DATE.format(LocalDate.now());
        // 拼接fileName
        fileName = datePath + "/" + fileName;

        // 如果需要上传时设置存储类型与访问权限
        ObjectMetadata metadata = new ObjectMetadata();
        metadata.setContentType(getContentType(fileName.substring(fileName.lastIndexOf("."))));

        // 上传文件到OSS时需要指定包含文件后缀在内的完整路径，例如abc/efg/123.jpg。
        PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, fileName, inputStream);
        putObjectRequest.setMetadata(metadata);

        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        ossClient.putObject(putObjectRequest);

        //文件访问路径
        Date expiration = new Date(System.currentTimeMillis() + 3600L * 1000 * 24 * 365 * 100);
        URL url = ossClient.generatePresignedUrl(bucketName, fileName, expiration);

        // 关闭ossClient
        ossClient.shutdown();
        // 把上传到oss的路径返回
        return url.toString();
    }

    /**
     * 返回contentType
     *
     * @param FileNameExtension
     * @return
     */
    private static String getContentType (String FileNameExtension) {
        if (FileNameExtension.equalsIgnoreCase(".bmp")) {
            return "image/bmp";
        }
        if (FileNameExtension.equalsIgnoreCase(".gif")) {
            return "image/gif";
        }
        if (FileNameExtension.equalsIgnoreCase(".jpeg") ||
                FileNameExtension.equalsIgnoreCase(".jpg") ||
                FileNameExtension.equalsIgnoreCase(".png")
        ) {
            return "image/jpg";
        }
        return "image/jpg";
    }

    /**
     * 列举 指定路径下所有的文件的文件名
     * 如果要列出根路径下的所有文件，path= ""
     *
     * @param path
     * @return
     */
    public static List<String> listFileName (String path) {
        List<String> res = new ArrayList<>();
        // 构造ListObjectsRequest请求。
        ListObjectsRequest listObjectsRequest = new ListObjectsRequest(bucketName);

        // 设置prefix参数来获取fun目录下的所有文件。
        listObjectsRequest.setPrefix(path);

        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        // 列出文件。
        ObjectListing listing = ossClient.listObjects(listObjectsRequest);
        // 遍历所有文件
        for (OSSObjectSummary objectSummary : listing.getObjectSummaries()) {
            System.out.println(objectSummary.getKey());
        }
        // 关闭OSSClient。
        ossClient.shutdown();
        return res;
    }

    /**
     * 列举文件下所有的文件url信息
     */
    public static List<String> listFileUrl (String path) {
        List<String> res = new ArrayList<>();

        // 构造ListObjectsRequest请求
        ListObjectsRequest listObjectsRequest = new ListObjectsRequest(bucketName);

        // 设置prefix参数来获取fun目录下的所有文件。
        listObjectsRequest.setPrefix(path);

        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        // 列出文件。
        ObjectListing listing = ossClient.listObjects(listObjectsRequest);
        // 遍历所有文件。

        for (OSSObjectSummary objectSummary : listing.getObjectSummaries()) {
            //文件访问路径
            Date expiration = new Date(System.currentTimeMillis() + 3600L * 1000 * 24 * 365 * 100);
            URL url = ossClient.generatePresignedUrl(bucketName, objectSummary.getKey(), expiration);
            res.add(url.toString());
        }
        // 关闭OSSClient。
        ossClient.shutdown();
        return res;
    }

    /**
     * 判断文件是否存在
     *
     * @param objectName
     * @return
     */
    public static boolean isFileExist (String objectName) {
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        boolean res = ossClient.doesObjectExist(bucketName, objectName);
        return res;
    }

    /**
     * 创建文件夹
     *
     * @param folder
     * @return
     */
    public static String createFolder (String folder) {
        // 文件夹名
        final String keySuffixWithSlash = folder;
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        // 判断文件夹是否存在，不存在则创建
        if (!ossClient.doesObjectExist(bucketName, keySuffixWithSlash)) {
            // 创建文件夹
            ossClient.putObject(bucketName, keySuffixWithSlash, new ByteArrayInputStream(new byte[0]));
            // 得到文件夹名
            OSSObject object = ossClient.getObject(bucketName, keySuffixWithSlash);
            String fileDir = object.getKey();
            ossClient.shutdown();
            return fileDir;
        }

        return keySuffixWithSlash;
    }

    /**
     * 通过文件名下载文件
     *
     * @param objectName    要下载的文件名
     * @param localFileName 本地要创建的文件名
     */
    public static void downloadFile (String objectName, String localFileName) {
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        // 下载OSS文件到本地文件。如果指定的本地文件存在会覆盖，不存在则新建。
        ossClient.getObject(new GetObjectRequest(bucketName, objectName), new File(localFileName));
        // 关闭OSSClient。
        ossClient.shutdown();
    }

    /**
     * url地址资源转化为file流
     *
     * @param urlPath
     * @return
     */
    private static File urlToFile (String urlPath) {

        URLConnection urlConn = null;
        InputStream is = null;
        File file = null;
        FileOutputStream fos = null;
        try {
            URL url = new URL(urlPath);
            urlConn = url.openConnection();
            is = urlConn.getInputStream();

            file = File.createTempFile("tmp", null);
            fos = new FileOutputStream(file);
            byte[] buffer = new byte[4096];
            int length;
            while ((length = is.read(buffer)) > 0) {
                fos.write(buffer, 0, length);
            }
            return file;
        } catch (IOException e) {
            return null;
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException ignored) {
                }
            }
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException ignored) {
                }
            }
        }
    }

    /**
     * 下载要发送的附件
     *
     * @param fileName 附件地址（相对路径）
     * @return
     * @throws Exception
     */
    public static InputStreamSource getDownLoadFile (String fileName) throws Exception {
        InputStreamSource inputStream = null;
        ByteArrayOutputStream stream = null;
        try {
            stream = downloadForOutputStream(DOMAIN.concat(fileName));
            inputStream = new ByteArrayResource(stream.toByteArray());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (stream != null) stream.close();
        }
        return inputStream;
    }

    /**
     * 文件下载，并返回字节输出流
     *
     * @param sourceUrl 资源下载地址
     * @return steram（谁得谁关）
     * @throws Exception
     */
    public static ByteArrayOutputStream downloadForOutputStream (String sourceUrl) throws Exception {
        ByteArrayOutputStream steram = null;
        InputStream is = null;
        try {
            OkHttpClient client = new OkHttpClient();
            Request req = new Request.Builder().url(sourceUrl).build();
            okhttp3.Response resp = client.newCall(req).execute();
            if (resp.isSuccessful()) {
                ResponseBody body = resp.body();
                assert body != null;
                is = body.byteStream();
                steram = readInputStream(is);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (is != null) is.close();
        }
        return steram;
    }

    /**
     * 将输入流转换成字节输出流
     *
     * @param is 输入流（谁传谁关）
     * @return writer（谁得谁关）
     * @throws IOException
     */
    public static ByteArrayOutputStream readInputStream (InputStream is) {
        ByteArrayOutputStream steram = new ByteArrayOutputStream();
        try {
            byte[] buff = new byte[1024 * 2];
            int len = 0;
            while ((len = is.read(buff)) != -1) {
                steram.write(buff, 0, len);
            }
        } catch (Exception e) {
            try {
                if (steram != null) steram.close();
            } catch (IOException ioe) {
                ioe.printStackTrace();
            }
            return null;
        }
        return steram;
    }
}
