package com.changfa.multi.template;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.changfa.constants.ApiResult;
import com.changfa.entity.FileData;
import com.changfa.multi.config.MyMinioClient;
import com.changfa.multi.util.MinioUtil;
import com.changfa.service.IFileDataService;
import com.changfa.utils.MinioProperties;
import com.changfa.utils.MinioUtilS;
import com.google.common.collect.Multimap;
import io.minio.*;
import io.minio.errors.*;
import io.minio.http.Method;
import io.minio.messages.Part;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
import java.util.UUID;

@Slf4j
@AllArgsConstructor
@Component
public class MinioTemplate {

    @Autowired
    private IFileDataService fileDataService;
    @Autowired
    private MinioProperties minioProperties;

    @Autowired
    private MinioUtilS minioUtilS;

    @Autowired
    private MyMinioClient minioClient;


    /**
     *  上传分片上传请求，返回uploadId
     */
    public CreateMultipartUploadResponse uploadId( String objectName, Multimap<String, String> extraQueryParams) throws NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, ServerException, XmlParserException, ErrorResponseException, InternalException, InvalidResponseException {
        return minioClient.createMultipartUpload(minioProperties.getBucketName(), objectName, extraQueryParams);
    }
    @SneakyThrows
    @Transactional
    public ApiResult upload(MultipartFile file) {
       return ApiResult.success( notExistRecord(file));

    }

    @SneakyThrows
    @Transactional
    public ApiResult removeFile(String id) {
        FileData fileData = fileDataService.getById(id);
        if (!ObjectUtils.isEmpty(fileData)) {
            fileDataService.removeById(id);
            minioUtilS.removeObject(minioProperties.getBucketName(), fileData.getFileName() + fileData.getFileExtension());
        }
        return ApiResult.success();
    }

    public FileData notExistRecord(MultipartFile file) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        String fileName = UUID.randomUUID().toString().replace("-", "");
        String originalFilename = file.getOriginalFilename();
        String[] split = originalFilename.split("\\.");
        String fileExe = "." + split[split.length - 1];
        PutObjectArgs args = getPutObjectArgs(file,fileName,fileExe);
        Multimap<String, String> headers = MinioUtil.getHeaders(args);
        String uploadId = null;
        if (args.partCount() > 1) {
            uploadId = MinioUtil.createUploadId(args, headers);
        }
        String bucketName = minioProperties.getBucketName();
        String separator = minioProperties.getSeparator();
        String showPath = minioProperties.getShowPath();
        String downloadPath = minioProperties.getDownloadPath();

        FileData fileData = new FileData(showPath + separator + fileName, file.getSize(), fileExe, fileName, originalFilename.substring(0, originalFilename.lastIndexOf(".")), minioProperties.getFilePath(), downloadPath + separator + fileName, bucketName);
        fileDataService.save(fileData);
        // 分片是从1开始
        doUpload(args, 1, uploadId, headers);
        return fileData;
    }


    public  PutObjectArgs getPutObjectArgs(MultipartFile file,String fileName,String fileExt) throws IOException {
        InputStream stream = file.getInputStream();
        return PutObjectArgs.builder()
                .object(fileName+fileExt)
                .bucket(MinioUtil.BUCKET_NAME)
                .contentType("application/octet-stream")
                .stream(stream, stream.available(), MinioUtil.PART_MAX_SIZE).build();
    }

    public String doUpload(PutObjectArgs args, Integer currentPart, String uploadId, Multimap<String, String> headers) throws IOException, InternalException, ServerException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException {
        String fileUrl = MinioUtil.URL + "/" + args.bucket() + "/" + args.object();
        // 如果上传的文件大小小于分片大小，整体上传
        if (null == uploadId || "".equals(uploadId)) {
            MinioUtil.upload(args);
            return fileUrl;
        }

        long uploadedSize = 0L;
        // 分片数量
        int partCount = args.partCount();
        // 分片大小
        long partSize = args.partSize();
        // 文件大小
        long objectSize = args.objectSize();
        BufferedInputStream data = args.stream();
        Part[] parts = new Part[partCount];
        for (int partNumber = currentPart; partNumber <= partCount; ++partNumber) {
            long availableSize = partSize;
            if (partNumber == partCount) {
                availableSize = objectSize - uploadedSize;
            }
            String etag = MinioUtil.uploadPart(args, data, availableSize, uploadId, partNumber, headers);
            parts[partNumber - 1] = new Part(partNumber, etag);
            uploadedSize += availableSize;
        }
        MinioUtil.completeMultipartUpload(args, uploadId, parts);
        return fileUrl;
    }

    /**
     * 返回临时带签名、过期时间一天、Get请求方式的访问URL
     *
     * @param ossFilePath Oss文件路径
     * @return
     */
    @SneakyThrows
    public String getPresignedObjectUrl(String ossFilePath,Map<String, String> queryParams) {
        return minioClient.getPresignedObjectUrl(
                GetPresignedObjectUrlArgs.builder()
                        .method(Method.PUT)
                        .bucket(minioProperties.getBucketName())
                        .object(ossFilePath)
                        .expiry(60 * 60 * 24)
                        .extraQueryParams(queryParams)
                        .build());
    }

    /**
     * 查询分片数据
     *
     * @param region           区域
     * @param objectName       对象名
     * @param uploadId         上传ID
     * @param extraHeaders     额外消息头
     * @param extraQueryParams 额外查询参数
     */
    public ListPartsResponse listMultipart(String region, String objectName, Integer maxParts, Integer partNumberMarker, String uploadId, Multimap<String, String> extraHeaders, Multimap<String, String> extraQueryParams) throws NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, ServerException, XmlParserException, ErrorResponseException, InternalException, InvalidResponseException {
        return minioClient.listMultipart(minioProperties.getBucketName(), region, objectName, maxParts, partNumberMarker, uploadId, extraHeaders, extraQueryParams);
    }


    /**
     * 完成分片上传，执行合并文件
     *
     * @param objectName       对象名
     * @param uploadId         上传ID
     */
    @Transactional(rollbackFor = Throwable.class)
    public FileData completeMultipartUpload(String objectName, String uploadId,long fileSize,String fileExe,String originalFilename) throws NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, ServerException, XmlParserException, ErrorResponseException, InternalException, InvalidResponseException {
        Part[] parts = new Part[10000];
        String filename = objectName + fileExe;
        ListPartsResponse partResult = listMultipart(null, filename, 1000, 0, uploadId, null, null);
        int partNumber = 1;
        System.err.println(partResult.result().partList().size() + "========================");
        for (Part part : partResult.result().partList()) {
            parts[partNumber - 1] = new Part(partNumber, part.etag());
            partNumber++;
        }
        String bucketName = minioProperties.getBucketName();
        String separator = minioProperties.getSeparator();
        String showPath = minioProperties.getShowPath();
        String downloadPath = minioProperties.getDownloadPath();
        FileData fileData = new FileData(showPath + separator + objectName, fileSize, fileExe, objectName, originalFilename.substring(0, originalFilename.lastIndexOf(".")), minioProperties.getFilePath(), downloadPath + separator + objectName, bucketName);
        fileDataService.save(fileData);
        minioClient.completeMultipartUpload(minioProperties.getBucketName(), null, filename, uploadId, parts, null, null);
        return fileData;
    }
}
