// Package storage 提供对象存储基础设施
// 封装 MinIO 客户端初始化和存储桶自动创建逻辑
package storage

import (
	"context"
	"fmt"
	"strings"

	"github.com/echochat/backend/config"
	"github.com/minio/minio-go/v7"
	"github.com/minio/minio-go/v7/pkg/credentials"
)

// NewMinioClient 初始化 MinIO 客户端并确保存储桶存在
// 启动时：
//  1. 创建配置中指定的 bucket（如不存在）
//  2. 为 bucket 设置 public-read 策略，使前端可通过 URL 直接访问已上传的图片、语音、文件等资源
//     （写入操作仍需 AccessKey/SecretKey 凭据，不影响安全性）
func NewMinioClient(cfg *config.MinioConfig) (*minio.Client, error) {
	client, err := minio.New(cfg.Endpoint, &minio.Options{
		Creds:  credentials.NewStaticV4(cfg.AccessKey, cfg.SecretKey, ""),
		Secure: cfg.UseSSL,
	})
	if err != nil {
		return nil, fmt.Errorf("创建 MinIO 客户端失败: %w", err)
	}

	ctx := context.Background()
	exists, err := client.BucketExists(ctx, cfg.Bucket)
	if err != nil {
		return nil, fmt.Errorf("检查存储桶 %s 失败: %w", cfg.Bucket, err)
	}

	if !exists {
		if err := client.MakeBucket(ctx, cfg.Bucket, minio.MakeBucketOptions{}); err != nil {
			return nil, fmt.Errorf("创建存储桶 %s 失败: %w", cfg.Bucket, err)
		}
	}

	if err := ensureBucketPublicRead(ctx, client, cfg.Bucket); err != nil {
		return nil, fmt.Errorf("设置存储桶 %s 公开读策略失败: %w", cfg.Bucket, err)
	}

	return client, nil
}

// ensureBucketPublicRead 设置 bucket 的匿名公开读策略
// 允许匿名用户通过 HTTP GET 访问 bucket 内的对象（s3:GetObject）
// 其他操作（上传、删除等）仍然需要 AccessKey/SecretKey 凭据
func ensureBucketPublicRead(ctx context.Context, client *minio.Client, bucket string) error {
	policy := fmt.Sprintf(`{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {"AWS": ["*"]},
      "Action": ["s3:GetObject"],
      "Resource": ["arn:aws:s3:::%s/*"]
    }
  ]
}`, bucket)
	return client.SetBucketPolicy(ctx, bucket, policy)
}

func BuildObjectURL(cfg *config.MinioConfig, objectName string) string {
	objectName = strings.TrimLeft(strings.TrimSpace(objectName), "/")
	if cfg == nil {
		return objectName
	}
	bucket := strings.Trim(strings.TrimSpace(cfg.Bucket), "/")
	if base := strings.TrimRight(strings.TrimSpace(cfg.PublicBaseURL), "/"); base != "" {
		if bucket != "" && !strings.HasSuffix(base, "/"+bucket) {
			base = fmt.Sprintf("%s/%s", base, bucket)
		}
		if objectName == "" {
			return base
		}
		return fmt.Sprintf("%s/%s", base, objectName)
	}
	scheme := "http"
	if cfg.UseSSL {
		scheme = "https"
	}
	if objectName == "" {
		return fmt.Sprintf("%s://%s/%s", scheme, cfg.Endpoint, bucket)
	}
	return fmt.Sprintf("%s://%s/%s/%s", scheme, cfg.Endpoint, bucket, objectName)
}
