package com.css.simulation.resource.common.adapter.controller;

import api.common.pojo.common.ResponseBodyVO;
import api.common.pojo.constants.DictConstants;
import api.common.pojo.param.MinioParameter;
import api.common.util.FileUtil;
import api.common.util.ObjectUtil;
import api.common.util.TimeUtil;
import com.css.simulation.resource.common.infra.config.minio.MinioConfiguration;
import com.css.simulation.resource.common.infra.config.minio.SubMinioClient;
import com.css.simulation.resource.common.infra.mysql.mapper.AlgorithmMapper;
import com.css.simulation.resource.common.infra.util.MinioUtil;
import io.minio.CreateMultipartUploadResponse;
import io.minio.ListPartsResponse;
import io.minio.errors.*;
import io.minio.http.Method;
import io.minio.messages.Part;
import lombok.SneakyThrows;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.*;

@RestController
@RequestMapping("/minio")
public class MinioController {

    @Resource(name = "minioClientPrivate")
    private SubMinioClient minioClientPrivate;

    @Resource
    private MinioConfiguration minioConfiguration;

    @Resource
    private AlgorithmMapper algorithmMapper;


    @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public ResponseBodyVO<String> upload(
            @RequestPart("file") MultipartFile file,
            @RequestParam("objectName") String objectName//  "/xx/xxx/x/xx"
    ) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        MinioUtil.uploadFromMultipartFile(
                minioClientPrivate,
                file,
                minioConfiguration.getBucketName(),
                objectName
        );
//        String previewUrl = MinioUtil.getPreviewUrl(minioClientPublic, Method.GET, minioConfiguration.getBucketName(), objectName);
        String previewUrl = MinioUtil.getPublicPreviewUrl(minioClientPrivate, Method.GET, minioConfiguration.getBucketName(), objectName,
                minioConfiguration.getEndpointPrivate(), minioConfiguration.getEndpointPublic());
        return new ResponseBodyVO<>(ResponseBodyVO.Response.SUCCESS, "请求成功！", previewUrl);
    }

    @GetMapping(value = "/uploadLocalFile")
    public ResponseBodyVO<String> uploadLocalFile(
            @RequestParam("localFilePath") String localFilePath,
            @RequestParam("objectName") String objectName//  "/xx/xxx/x/xx"
    ) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        MinioUtil.uploadFromFile(
                minioClientPrivate,
                localFilePath,
                minioConfiguration.getBucketName(),
                objectName
        );
//        String previewUrl = MinioUtil.getPreviewUrl(minioClientPublic, Method.GET, minioConfiguration.getBucketName(), objectName);
        String previewUrl = MinioUtil.getPublicPreviewUrl(minioClientPrivate, Method.GET, minioConfiguration.getBucketName(), objectName,
                minioConfiguration.getEndpointPrivate(), minioConfiguration.getEndpointPublic());
        return new ResponseBodyVO<>(ResponseBodyVO.Response.SUCCESS, "请求成功！", previewUrl);
    }

    @GetMapping(value = "/getPublicUrl")
    public ResponseBodyVO<String> getPublicUrl(@RequestParam("objectName") String objectName){
        String previewUrl = MinioUtil.getPublicPreviewUrl(minioClientPrivate, Method.GET, minioConfiguration.getBucketName(), objectName,
            minioConfiguration.getEndpointPrivate(), minioConfiguration.getEndpointPublic());
        return new ResponseBodyVO<>(ResponseBodyVO.Response.SUCCESS, "请求成功！", previewUrl);
    }

    @GetMapping(value = "/copy")
    public ResponseBodyVO<String> copy(
            @RequestParam("source") String source,
            @RequestParam("target") String target//  "/xx/xxx/x/xx"
    ) {
        MinioUtil.copyObject(
                minioClientPrivate,
                minioConfiguration.getBucketName(),
                source,
                minioConfiguration.getBucketName(),
                target
        );
        return new ResponseBodyVO<>(ResponseBodyVO.Response.SUCCESS);
    }

    @GetMapping(value = "/move")
    public ResponseBodyVO<String> move(
            @RequestParam("source") String source,
            @RequestParam("target") String target//  "/xx/xxx/x/xx"
    ) {
        MinioUtil.moveObject(
                minioClientPrivate,
                minioConfiguration.getBucketName(),
                source,
                minioConfiguration.getBucketName(),
                target
        );
        return new ResponseBodyVO<>(ResponseBodyVO.Response.SUCCESS);
    }


    @PostMapping("/list")
    @SneakyThrows
    public ResponseBodyVO<List<String>> list(@RequestBody @Validated MinioParameter minioParameter) {
        List<String> objects = MinioUtil.listObjects(minioClientPrivate, minioConfiguration.getBucketName(), minioParameter.getObjectName());
        return new ResponseBodyVO<>(ResponseBodyVO.Response.SUCCESS, objects);
    }

    @PostMapping("/listDeepOne")
    @SneakyThrows
    public ResponseBodyVO<List<String>> listDeepOne(
            @RequestBody @Validated MinioParameter minioParameter
    ) {
        List<String> objects = MinioUtil.listObjectsUnRecursive(minioClientPrivate, minioConfiguration.getBucketName(), minioParameter.getObjectName());
        return new ResponseBodyVO<>(ResponseBodyVO.Response.SUCCESS, objects);
    }

    @PostMapping("/download")
    public void download(
            @RequestBody @Validated MinioParameter minioParameter,
            HttpServletResponse response
    ) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        InputStream inputStream = MinioUtil.downloadToStream(
                minioClientPrivate,
                minioConfiguration.getBucketName(),
                minioParameter.getObjectName()
        );
        String fileName = FileUtil.getFileName(minioParameter.getObjectName());
        FileUtil.downloadForHttp(fileName, inputStream, response, 1024);
    }

    @RequestMapping("/preview")
    public void preview(@RequestParam("objectName") String objectName, HttpServletResponse response) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        InputStream inputStream = MinioUtil.downloadToStream(
                minioClientPrivate,
                minioConfiguration.getBucketName(),
                objectName
        );
        String fileName = FileUtil.getFileName(objectName);
        FileUtil.downloadForHttp(fileName, inputStream, response, 1024);
    }

    @RequestMapping("/getPreviewUrl")
    public ResponseBodyVO<String> getPreviewUrl(@RequestBody @Validated MinioParameter minioParameter) {
//        String previewUrl = MinioUtil.getPreviewUrl(minioClientPublic, Method.GET, minioConfiguration.getBucketName(), minioParameter.getObjectName());
        String previewUrl = MinioUtil.getPublicPreviewUrl(
                minioClientPrivate,
                Method.GET,
                minioConfiguration.getBucketName(),
                minioParameter.getObjectName(),
                minioConfiguration.getEndpointPrivate(),
                minioConfiguration.getEndpointPublic()
        );
        return new ResponseBodyVO<>(ResponseBodyVO.Response.SUCCESS, "请求成功！", previewUrl);
    }

    @RequestMapping("/createMultipartUpload")
    @ResponseBody
    public ResponseBodyVO<Map<String, Object>> createMultipartUpload(@RequestBody Map<String, String> paramMap) {
        try {
            String objectName = paramMap.get("objectName");
            String chunkSize = paramMap.get("chunkSize");
            String type = paramMap.get("type");
            if (ObjectUtil.isNull(objectName) || ObjectUtil.isNull(chunkSize) || ObjectUtil.isNull(type)) {
                return new ResponseBodyVO<>(ResponseBodyVO.Response.CLIENT_FAILURE, "请求参数缺失！");
            }
            Integer nowTime = TimeUtil.getRq(new Date(), 0);
            if (type.equals(DictConstants.ALGORITHM_FILE)) {
                objectName = "algorithm/" + nowTime + "/" + objectName;
            }
            Map<String, Object> result = new HashMap<>();//结果数据
            CreateMultipartUploadResponse multipartUpload = minioClientPrivate.createMultipartUpload(minioConfiguration.getBucketName(), null, objectName, null, null);
            String uploadId = multipartUpload.result().uploadId();
            List<String> UrlList = new LinkedList<>();
            Map<String, String> queryParam = new HashMap<>();
            queryParam.put("uploadId", uploadId);
            int size = Integer.parseInt(chunkSize);
            for (int i = 1; i <= size; i++) {
                queryParam.put("partNumber", String.valueOf(i));
                // 将 url 中的内网ip更换称外网ip
                String presignedObjectUrl = MinioUtil.getPublicPreviewUrl2(
                        minioClientPrivate,
                        Method.PUT,
                        minioConfiguration.getBucketName(),
                        objectName,
                        queryParam,
                        minioConfiguration.getEndpointPrivate(),
                        minioConfiguration.getEndpointPublic()
                );
                UrlList.add(presignedObjectUrl);
            }
            result.put("uploadId", uploadId);
            result.put("uploadUrls", UrlList);
            result.put("objectName", objectName);
            return new ResponseBodyVO<>(ResponseBodyVO.Response.SUCCESS, result);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @RequestMapping("/completeMultipartUpload")
    @ResponseBody
    public ResponseBodyVO<String> completeMultipartUpload(@RequestBody Map<String, String> paramMap) throws Exception {
        String objectName = paramMap.get("objectName");
        String uploadId = paramMap.get("uploadId");
        if (ObjectUtil.isNull(objectName) || ObjectUtil.isNull(uploadId)) {
            return new ResponseBodyVO<>(ResponseBodyVO.Response.CLIENT_FAILURE, "请求参数缺失！");
        }
        ListPartsResponse listPartsResponse = minioClientPrivate.listParts(minioConfiguration.getBucketName(), null, objectName, 10000, 0, uploadId, null, null);
        List<Part> parts = listPartsResponse.result().partList();
        Part[] partArr = new Part[parts.size()];
        int partNumber = 1;
        for (Part part : parts) {
            partArr[partNumber - 1] = new Part(partNumber, part.etag());
            partNumber++;
        }
        minioClientPrivate.completeMultipartUpload(minioConfiguration.getBucketName(), null, objectName, uploadId, partArr, null, null);
        return new ResponseBodyVO<>(ResponseBodyVO.Response.SUCCESS, "合并成功！");
    }

    @PostMapping("/remove")
    public void download(@RequestBody @Validated MinioParameter minioParameter) {

        MinioUtil.removeObject(minioClientPrivate, minioConfiguration.getBucketName(), minioParameter.getObjectName());
    }

    // ------- 清理 minio 无用文件 -------
    @PostMapping("clean")
    @SneakyThrows
    public ResponseBodyVO<String> clean() {
        //1 清理算法文件
        {
            List<String> objects = MinioUtil.listObjects(minioClientPrivate, minioConfiguration.getBucketName(), "algorithm");

            List<String> strings = algorithmMapper.selectMinioPaths();
            for (String object : objects) {
                if (!strings.contains(object)) {
                    MinioUtil.removeObject(minioClientPrivate, minioConfiguration.getBucketName(), object);
                }
            }
        }

        //2 清理项目文件
        {

        }

        return new ResponseBodyVO<>(ResponseBodyVO.Response.SUCCESS, "清理垃圾文件完成！");

    }

}
