package services

import (
	"context"
	"fmt"
	"io"
	"time"

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

// MinioService 结构体，持有Minio客户端和配置
type MinioService struct {
	client   *minio.Client
	endpoint string
}

// NewMinioService 构造函数，用于初始化MinioService
func NewMinioService(endpoint, accessKeyID, secretAccessKey string, useSSL bool) *MinioService {
	// 初始化minio client对象[citation:2]
	client, err := minio.New(endpoint, &minio.Options{
		Creds:  credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
		Secure: useSSL,
	})
	if err != nil {
		fmt.Printf("failed to create MinIO client: %w", err)
		return nil
	}

	return &MinioService{
		client:   client,
		endpoint: endpoint,
	}
}

// BucketExists 检查存储桶是否存在[citation:2]
func (s *MinioService) BucketExists(bucketName string) (bool, error) {
	exists, err := s.client.BucketExists(context.Background(), bucketName)
	if err != nil {
		return false, fmt.Errorf("failed to check if bucket '%s' exists: %w", bucketName, err)
	}
	return exists, nil
}

// MakeBucket 创建存储桶（如果不存在）[citation:2]
func (s *MinioService) MakeBucket(bucketName string) error {
	exists, err := s.BucketExists(bucketName)
	if err != nil {
		return err
	}
	if !exists {
		// 创建一个存储桶[citation:2]
		err = s.client.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{})
		if err != nil {
			return fmt.Errorf("failed to create bucket '%s': %w", bucketName, err)
		}
	}
	return nil
}

// UploadFile 上传文件（使用默认日期目录）
func (s *MinioService) UploadFile(bucketName string, reader io.Reader, objectName string) (string, error) {
	// 使用当前日期生成目录，格式 yyyy-MM-dd
	dir := time.Now().Format("2006-01-02")
	return s.uploadFileToDir(bucketName, reader, objectName, dir)
}

// UploadFileToDir 上传文件到指定目录
func (s *MinioService) UploadFileToDir(bucketName string, reader io.Reader, objectName, dir string) (string, error) {
	return s.uploadFileToDir(bucketName, reader, objectName, dir)
}

// uploadFileToDir 内部上传方法
func (s *MinioService) uploadFileToDir(bucketName string, reader io.Reader, objectName, dir string) (string, error) {
	// 确保存储桶存在
	if err := s.MakeBucket(bucketName); err != nil {
		return "", err
	}

	// 构建对象路径
	objectPath := fmt.Sprintf("%s/%s", dir, objectName)

	// 上传对象。PutObject内部已支持分块上传[citation:7]，partSize参数(10MiB)与你的Java代码对应。
	uploadInfo, err := s.client.PutObject(context.Background(), bucketName, objectPath, reader, -1, minio.PutObjectOptions{
		ContentType: "application/octet-stream",
		PartSize:    10 * 1024 * 1024, // 10 MiB
	})
	if err != nil {
		return "", fmt.Errorf("failed to upload file '%s' to bucket '%s': %w", objectPath, bucketName, err)
	}

	// 返回路径，这里返回对象在桶内的完整键（Key），你也可以选择返回一个可访问的URL
	// 格式类似于Java版本返回的 "/bucket/dir/objectName"
	return fmt.Sprintf("/%s/%s", bucketName, uploadInfo.Key), nil
}

// GetFileURL 获取文件的预签名URL（用于临时访问）
func (s *MinioService) GetFileURL(bucketName, objectName string, expiry time.Duration) (string, error) {
	// 生成一个 presigned GET URL，默认7天有效[citation:2]
	if expiry == 0 {
		expiry = 7 * 24 * time.Hour
	}
	url, err := s.client.PresignedGetObject(context.Background(), bucketName, objectName, expiry, nil)
	if err != nil {
		return "", fmt.Errorf("failed to generate URL for '%s/%s': %w", bucketName, objectName, err)
	}
	return url.String(), nil
}

// DownloadFile 下载文件，返回一个读取流[citation:8]
func (s *MinioService) DownloadFile(bucketName, objectName string) (*minio.Object, error) {
	reader, err := s.client.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{})
	if err != nil {
		return nil, fmt.Errorf("failed to download file '%s' from bucket '%s': %w", objectName, bucketName, err)
	}
	// 注意：调用者需要负责关闭这个 reader (defer reader.Close())
	return reader, nil
}

// RemoveFile 删除文件
func (s *MinioService) RemoveFile(bucketName, objectName string) error {
	err := s.client.RemoveObject(context.Background(), bucketName, objectName, minio.RemoveObjectOptions{})
	if err != nil {
		return fmt.Errorf("failed to remove file '%s' from bucket '%s': %w", objectName, bucketName, err)
	}
	return nil
}

// IsObjectExist 检查对象是否存在
func (s *MinioService) IsObjectExist(bucketName, objectName string) (bool, error) {
	_, err := s.client.StatObject(context.Background(), bucketName, objectName, minio.StatObjectOptions{})
	if err != nil {
		// 尝试将错误转换为MinIO的错误响应
		errResp := minio.ToErrorResponse(err)
		if errResp.Code == "NoSuchKey" {
			return false, nil
		}
		return false, fmt.Errorf("failed to stat object '%s' in bucket '%s': %w", objectName, bucketName, err)
	}
	return true, nil
}
