package com.multictrl.modules.business.service.impl;

import cn.hutool.core.io.file.FileNameUtil;
import com.multictrl.common.config.MinioConfig;
import com.multictrl.common.constant.BusinessConstant;
import com.multictrl.common.exception.ErrorCode;
import com.multictrl.common.exception.ExceptionUtils;
import com.multictrl.common.exception.RenException;
import com.multictrl.common.utils.DateUtils;
import com.multictrl.modules.business.service.MinioService;
import io.minio.*;
import io.minio.credentials.AssumeRoleProvider;
import io.minio.credentials.Credentials;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.Map;

/**
 * minio对象存储
 *
 * @author Sdy
 * @since 1.0.0 2026/4/16
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class MinioServiceImpl implements MinioService {
    private final MinioClient minioClient;
    private final MinioConfig minioConfig;

    private static final String policyTemplate = """
            {
                "Version": "2012-10-17",
                "Statement": [
                    {
                        "Effect": "Allow",
                        "Principal": "*",
                        "Action": ["s3:GetObject"],
                        "Resource": ["arn:aws:s3:::%s/*"]
                    },
                    {
                        "Effect": "Allow",
                        "Principal": "*",
                        "Action": ["s3:ListBucket"],
                        "Resource": ["arn:aws:s3:::%s"]
                    }
                ]
            }""";

    @Override
    public Credentials getCredentials() {
        try {
            AssumeRoleProvider provider = new AssumeRoleProvider(minioConfig.getEndpoint(), minioConfig.getAccessKey(),
                    minioConfig.getSecretKey(), 3600,
                    null, "us-east-1", null, null, null, null);
            return provider.fetch();
        } catch (NoSuchAlgorithmException e) {
            log.error("Failed to obtain sts.", e);
        }
        return null;
    }

    @Override
    public void createBucket(String bucketName) {
        try {
            boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            if (!exists) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
                //开放权限
                String policy = String.format(policyTemplate, bucketName, bucketName);
                minioClient.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucketName).config(policy).build());
            }
        } catch (Exception e) {
            log.error(ExceptionUtils.getErrorStackTrace(e));
            throw new RenException(ErrorCode.OSS_CREATE_BUCKET_ERROR);
        }
    }

    @Override
    public void removeBucket(String bucketName) {
        try {
            boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            if (exists) {
                minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
            }
        } catch (Exception e) {
            log.error(ExceptionUtils.getErrorStackTrace(e));
            throw new RenException(ErrorCode.OSS_REMOVE_BUCKET_ERROR);
        }
    }

    @Override
    public void uploadFile(InputStream inputStream, String bucketName, String fileName) {
        createBucket(bucketName);
        Path path = Paths.get(fileName);
        try {
            minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(fileName).
                    stream(inputStream, inputStream.available(), -1)
                    .contentType(getMimeType(fileName))
                    .headers(Map.of("Content-Disposition", "inline; filename=\"" +
                            URLEncoder.encode(path.getFileName().toString(), StandardCharsets.UTF_8) + "\""))
                    .build());
        } catch (Exception e) {
            log.error(ExceptionUtils.getErrorStackTrace(e));
            throw new RenException(ErrorCode.OSS_UPLOAD_FILE_ERROR, fileName);
        }
    }

    @Override
    public void removeFile(String bucketName, String fileName) {
        try {
            minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(fileName).build());
        } catch (Exception e) {
            log.error(ExceptionUtils.getErrorStackTrace(e));
            throw new RenException(ErrorCode.OSS_REMOVE_FILE_ERROR, fileName);
        }
    }

    @Override
    public String uploadRouteImg(String dockSn, MultipartFile file) {
        //文件路径
        String path = dockSn + "/" + DateUtils.format(new Date(), DateUtils.DATE_TIME_PATTERN_COMPACT) + "." + FileNameUtil.extName(file.getOriginalFilename());
        try {
            uploadFile(file.getInputStream(), BusinessConstant.ROUTE_IMG_BUCKET, path);
        } catch (Exception e) {
            log.error(ExceptionUtils.getErrorStackTrace(e));
            throw new RenException(ErrorCode.OSS_UPLOAD_FILE_ERROR, file.getOriginalFilename());
        }

        return BusinessConstant.IMAGE_PATH + BusinessConstant.ROUTE_IMG_BUCKET + "/" + path;
    }

    @Override
    public String uploadRouteKmz(String dockSn, byte[] bytes) {
        //文件路径
        String path = dockSn + "/" + DateUtils.format(new Date(), DateUtils.DATE_TIME_PATTERN_COMPACT) + ".kmz";
        try {
            uploadFile(new ByteArrayInputStream(bytes), BusinessConstant.ROUTE_KMZ_BUCKET, path);
        } catch (Exception e) {
            log.error("上传文件失败：{}", ExceptionUtils.getErrorStackTrace(e));
            throw new RenException(ErrorCode.OSS_UPLOAD_FILE_ERROR);
        }

        return BusinessConstant.ROUTE_KMZ_BUCKET + "/" + path;
    }

    @Override
    public String uploadFirmware(InputStream inputStream, String deviceName, String originalFilename) {
        //文件路径
        String path = deviceName + "/" + DateUtils.format(new Date(), DateUtils.DATE_TIME_PATTERN_COMPACT) + "." + FileNameUtil.extName(originalFilename);
        try {
            uploadFile(inputStream, BusinessConstant.DEVICE_FIRMWARE_BUCKET, path);
        } catch (Exception e) {
            log.error(ExceptionUtils.getErrorStackTrace(e));
            throw new RenException(ErrorCode.OSS_UPLOAD_FILE_ERROR, originalFilename);
        }

        return BusinessConstant.DEVICE_FIRMWARE_BUCKET + "/" + path;
    }

    @Override
    public String uploadSpeakerAudio(String dockSn, MultipartFile file) {
        //文件路径
        String path = dockSn + "/" + DateUtils.format(new Date(), DateUtils.DATE_TIME_PATTERN_COMPACT) + "." + FileNameUtil.extName(file.getOriginalFilename());
        try {
            uploadFile(file.getInputStream(), BusinessConstant.SPEAKER_AUDIO_BUCKET, path);
        } catch (Exception e) {
            log.error(ExceptionUtils.getErrorStackTrace(e));
            throw new RenException(ErrorCode.OSS_UPLOAD_FILE_ERROR, file.getOriginalFilename());
        }

        return BusinessConstant.SPEAKER_AUDIO_BUCKET + "/" + path;
    }

    @Override
    public String uploadGeoMark(String dockSn, MultipartFile file) {
        //文件路径
        String path = dockSn + "/" + DateUtils.format(new Date(), DateUtils.DATE_TIME_PATTERN_COMPACT) + "." + FileNameUtil.extName(file.getOriginalFilename());
        try {
            uploadFile(file.getInputStream(), BusinessConstant.GEO_MARK_BUCKET, path);
        } catch (Exception e) {
            log.error(ExceptionUtils.getErrorStackTrace(e));
            throw new RenException(ErrorCode.OSS_UPLOAD_FILE_ERROR, file.getOriginalFilename());
        }

        return BusinessConstant.GEO_MARK_BUCKET + "/" + path;
    }

    @Override
    public String uploadDictImage(MultipartFile file) {
        //文件路径
        String path = DateUtils.format(new Date(), DateUtils.DATE_TIME_PATTERN_COMPACT) + "." + FileNameUtil.extName(file.getOriginalFilename());
        try {
            uploadFile(file.getInputStream(), BusinessConstant.DICT_IMAGE_BUCKET, path);
        } catch (Exception e) {
            log.error(ExceptionUtils.getErrorStackTrace(e));
            throw new RenException(ErrorCode.OSS_UPLOAD_FILE_ERROR, file.getOriginalFilename());
        }

        return BusinessConstant.DICT_IMAGE_BUCKET + "/" + path;
    }

    private String getMimeType(String fileName) {
        if (fileName == null) return "application/octet-stream";

        String lowercaseFileName = fileName.toLowerCase();

        if (lowercaseFileName.endsWith(".png")) {
            return "image/png";
        } else if (lowercaseFileName.endsWith(".jpg") || lowercaseFileName.endsWith(".jpeg")) {
            return "image/jpeg";
        } else if (lowercaseFileName.endsWith(".gif")) {
            return "image/gif";
        } else if (lowercaseFileName.endsWith(".svg")) {
            return "image/svg+xml";
        } else if (lowercaseFileName.endsWith(".webp")) {
            return "image/webp";
        } else if (lowercaseFileName.endsWith(".pdf")) {
            return "application/pdf";
        } else if (lowercaseFileName.endsWith(".mp4")) {
            return "video/mp4";
        }

        // 默认值
        return "application/octet-stream";
    }
}
