package s3storage

import (
	"bytes"
	"context"

	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
	openapicred "github.com/aliyun/credentials-go/credentials"

	"io"

	log "github.com/sirupsen/logrus"
	"github.com/tidbcloud/observability/pkg/common/configs"
)

// AliyunOSSClient implements the Client interface for Aliyun OSS.
type AliyunOSSClient struct {
	client *oss.Client
	region string
	// AccessKeyID, AccessKeySecret, Endpoint might be stored or derived from env
}

// NewAliyunOSSClient creates a new AliyunOSSClient.
func NewAliyunOSSClient(region string) *AliyunOSSClient {
	cred, err := openapicred.NewCredential(nil)
	if err != nil {
		return nil
	}
	provider := credentials.CredentialsProviderFunc(func(ctx context.Context) (credentials.Credentials, error) {
		cred, err := cred.GetCredential()
		if err != nil {
			return credentials.Credentials{}, err
		}
		return credentials.Credentials{
			AccessKeyID:     *cred.AccessKeyId,
			AccessKeySecret: *cred.AccessKeySecret,
			SecurityToken:   *cred.SecurityToken,
		}, nil
	})
	cfg := oss.LoadDefaultConfig().WithRegion(configs.GetServiceInfo().Region).WithCredentialsProvider(provider)
	client := oss.NewClient(cfg)

	return &AliyunOSSClient{
		client: client,
		region: region,
	}
}

// Get downloads an object from Aliyun OSS.
func (ali *AliyunOSSClient) Get(ctx context.Context, bucketName string, objectName string) (*io.ReadCloser, error) {
	out, err := ali.client.GetObject(ctx, &oss.GetObjectRequest{
		Bucket: oss.Ptr(bucketName),
		Key:    oss.Ptr(objectName),
	})
	if err != nil {
		log.Errorf("Failed to get object %s from bucket %s: %v", objectName, bucketName, err)
		return nil, err
	}

	return &out.Body, nil
}

// Put uploads an object to Aliyun OSS.
func (ali *AliyunOSSClient) Put(ctx context.Context, bucketName string, objectName string, reader *bytes.Reader) error {
	_, err := ali.client.PutObject(ctx, &oss.PutObjectRequest{
		Bucket: oss.Ptr(bucketName),
		Key:    oss.Ptr(objectName),
		Body:   reader,
	})
	if err != nil {
		log.Errorf("Failed to put object %s to bucket %s: %v", objectName, bucketName, err)
		return err
	}
	return nil
}

// GetLarge downloads a large object from Aliyun OSS using multipart download.
func (ali *AliyunOSSClient) GetLarge(ctx context.Context, bucketName string, objectName string) (*io.ReadCloser, error) {
	return ali.Get(ctx, bucketName, objectName)
}

// PutLarge uploads a large object to Aliyun OSS using multipart upload.
func (ali *AliyunOSSClient) PutLarge(ctx context.Context, bucketName string, objectName string, reader *bytes.Reader) error {
	return ali.Put(ctx, bucketName, objectName, reader)
}
