// Package tencentcos 封装后台管理服务的腾讯云 COS 对象上传能力。
package tencentcos

import (
	"context"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strings"
	"time"

	cos "github.com/tencentyun/cos-go-sdk-v5"
)

// Config 是后台文件上传所需的 COS 最小配置。
type Config struct {
	SecretID  string
	SecretKey string
	Bucket    string
	Region    string
	AccessURL string
}

// Uploader 负责把后台收到的文件流写入 COS，并返回 CDN/自定义域名访问地址。
type Uploader struct {
	client    *cos.Client
	accessURL string
}

// NewUploader 按 bucket + region 装配 COS SDK client。
func NewUploader(cfg Config, transport http.RoundTripper) (*Uploader, error) {
	cfg.SecretID = strings.TrimSpace(cfg.SecretID)
	cfg.SecretKey = strings.TrimSpace(cfg.SecretKey)
	cfg.Bucket = strings.TrimSpace(cfg.Bucket)
	cfg.Region = strings.TrimSpace(cfg.Region)
	cfg.AccessURL = strings.TrimRight(strings.TrimSpace(cfg.AccessURL), "/")
	if cfg.SecretID == "" || cfg.SecretKey == "" || cfg.Bucket == "" || cfg.Region == "" || cfg.AccessURL == "" {
		return nil, fmt.Errorf("tencent cos config is incomplete")
	}
	if _, err := url.ParseRequestURI(cfg.AccessURL); err != nil {
		return nil, fmt.Errorf("tencent cos access_url is invalid: %w", err)
	}

	bucketURL, err := url.Parse(fmt.Sprintf("https://%s.cos.%s.myqcloud.com", cfg.Bucket, cfg.Region))
	if err != nil {
		return nil, fmt.Errorf("tencent cos bucket url is invalid: %w", err)
	}
	if transport == nil {
		transport = http.DefaultTransport
	}

	client := cos.NewClient(&cos.BaseURL{BucketURL: bucketURL}, &http.Client{
		Timeout: 30 * time.Second,
		Transport: &cos.AuthorizationTransport{
			SecretID:  cfg.SecretID,
			SecretKey: cfg.SecretKey,
			Transport: transport,
		},
	})

	return &Uploader{client: client, accessURL: cfg.AccessURL}, nil
}

// PutObject 上传对象并返回对外访问 URL。
func (u *Uploader) PutObject(ctx context.Context, key string, reader io.Reader, sizeBytes int64, contentType string) (string, error) {
	key = strings.TrimLeft(strings.TrimSpace(key), "/")
	if key == "" || reader == nil || sizeBytes <= 0 {
		return "", fmt.Errorf("object upload input is invalid")
	}

	options := &cos.ObjectPutOptions{
		ObjectPutHeaderOptions: &cos.ObjectPutHeaderOptions{
			ContentLength: sizeBytes,
			ContentType:   strings.TrimSpace(contentType),
		},
	}
	if _, err := u.client.Object.Put(ctx, key, reader, options); err != nil {
		return "", err
	}

	publicURL, err := url.JoinPath(u.accessURL, key)
	if err != nil {
		return "", fmt.Errorf("build public object url: %w", err)
	}

	return publicURL, nil
}
