package com.njcn.gather.user.user.component;

import cn.hutool.core.util.StrUtil;
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.gather.user.user.config.StorageMinioProperties;
import io.minio.BucketExistsArgs;
import io.minio.GetObjectArgs;
import io.minio.GetObjectResponse;
import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.RemoveObjectArgs;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.http.MediaTypeFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.util.Locale;

/**
 * MinIO 通用文件存储服务。
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class SysFileMinioStorageService {

    private final StorageMinioProperties properties;

    public StoredObjectInfo save(String bucketName, String bizDir, String ownerId, String fileId, MultipartFile file) {
        MultipartFile normalizedFile = requireFile(file);
        validateConfig();
        String originalFileName = normalizeFileName(normalizedFile.getOriginalFilename());
        String extension = resolveExtension(originalFileName);
        String objectName = buildObjectName(bizDir, ownerId, fileId, extension);
        String contentType = resolveContentType(normalizedFile, originalFileName);
        try (InputStream inputStream = normalizedFile.getInputStream()) {
            MinioClient client = buildClient();
            ensureBucket(client, bucketName);
            client.putObject(PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .stream(inputStream, normalizedFile.getSize(), -1)
                    .contentType(contentType)
                    .build());
            return new StoredObjectInfo(originalFileName, bucketName, objectName, contentType, normalizedFile.getSize());
        } catch (BusinessException exception) {
            throw exception;
        } catch (Exception exception) {
            log.error("upload file to minio failed, bucketName={}, objectName={}", bucketName, objectName, exception);
            throw new BusinessException(CommonResponseEnum.FAIL, "上传文件失败");
        }
    }

    public StoredObject getObject(String bucketName, String objectName) {
        validateConfig();
        if (StrUtil.hasBlank(bucketName, objectName)) {
            throw new BusinessException(CommonResponseEnum.FAIL, "文件不存在");
        }
        try {
            GetObjectResponse response = buildClient().getObject(GetObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .build());
            return new StoredObject(response);
        } catch (BusinessException exception) {
            throw exception;
        } catch (Exception exception) {
            log.error("read file from minio failed, bucketName={}, objectName={}", bucketName, objectName, exception);
            throw new BusinessException(CommonResponseEnum.FAIL, "读取文件失败");
        }
    }

    public void deleteQuietly(String bucketName, String objectName) {
        if (StrUtil.hasBlank(bucketName, objectName)) {
            return;
        }
        try {
            validateConfig();
            buildClient().removeObject(RemoveObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .build());
        } catch (Exception exception) {
            log.warn("delete file from minio failed, bucketName={}, objectName={}", bucketName, objectName, exception);
        }
    }

    private MultipartFile requireFile(MultipartFile file) {
        if (file == null || file.isEmpty()) {
            throw new BusinessException(CommonResponseEnum.FAIL, "上传文件不能为空");
        }
        return file;
    }

    private void validateConfig() {
        if (StrUtil.hasBlank(properties.getEndpoint(), properties.getAccessKey(), properties.getSecretKey())) {
            throw new BusinessException(CommonResponseEnum.FAIL, "MinIO配置不完整");
        }
    }

    private MinioClient buildClient() {
        return MinioClient.builder()
                .endpoint(properties.getEndpoint())
                .credentials(properties.getAccessKey(), properties.getSecretKey())
                .build();
    }

    private void ensureBucket(MinioClient client, String bucketName) throws Exception {
        boolean exists = client.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        if (!exists) {
            client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
        }
    }

    private String normalizeFileName(String originalFileName) {
        String fileName = StrUtil.trimToEmpty(originalFileName);
        if (StrUtil.isBlank(fileName)) {
            throw new BusinessException(CommonResponseEnum.FAIL, "文件名不能为空");
        }
        int slashIndex = Math.max(fileName.lastIndexOf('/'), fileName.lastIndexOf('\\'));
        return slashIndex >= 0 ? fileName.substring(slashIndex + 1) : fileName;
    }

    private String resolveExtension(String originalFileName) {
        int dotIndex = originalFileName.lastIndexOf('.');
        if (dotIndex < 0 || dotIndex == originalFileName.length() - 1) {
            return "bin";
        }
        return originalFileName.substring(dotIndex + 1).toLowerCase(Locale.ENGLISH);
    }

    private String buildObjectName(String bizDir, String ownerId, String fileId, String extension) {
        StringBuilder builder = new StringBuilder();
        builder.append(trimPathSegment(bizDir));
        if (StrUtil.isNotBlank(ownerId)) {
            builder.append("/").append(trimPathSegment(ownerId));
        }
        builder.append("/").append(fileId);
        if (StrUtil.isNotBlank(extension)) {
            builder.append(".").append(extension);
        }
        return builder.toString();
    }

    private String trimPathSegment(String value) {
        String text = StrUtil.trimToEmpty(value);
        String normalized = text.replace("\\", "/");
        while (normalized.startsWith("/")) {
            normalized = normalized.substring(1);
        }
        while (normalized.endsWith("/")) {
            normalized = normalized.substring(0, normalized.length() - 1);
        }
        if (StrUtil.isBlank(normalized)) {
            throw new BusinessException(CommonResponseEnum.FAIL, "对象目录配置不合法");
        }
        return normalized;
    }

    private String resolveContentType(MultipartFile file, String originalFileName) {
        if (StrUtil.isNotBlank(file.getContentType())) {
            return file.getContentType();
        }
        return MediaTypeFactory.getMediaType(originalFileName)
                .map(MediaType::toString)
                .orElse(MediaType.APPLICATION_OCTET_STREAM_VALUE);
    }

    @Getter
    public static class StoredObjectInfo {
        private final String fileName;
        private final String bucketName;
        private final String objectName;
        private final String contentType;
        private final long fileSize;

        public StoredObjectInfo(String fileName, String bucketName, String objectName, String contentType, long fileSize) {
            this.fileName = fileName;
            this.bucketName = bucketName;
            this.objectName = objectName;
            this.contentType = contentType;
            this.fileSize = fileSize;
        }
    }

    @Getter
    public static class StoredObject {
        private final GetObjectResponse inputStream;

        public StoredObject(GetObjectResponse inputStream) {
            this.inputStream = inputStream;
        }
    }
}
