package awss3cli

import (
	"context"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/aws/signer/v4"
	"github.com/aws/aws-sdk-go-v2/service/s3"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"

	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/adapter/awss3"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
)

var (
	_ awss3.PresignClient = &awsS3PresignClient{}
	_ awss3.GetClient     = &awsS3Client{}
)

type awsS3PresignClient struct {
	*s3.Client
	*s3.PresignClient
}

func NewAWSS3PresignClient(conf *aws.Config) awss3.PresignClient {
	return &awsS3PresignClient{
		Client: s3.NewFromConfig(*conf),
		PresignClient: s3.NewPresignClient(
			s3.NewFromConfig(*conf)),
	}
}

func (a *awsS3PresignClient) PresignGetObject(
	ctx context.Context,
	params *s3.GetObjectInput,
	optFns ...func(*s3.PresignOptions),
) (*v4.PresignedHTTPRequest, bizerr.BizError) {
	tilog.Debug(ctx, "presign get object called",
		zap.String("bucket", aws.ToString(params.Bucket)),
		zap.String("key", aws.ToString(params.Key)))
	_, err := a.GetObject(ctx, params)
	if err != nil {
		return nil, bizerr.New(bizerr.GeneralInternalErrorCode, "failed to get object from S3")
	}
	resp, err := a.PresignClient.PresignGetObject(ctx, params, optFns...)
	if err != nil {
		return nil, bizerr.New(bizerr.GeneralInternalErrorCode, "failed to presign get object: %v", err)
	}
	return resp, nil
}

type awsS3Client struct {
	*s3.Client
}

func NewAWSS3Client(conf *aws.Config) awss3.GetClient {
	return &awsS3Client{
		Client: s3.NewFromConfig(*conf),
	}
}

func (a *awsS3Client) GetObject(ctx context.Context, params *s3.GetObjectInput, optFns ...func(*s3.Options)) (*s3.GetObjectOutput, bizerr.BizError) {
	resp, err := a.Client.GetObject(ctx, params, optFns...)
	if err != nil {
		return nil, bizerr.New(bizerr.GeneralInternalErrorCode, "failed to get object from S3: %v", err)
	}
	return resp, nil
}

func (a *awsS3Client) PutObject(ctx context.Context, params *s3.PutObjectInput, optFns ...func(*s3.Options)) (*s3.PutObjectOutput, bizerr.BizError) {
	resp, err := a.Client.PutObject(ctx, params, optFns...)
	if err != nil {
		return nil, bizerr.New(bizerr.GeneralInternalErrorCode, "failed to put object to S3: %v", err)
	}
	return resp, nil
}
