package com.artfess.file.attachmentService;

import cn.hutool.core.util.StrUtil;
import com.artfess.base.attachment.Attachment;
import com.artfess.base.attachment.AttachmentService;
import com.artfess.base.exception.BaseException;
import com.artfess.base.util.AppUtil;
import com.artfess.base.util.BeanUtils;
import com.artfess.base.util.StringUtil;
import com.artfess.file.config.AliyunOssSettings;
import com.artfess.file.config.MinioSetting;
import com.artfess.file.model.FileStorage;
import com.artfess.file.params.FlowUploadPropertiesStorageDTO;
import com.artfess.file.persistence.manager.FileStorageManager;
import com.artfess.file.persistence.manager.FlowUploadPropertiesManager;
import com.artfess.file.util.CustomMinioClient;
import com.artfess.file.util.MinioUtil;
import com.google.common.collect.HashMultimap;
import io.minio.GetPresignedObjectUrlArgs;
import io.minio.ListPartsResponse;
import io.minio.errors.ErrorResponseException;
import io.minio.errors.InsufficientDataException;
import io.minio.errors.InternalException;
import io.minio.errors.InvalidResponseException;
import io.minio.errors.ServerException;
import io.minio.errors.XmlParserException;
import io.minio.http.Method;
import io.minio.messages.Part;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Paths;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class MinioAttachmentServiceImpl implements AttachmentService {

    @Autowired
    private CustomMinioClient customMinioClient;

    @Autowired
    private MinioSetting minioSetting;

    public MinioAttachmentServiceImpl(){}

    @Override
    public String getStoreType() {
        return "minio";
    }

    private MinioSetting initMinioSettings(Attachment attachment, String propertiesId) {
        MinioSetting ossSettings = AppUtil.getBean(MinioSetting.class);
        if (StringUtil.isNotEmpty(propertiesId)) {
            FileStorageManager fileStorageManager = AppUtil.getBean(FileStorageManager.class);
            FileStorage fileStorage = fileStorageManager.get(propertiesId);
            if (BeanUtils.isNotEmpty(fileStorage)) {
                ossSettings.setMinioName(fileStorage.getUserName());
                ossSettings.setMinioPass(fileStorage.getPassword());
                String minioUrl =fileStorage.getUrl();
                if(!minioUrl.startsWith("http")){
                    minioUrl = "http://" + minioUrl;
                }
                if(!minioUrl.endsWith("/")){
                    minioUrl = minioUrl.concat("/");
                }
                ossSettings.setMinioUrl(minioUrl);
                ossSettings.setBucketName(fileStorage.getLocation());
                attachment.setEntryptName(fileStorage.getEncryptName() == 0 ? false : true);
            }else{
                FlowUploadPropertiesManager uploadPropertiesManager = AppUtil.getBean(FlowUploadPropertiesManager.class);
                FlowUploadPropertiesStorageDTO uploadProperties = uploadPropertiesManager.getById(propertiesId);
                if (BeanUtils.isNotEmpty(uploadProperties)) {
                    ossSettings.setMinioName(uploadProperties.getUserName());
                    ossSettings.setMinioPass(uploadProperties.getPassword());
                    String minioUrl =uploadProperties.getUrl();
                    if(!minioUrl.startsWith("http")){
                        minioUrl = "http://" + minioUrl;
                    }
                    if(!minioUrl.endsWith("/")){
                        minioUrl = minioUrl.concat("/");
                    }
                    ossSettings.setMinioUrl(minioUrl);
                    ossSettings.setBucketName(uploadProperties.getLocation());
                    attachment.setEntryptName(uploadProperties.getEncryptName() == 0 ? false : true);
                }
            }
        }
        MinioUtil.setMinioUrl(ossSettings.getMinioUrl());
        MinioUtil.setMinioName(ossSettings.getMinioName());
        MinioUtil.setMinioPass(ossSettings.getMinioPass());
        MinioUtil.setBucketName(ossSettings.getBucketName());
        return ossSettings;
    }

    @Override
    public void remove(Attachment attachment, String propertiesId) throws Exception {
        String filePath = getFilePath(attachment);
        initMinioSettings(attachment,propertiesId);
        MinioUtil.removeObject(filePath,true);
    }

    @Override
    public void upload(Attachment attachment, InputStream inputStream, String propertiesId) throws Exception {
        String filePath = getFilePath(attachment);
        initMinioSettings(attachment,propertiesId);
        MinioUtil.uploadByInputStream(inputStream,filePath);
    }

    @Override
    public void download(Attachment attachment, OutputStream outStream, String propertiesId) throws Exception {
        String filePath = getFilePath(attachment);
        initMinioSettings(attachment,propertiesId);
        MinioUtil.downFile("",filePath,outStream,true);
    }

    @Override
    public boolean chekckFile(Attachment attachment, String propertiesId) throws Exception {
        initMinioSettings(attachment,propertiesId);
        return MinioUtil.checkFile("",attachment.getFilePath(),true);
    }

    @Override
    public byte[] getFileBytes(Attachment sysFile) throws Exception {
        return new byte[0];
    }

    @Override
    public String getUrl(Attachment attachment) {
        return MinioUtil.getPresignedObjectUrl2upload(attachment.getFilePath());
    }

    @Override
    public String initMultiPartUpload(Attachment attachment) {
        HashMultimap<String, String> headers = HashMultimap.create();
        if (StrUtil.isBlank(attachment.getContentType())) {
            headers.put("Content-Type", "application/octet-stream");
        }

        String uploadId = null;
        try {
            uploadId = customMinioClient.initMultiPartUpload(attachment.getBucket(), null, attachment.getFileName(), headers, null);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InsufficientDataException e) {
            e.printStackTrace();
        } catch (ServerException e) {
            e.printStackTrace();
        } catch (InternalException e) {
            e.printStackTrace();
        } catch (XmlParserException e) {
            e.printStackTrace();
        } catch (InvalidResponseException e) {
            e.printStackTrace();
        } catch (ErrorResponseException e) {
            e.printStackTrace();
        }
        return uploadId;
    }

    @Override
    public String getChunkUrl(Attachment attachment, Integer partNumber, String uploadId) {
        try {
            Map<String, String> reqParams = new HashMap<>();
            reqParams.put("partNumber", String.valueOf(partNumber));
            reqParams.put("uploadId", uploadId);
            if(StringUtil.isEmpty(attachment.getBucket())) {
                attachment.setBucket(minioSetting.getBucketName());
            }
            String uploadUrl = customMinioClient.getPresignedObjectUrl(
                    GetPresignedObjectUrlArgs.builder()
                            .method(Method.PUT)
                            .bucket(attachment.getBucket())
                            .object(attachment.getFileName())
                            .expiry(1, TimeUnit.DAYS)
                            .extraQueryParams(reqParams)
                            .build());
            return uploadUrl;
        } catch (ErrorResponseException e) {
            e.printStackTrace();
        } catch (InsufficientDataException e) {
            e.printStackTrace();
        } catch (InternalException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (InvalidResponseException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (XmlParserException e) {
            e.printStackTrace();
        } catch (ServerException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public boolean mergeMultipartUpload(Attachment attachment,String uploadId, int realyPartNumber) {
        Integer maxPart = 1000;
        Part[] parts = new Part[maxPart];
        ListPartsResponse partResult = null;
        try {
            partResult = customMinioClient.listMultipart(null, null, attachment.getFileName(), maxPart, 0, uploadId, null, null);
            int partNumber = 1;
            for (Part part : partResult.result().partList()) {
                parts[partNumber - 1] = new Part(partNumber, part.etag());
                partNumber++;
            }
            Integer completedPartCount = partResult.result().partList().size();
            // 若上传的分片少于实际分片数量，不能合并
            if (completedPartCount < realyPartNumber) {
                // 分片文件不够，不能合并
                log.info(">>>>>>>>>>{}分片文件不够，不能合并", uploadId);
                throw new BaseException("分片文件不够，不能合并！");
            }
            customMinioClient.mergeMultipartUpload(null, null, attachment.getFileName(), uploadId, parts, null, null);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

        return true;
    }

    /**
    * @Description: 获取文件相对路径
    * @param attachment  附件信息
    * @Return: 文件相对路径
    * @Author: llj
    * @Date: 2021/3/23 11:36
    */
    public String getFilePath(Attachment attachment){
        String fileParentPath = "";
        String filePath = attachment.getFilePath();
        if (StringUtil.isNotEmpty(filePath)) {
            String[] split = filePath.split("/");
            if(null != split && split.length > 1) {
                fileParentPath = Paths.get(filePath).getParent().toString();
                fileParentPath = fileParentPath.replaceAll("\\\\", "/");
                if (fileParentPath.startsWith("/")) {
                    fileParentPath = fileParentPath.substring(1);
                }
            }

        }
        String file =fileParentPath+ "/"+attachment.getId()+"."+attachment.getExtensionName();
        return file;
    }
}
