package tech.easyflow.agent.runtime.artifact;

import io.minio.GetObjectArgs;
import io.minio.PutObjectArgs;
import io.minio.RemoveObjectArgs;
import io.minio.StatObjectArgs;
import io.minio.StatObjectResponse;
import io.minio.errors.ErrorResponseException;
import org.dromara.x.file.storage.core.FileStorageService;
import org.dromara.x.file.storage.core.platform.MinioFileStorage;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.io.InputStream;

/**
 * 复用 x-file-storage 中固定私有平台的 Agent Artifact 对象存储适配器。
 */
@Component
public class AgentArtifactObjectStorage {

    /** 固定私有产物平台，不允许配置回退。 */
    public static final String PLATFORM = "minio-agent-artifacts";

    private final FileStorageService fileStorageService;

    /**
     * 创建对象存储适配器。
     *
     * @param fileStorageService x-file-storage 聚合服务
     */
    public AgentArtifactObjectStorage(FileStorageService fileStorageService) {
        this.fileStorageService = fileStorageService;
    }

    /**
     * 应用就绪时校验固定私有平台，缺失或配置公开域名时 fail-fast。
     */
    @EventListener(ApplicationReadyEvent.class)
    public void validatePlatform() {
        MinioFileStorage storage = storage();
        if (StringUtils.hasText(storage.getDomain())) {
            throw new IllegalStateException("Agent Artifact 存储必须使用无公开域名的私有 MinIO 平台");
        }
        try {
            boolean exists = storage.getClient().bucketExists(
                    io.minio.BucketExistsArgs.builder().bucket(storage.getBucketName()).build());
            if (!exists) {
                throw new IllegalStateException("Agent Artifact 私有 MinIO bucket 不存在");
            }
        } catch (IllegalStateException error) {
            throw error;
        } catch (Exception error) {
            throw new IllegalStateException("校验 Agent Artifact 私有 MinIO bucket 失败", error);
        }
    }

    /**
     * 流式写入对象。
     *
     * @param objectKey 业务对象键
     * @param input 输入流，由调用方关闭
     * @param size 已校验字节数
     * @param mimeType MIME 类型
     * @return 对象 ETag
     */
    public String put(String objectKey, InputStream input, long size, String mimeType) {
        try {
            MinioFileStorage storage = storage();
            return storage.getClient().putObject(PutObjectArgs.builder()
                    .bucket(storage.getBucketName())
                    .object(fullKey(storage, objectKey))
                    .stream(input, size, -1)
                    .contentType(mimeType)
                    .build()).etag();
        } catch (Exception error) {
            throw new AgentArtifactOperationException(
                    "ARTIFACT_STORAGE_UNAVAILABLE", "产物存储暂时不可用", true, error);
        }
    }

    /**
     * 查询私有对象实际元数据。
     *
     * @param objectKey 业务对象键
     * @return 对象实际大小与 ETag
     */
    public StoredObjectMetadata stat(String objectKey) {
        try {
            MinioFileStorage storage = storage();
            StatObjectResponse response = storage.getClient().statObject(StatObjectArgs.builder()
                    .bucket(storage.getBucketName())
                    .object(fullKey(storage, objectKey))
                    .build());
            return new StoredObjectMetadata(response.size(), response.etag());
        } catch (Exception error) {
            throw new AgentArtifactOperationException(
                    "ARTIFACT_STORAGE_UNAVAILABLE", "读取产物对象元数据失败", true, error);
        }
    }

    /**
     * 打开私有对象读取流。
     *
     * @param objectKey 业务对象键
     * @return MinIO 输入流，由调用方关闭
     * @throws IOException 对象读取失败
     */
    public InputStream open(String objectKey) throws IOException {
        try {
            MinioFileStorage storage = storage();
            return storage.getClient().getObject(GetObjectArgs.builder()
                    .bucket(storage.getBucketName())
                    .object(fullKey(storage, objectKey))
                    .build());
        } catch (Exception error) {
            throw new IOException("读取 Agent Artifact 对象失败", error);
        }
    }

    /**
     * 幂等删除一个私有对象。
     *
     * @param objectKey 业务对象键
     */
    public void delete(String objectKey) {
        try {
            MinioFileStorage storage = storage();
            storage.getClient().removeObject(RemoveObjectArgs.builder()
                    .bucket(storage.getBucketName())
                    .object(fullKey(storage, objectKey))
                    .build());
        } catch (ErrorResponseException error) {
            String code = error.errorResponse() == null ? null : error.errorResponse().code();
            if ("NoSuchKey".equals(code) || "NoSuchObject".equals(code)) {
                return;
            }
            throw new AgentArtifactOperationException(
                    "ARTIFACT_STORAGE_UNAVAILABLE", "产物对象删除失败", true, error);
        } catch (Exception error) {
            throw new AgentArtifactOperationException(
                    "ARTIFACT_STORAGE_UNAVAILABLE", "产物对象删除失败", true, error);
        }
    }

    private MinioFileStorage storage() {
        MinioFileStorage storage = fileStorageService.getFileStorage(PLATFORM);
        if (storage == null) {
            throw new IllegalStateException("缺少固定 x-file-storage 平台: " + PLATFORM);
        }
        return storage;
    }

    private String fullKey(MinioFileStorage storage, String objectKey) {
        String basePath = storage.getBasePath();
        if (!StringUtils.hasText(basePath)) {
            return objectKey;
        }
        return basePath.replaceAll("/+$", "") + "/" + objectKey.replaceAll("^/+", "");
    }

    /**
     * 私有对象存储返回的可信元数据。
     *
     * @param size 实际对象字节数
     * @param etag 对象 ETag
     */
    public record StoredObjectMetadata(long size, String etag) {
    }
}
