package cdn

import (
	"context"
	"io"
	"strings"

	"go.uber.org/zap"

	"github.com/tidbcloud/tiinf/monitor/tilog"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/endpoints"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/pingcap/errors"
	"github.com/tidbcloud/account/commons/config"
)

type CDNHandler interface {
	UploadFile(ctx context.Context, path string, size int64, file io.ReadSeekCloser) error
	GetCDNFile(path string) (*FileObject, error)
}

type S3CDNHandlerImpl struct{}
type MockCDNHandler struct{}

type FileObject struct {
	File          io.ReadCloser
	ContentLength int64
}

func getS3Client(awsRegionID string) *s3.S3 {
	sess := session.Must(session.NewSession(&aws.Config{
		Region: aws.String(awsRegionID),
	}))
	client := s3.New(sess)
	return client
}

func (*S3CDNHandlerImpl) UploadFile(ctx context.Context, path string, size int64, file io.ReadSeekCloser) error {
	defer file.Close() // nolint: errcheck

	sess, err := session.NewSession()

	if err != nil {
		return errors.Annotate(err, "create s3 client error")
	}
	client := s3.New(sess)
	//client := getS3Client(endpoints.UsWest2RegionID)

	path = path + config.Global().ServerConfig.CDNBucketPathPrefix
	putObjectInput := &s3.PutObjectInput{
		Bucket:        &config.Global().ServerConfig.CDNBucket,
		Key:           &path,
		Body:          file,
		ContentLength: &size,
	}
	tilog.Info(ctx, "UploadFile", zap.Any("putObjectInput", putObjectInput))
	_, err = client.PutObject(putObjectInput)
	if err != nil {
		return errors.Annotate(err, "failed to upload object to s3")
	}
	return nil
}

func UploadLogoImag(ctx context.Context, contentType, path string, size int64, file io.ReadSeekCloser, bucket string) error {
	defer file.Close() // nolint: errcheck

	var awsRegionID string
	if config.IsStagingEnv() || config.IsDevEnv() || config.IsLocalEnv() {
		awsRegionID = endpoints.UsEast1RegionID
	} else if config.IsProdEnv() {
		awsRegionID = endpoints.UsWest1RegionID
	}
	client := getS3Client(awsRegionID)

	putObjectInput := &s3.PutObjectInput{
		Bucket:        &bucket,
		Key:           &path,
		Body:          file,
		ContentLength: &size,
		ContentType:   &contentType,
	}
	tilog.Info(ctx, "UploadFile", zap.Any("awsRegionID", awsRegionID), zap.Any("Bucket", putObjectInput.Bucket), zap.Any("Key", putObjectInput.Key), zap.Any("ContentType", putObjectInput.ContentType))
	_, err := client.PutObject(putObjectInput)
	if err != nil {
		tilog.Error(ctx, "UploadLogoImag fail", zap.Error(err))
		return errors.Annotate(err, "failed to upload msp logo  to s3")
	}
	return nil
}

func UploadLogoImagWithReadSeeker(ctx context.Context, contentType, path string, size int64, file io.ReadSeeker, bucket string) error {
	var awsRegionID string
	if config.IsStagingEnv() || config.IsDevEnv() || config.IsLocalEnv() {
		awsRegionID = endpoints.UsEast1RegionID
	} else if config.IsProdEnv() {
		awsRegionID = endpoints.UsWest1RegionID
	}
	client := getS3Client(awsRegionID)

	putObjectInput := &s3.PutObjectInput{
		Bucket:        &bucket,
		Key:           &path,
		Body:          file,
		ContentLength: &size,
		ContentType:   &contentType,
	}
	tilog.Info(ctx, "UploadFile", zap.Any("awsRegionID", awsRegionID), zap.Any("Bucket", putObjectInput.Bucket), zap.Any("Key", putObjectInput.Key), zap.Any("ContentType", putObjectInput.ContentType))
	_, err := client.PutObject(putObjectInput)
	if err != nil {
		tilog.Error(ctx, "UploadLogoImag fail", zap.Error(err))
		return errors.Annotate(err, "failed to upload msp logo  to s3")
	}
	return nil
}

func (*S3CDNHandlerImpl) GetCDNFile(path string) (*FileObject, error) {
	sess, err := session.NewSession()
	if err != nil {
		return nil, errors.Annotate(err, "create s3 client error")
	}
	client := s3.New(sess)
	path = path + config.Global().ServerConfig.CDNBucketPathPrefix

	input := &s3.GetObjectInput{
		Bucket: &config.Global().ServerConfig.CDNBucket,
		Key:    &path,
	}

	object, err := client.GetObject(input)
	if err != nil {
		return nil, errors.Annotate(err, "get s3 object error")
	}

	return &FileObject{
		File:          object.Body,
		ContentLength: *object.ContentLength,
	}, nil
}

func (*MockCDNHandler) UploadFile(ctx context.Context, path string, size int64, file io.ReadSeekCloser) error {
	defer file.Close() // nolint: errcheck
	return nil
}

func (*MockCDNHandler) GetCDNFile(path string) (*FileObject, error) {
	return &FileObject{
		File:          io.NopCloser(strings.NewReader("")),
		ContentLength: 0,
	}, nil
}

func Get() CDNHandler {
	if config.Global().ServerConfig.EnableCDNBucket {
		return &S3CDNHandlerImpl{}
	}
	return &MockCDNHandler{}
}
