package com.vctgo.file.service.impl;

import com.alibaba.nacos.common.utils.IoUtils;
import com.vctgo.common.core.constant.HttpStatus;
import com.vctgo.common.core.constant.SecurityConstants;
import com.vctgo.common.core.constant.StatusConstants;
import com.vctgo.common.core.exception.ServiceException;
import com.vctgo.common.security.utils.SecurityUtils;
import com.vctgo.file.domain.MinioInfo;
import com.vctgo.file.service.IDistributedFileSystemService;
import com.vctgo.file.utils.ConstantUtils;
import com.vctgo.file.utils.FileUtils;
import com.vctgo.system.api.domain.FileInfo;
import io.minio.*;
import io.minio.messages.DeleteObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.SystemUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * Minio 文件存储
 *
 * @author ruoyi
 */
@Slf4j
@Service
public class MinioFileInfoServiceImpl implements IDistributedFileSystemService {
    @Autowired
    private MinioInfo minioInfo;
    /**
     * 桶占位符
     */
    private static final String BUCKET_PARAM = "${bucket}";
    /**
     * bucket权限-读写
     */
    private static final String READ_WRITE = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetBucketLocation\",\"s3:ListBucket\",\"s3:ListBucketMultipartUploads\"],\"Resource\":[\"arn:aws:s3:::" + BUCKET_PARAM + "\"]},{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:DeleteObject\",\"s3:GetObject\",\"s3:ListMultipartUploadParts\",\"s3:PutObject\",\"s3:AbortMultipartUpload\"],\"Resource\":[\"arn:aws:s3:::" + BUCKET_PARAM + "/*\"]}]}";

    @Autowired
    private MinioClient client;

    /**
     * Minio文件上传接口
     *
     * @param file 上传的文件
     * @return 访问地址
     * @throws Exception
     */
    @Override
    public String uploadFile(MultipartFile file) throws Exception {
        String bucket = SecurityConstants.DETAILS_TENANT + SecurityUtils.getTenantId();
        // 判断桶是否存在
        if (!client.bucketExists(BucketExistsArgs.builder().bucket(bucket).build())) {
            client.makeBucket(MakeBucketArgs.builder().bucket(bucket).build());
            client.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucket).config(READ_WRITE.replace(BUCKET_PARAM, bucket)).build());
        }

        // 上传文件
        String fileName = FileUtils.extractFilename(file);
        InputStream inputStream = file.getInputStream();
        PutObjectArgs args = PutObjectArgs.builder()
                .bucket(bucket)
                .object(fileName)
                .stream(inputStream, file.getSize(), -1)
                .contentType(file.getContentType())
                .build();
        client.putObject(args);
        IoUtils.closeQuietly(inputStream);
        return minioInfo.getExternalUrl() + "/" + SecurityConstants.DETAILS_TENANT + SecurityUtils.getTenantId() + "/" + fileName;
    }

    /**
     * 删除文件
     *
     * @param fileInfoList
     */
    @Override
    public void deleteMinioFile(List<FileInfo> fileInfoList) {
        Map<Long, List<FileInfo>> fileListMap = fileInfoList.stream().filter(fileInfo -> Objects.nonNull(fileInfo.getTenantId())).collect(Collectors.groupingBy(FileInfo::getTenantId));
        fileListMap.forEach((tenantId, fileInfoListTmp) -> {
            ArrayList<DeleteObject> deleteObjectList = new ArrayList<>();
            fileInfoListTmp.stream().filter(fileInfo -> ConstantUtils.IS_DIRECTORY_FILE_0.equals(fileInfo.getFileIsDirectory())).forEach(fileInfo -> deleteObjectList.add(new DeleteObject(fileInfo.getFileStorageName())));
            client.removeObjects(RemoveObjectsArgs.builder().bucket(SecurityConstants.DETAILS_TENANT + tenantId).objects(deleteObjectList).build()).forEach(deleteErrorResult -> {
                try {
                    if (HttpStatus.SUCCESS != (int) Integer.parseInt(deleteErrorResult.get().code())) {
                        log.error(deleteErrorResult.get().message());
                        throw new SecurityException("删除失败！");
                    }
                } catch (Exception e) {
                    throw new SecurityException("删除失败！");
                }
            });
        });
    }

    /**
     * 批量下载
     *
     * @date: 2022/8/17 16:50
     * @param: fileInfoList: 多个文件名称
     * @Param: zip: 压缩包名称
     * @Param: res: 响应对象
     * @return: void
     **/
    @Async
    @Override
    public void batchDownload(List<FileInfo> fileInfoList, String zipName, HttpServletResponse res, HttpServletRequest req, ConcurrentHashMap<String,Integer> concurrentHashMap) {
        try (FileOutputStream fileOutputStream = new FileOutputStream((SystemUtils.IS_OS_WINDOWS ? "D:\\example\\" : "/home/vctgo/uploadPath/") + zipName);
             ZipOutputStream out = new ZipOutputStream(fileOutputStream)) {
            concurrentHashMap.put(zipName, StatusConstants.UNDER_WAY);
            if (1 == fileInfoList.size() && !"1".equals(fileInfoList.get(0).getFileIsDirectory())) {
                fileInfoList.forEach(fileInfo -> {
                    try (OutputStream outputStream = res.getOutputStream()) {
                        String bucket = SecurityConstants.DETAILS_TENANT + fileInfo.getTenantId();
                        GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(bucket)
                                .object(fileInfo.getFileStorageName()).build();
                        InputStream object = client.getObject(objectArgs);
                        byte buf[] = new byte[1024];
                        int length = 0;
                        while ((length = object.read(buf)) > 0) {
                            outputStream.write(buf, 0, length);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                });
            } else {
                batchDownload(out, fileInfoList, "");
            }
            concurrentHashMap.put(zipName, StatusConstants.COMPLETED);
        } catch (Exception e) {
            concurrentHashMap.put(zipName, StatusConstants.LOSE);
            e.printStackTrace();
        }
    }

    @Override
    public void download(FileInfo fileInfo, String filename, String fileExtension, HttpServletResponse res, HttpServletRequest req) {
        try {
            res.setHeader("Access-Control-Allow-Origin", "*");
            res.setCharacterEncoding("utf-8");
            res.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
            res.setHeader("Content-Disposition", "attachment;filename=" + FileUtils.filenameEncoding(filename, req) + "." + fileExtension);
            res.setHeader("Content-Length", "" + fileInfo.getFileSize());
            download(fileInfo, res);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException("下载文件失败");
        }
    }

    public void download(FileInfo fileInfo, HttpServletResponse response) {
        String bucket = SecurityConstants.DETAILS_TENANT + fileInfo.getTenantId();
        GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(bucket)
                .object(fileInfo.getFileStorageName()).build();
        try (InputStream inputStream = client.getObject(objectArgs);
             OutputStream outputStream = response.getOutputStream()){
            byte buf[] = new byte[1024];
            int length = 0;

            // 输出文件
            while ((length = inputStream.read(buf)) > 0) {
                outputStream.write(buf, 0, length);
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("下载文件失败：{}",e.getMessage());
            throw new ServiceException("下载文件失败");
        }
    }

    @Override
    public void batchDownload(List<FileInfo> fileInfoList, String zipName, HttpServletResponse res, HttpServletRequest req) {

    }

    public void batchDownload(ZipOutputStream zipOutputStream, List<FileInfo> fileInfoList, String path) {
        fileInfoList.forEach(fileInfo -> {
            try {
                if (ConstantUtils.IS_DIRECTORY_FILE_1.equals(fileInfo.getFileIsDirectory())) {
                    zipOutputStream.putNextEntry(new ZipEntry(path + "/" + fileInfo.getFileName() + "/"));
                    zipOutputStream.closeEntry();
                    if (!fileInfo.getSysFileList().isEmpty()) {
                        batchDownload(zipOutputStream, fileInfo.getSysFileList(), path + "/" + fileInfo.getFileName() + "/");
                    }
                } else {
                    String bucket = SecurityConstants.DETAILS_TENANT + fileInfo.getTenantId();
                    zipOutputStream.putNextEntry(new ZipEntry(path + "/" + fileInfo.getFileName()));
                    GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(bucket)
                            .object(fileInfo.getFileStorageName()).build();
                    InputStream object = client.getObject(objectArgs);
                    byte buf[] = new byte[1024];
                    int length = 0;
                    while ((length = object.read(buf)) > 0) {
                        zipOutputStream.write(buf, 0, length);
                    }
                    zipOutputStream.closeEntry();
                }
            } catch (Exception e) {
                e.printStackTrace();
                throw new ServiceException("批量下载文件失败");
            }
        });
    }

}
