package com.snack.server.common.storage;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.S3Configuration;
import software.amazon.awssdk.services.s3.model.BucketAlreadyOwnedByYouException;
import software.amazon.awssdk.services.s3.model.CreateBucketRequest;
import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
import software.amazon.awssdk.services.s3.model.HeadBucketRequest;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;

import java.net.URI;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

/**
 * S3 协议对象存储实现
 *
 * 支持：RustFS / MinIO / AWS S3 / 阿里云 OSS（S3 兼容模式）
 * 仅当 application.yml 中 snack.storage.type=s3 时被实例化
 *
 * 存储路径：{bucket}/{bizType}/{yyyy-MM}/{uuid}.{ext}
 * 访问路径：{publicDomain}/{bizType}/{yyyy-MM}/{uuid}.{ext}
 */
@Slf4j
@Component
@ConditionalOnProperty(name = "snack.storage.type", havingValue = "s3")
public class S3FileStorage implements FileStorage, DisposableBean {

    private final S3Client s3Client;
    private final S3StorageProperties props;
    private final String publicDomain;

    public S3FileStorage(S3StorageProperties props) {
        this.props = props;
        if (StrUtil.isBlank(props.getEndpoint())
                || StrUtil.isBlank(props.getAccessKey())
                || StrUtil.isBlank(props.getBucket())) {
            throw new IllegalStateException(
                    "S3 存储配置不完整：请在 application.yml 配置 snack.s3.{endpoint,access-key,secret-key,bucket}");
        }
        this.s3Client = S3Client.builder()
                .endpointOverride(URI.create(props.getEndpoint()))
                .region(Region.of(props.getRegion()))
                .credentialsProvider(StaticCredentialsProvider.create(
                        AwsBasicCredentials.create(props.getAccessKey(), props.getSecretKey())))
                .serviceConfiguration(S3Configuration.builder()
                        .pathStyleAccessEnabled(props.getPathStyleAccess())
                        .build())
                .build();
        // 启动时确保 bucket 存在
        ensureBucket();
        // 公网访问域名前缀
        this.publicDomain = StrUtil.removeSuffix(
                StrUtil.blankToDefault(props.getPublicDomain(),
                        props.getEndpoint() + "/" + props.getBucket()),
                "/");
        log.info("[S3 存储] 已初始化 endpoint={} bucket={} publicDomain={}",
                props.getEndpoint(), props.getBucket(), publicDomain);
    }

    /**
     * 检查并自动创建 bucket
     */
    private void ensureBucket() {
        try {
            s3Client.headBucket(HeadBucketRequest.builder()
                    .bucket(props.getBucket())
                    .build());
            log.info("S3 bucket 已存在：{}", props.getBucket());
        } catch (BucketAlreadyOwnedByYouException e) {
            // ignore
        } catch (Exception e) {
            // 大多数服务端对 headBucket 的 404 抛 NoSuchBucket，尝试创建
            try {
                s3Client.createBucket(CreateBucketRequest.builder()
                        .bucket(props.getBucket())
                        .build());
                log.info("S3 bucket 已创建：{}", props.getBucket());
            } catch (BucketAlreadyOwnedByYouException ignore) {
                // 并发场景
            } catch (Exception ex) {
                log.warn("S3 bucket 检查 / 创建失败（可能为只读账号）：{}", ex.getMessage());
            }
        }
    }

    @Override
    public FileStoreResult store(byte[] bytes, String originalName, String bizType) {
        if (bytes == null || bytes.length == 0) {
            throw new IllegalArgumentException("文件内容为空");
        }
        String ext = StrUtil.subAfter(originalName, '.', true);
        if (StrUtil.isBlank(ext)) ext = "bin";
        ext = ext.toLowerCase();

        String biz = StrUtil.blankToDefault(bizType, "common");
        String monthDir = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM"));
        String relativeDir = biz + "/" + monthDir;
        String storedName = IdUtil.fastSimpleUUID() + "." + ext;
        String key = relativeDir + "/" + storedName;

        PutObjectRequest putReq = PutObjectRequest.builder()
                .bucket(props.getBucket())
                .key(key)
                .contentType(detectContentType(ext))
                .contentLength((long) bytes.length)
                .build();
        s3Client.putObject(putReq, RequestBody.fromBytes(bytes));

        FileStoreResult result = new FileStoreResult();
        result.setPath(key);
        result.setStoredName(storedName);
        result.setUrl(publicDomain + "/" + key);
        result.setName(originalName);
        result.setSize((long) bytes.length);
        result.setContentType(detectContentType(ext));
        log.info("[S3 存储] 上传成功 bucket={} key={} size={}",
                props.getBucket(), key, bytes.length);
        return result;
    }

    @Override
    public void delete(String path) {
        if (StrUtil.isBlank(path)) return;
        // 兼容 path = URL 的情况
        if (path.startsWith("http://") || path.startsWith("https://")) {
            path = parsePathFromUrl(path);
            if (path == null) return;
        }
        try {
            s3Client.deleteObject(DeleteObjectRequest.builder()
                    .bucket(props.getBucket())
                    .key(path)
                    .build());
            log.info("[S3 存储] 删除成功 key={}", path);
        } catch (Exception e) {
            log.error("[S3 存储] 删除失败 key={}", path, e);
        }
    }

    @Override
    public String getAccessUrl(String path) {
        if (StrUtil.isBlank(path)) return null;
        if (path.startsWith("http://") || path.startsWith("https://")) {
            return path;
        }
        return publicDomain + "/" + path;
    }

    /**
     * 从 URL 反查 S3 key
     */
    public String parsePathFromUrl(String url) {
        if (url == null) return null;
        if (!url.startsWith("http://") && !url.startsWith("https://")) {
            return url;
        }
        // 去掉 publicDomain 前缀
        if (publicDomain != null && url.startsWith(publicDomain)) {
            return url.substring(publicDomain.length() + 1);
        }
        // 兜底：去掉 endpoint + bucket
        String prefix = props.getEndpoint() + "/" + props.getBucket() + "/";
        if (url.startsWith(prefix)) {
            return url.substring(prefix.length());
        }
        // 最后兜底：取 bucket 之后的部分
        int idx = url.indexOf(props.getBucket() + "/");
        if (idx != -1) {
            return url.substring(idx + props.getBucket().length() + 1);
        }
        return null;
    }

    private String detectContentType(String ext) {
        return switch (ext) {
            case "jpg", "jpeg" -> "image/jpeg";
            case "png"          -> "image/png";
            case "gif"          -> "image/gif";
            case "webp"         -> "image/webp";
            case "pdf"          -> "application/pdf";
            case "mp4"          -> "video/mp4";
            default             -> "application/octet-stream";
        };
    }

    @Override
    public void destroy() {
        if (s3Client != null) {
            s3Client.close();
            log.info("[S3 存储] S3Client 已关闭");
        }
    }
}
