package aws

import (
	"bytes"
	"context"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/tidbcloud/observability/pkg/common/errors"

	"github.com/aws/aws-sdk-go-v2/aws"
	s3api "github.com/aws/aws-sdk-go-v2/service/s3"
	"github.com/aws/aws-sdk-go-v2/service/s3/types"
	. "github.com/onsi/gomega"
	mocks "github.com/tidbcloud/observability/test/mockresourceworker/providers/aws"
	"go.uber.org/mock/gomock"
)

func newMockS3(cfg aws.Config, mock s3Client) S3Client {
	return S3Client{
		cli: mock,
		cfg: cfg,
	}
}

func TestS3CreateBucket(t *testing.T) {
	g := NewGomegaWithT(t)
	ctl := gomock.NewController(t)
	defer ctl.Finish()
	var cfg aws.Config
	cfg.Region = "us-west-2"
	mockclient := mocks.NewMocks3Client(ctl)
	s3client := newMockS3(cfg, mockclient)
	mockclient.EXPECT().CreateBucket(context.TODO(), &s3api.CreateBucketInput{
		Bucket:                    aws.String("test1"),
		ACL:                       types.BucketCannedACLPrivate,
		CreateBucketConfiguration: &types.CreateBucketConfiguration{LocationConstraint: types.BucketLocationConstraint(cfg.Region)},
	}).Return(&s3api.CreateBucketOutput{
		Location: aws.String("/test1"),
	}, nil)
	g.Expect(s3client.CreateBucket(context.TODO(), "test1")).Should(Succeed())
}

func TestS3CreateBucketIndefaultregion(t *testing.T) {
	g := NewGomegaWithT(t)
	ctl := gomock.NewController(t)
	defer ctl.Finish()
	var cfg aws.Config
	cfg.Region = "us-east-1"
	mockclient := mocks.NewMocks3Client(ctl)
	s3client := newMockS3(cfg, mockclient)
	mockclient.EXPECT().CreateBucket(context.TODO(), &s3api.CreateBucketInput{
		Bucket: aws.String("test1"),
		ACL:    types.BucketCannedACLPrivate,
	}).Return(&s3api.CreateBucketOutput{
		Location: aws.String("/test1"),
	}, nil)
	g.Expect(s3client.CreateBucket(context.TODO(), "test1")).Should(Succeed())
}

func TestS3BucketExists(t *testing.T) {
	g := NewGomegaWithT(t)
	ctl := gomock.NewController(t)
	defer ctl.Finish()
	var cfg aws.Config
	mockclient := mocks.NewMocks3Client(ctl)
	s3client := newMockS3(cfg, mockclient)
	mockclient.EXPECT().HeadBucket(context.TODO(), &s3api.HeadBucketInput{
		Bucket: aws.String("test1"),
	}).Return(&s3api.HeadBucketOutput{}, nil)
	g.Expect(s3client.BucketExists(context.TODO(), "test1")).Should(Equal(true))
	mockclient.EXPECT().HeadBucket(context.TODO(), &s3api.HeadBucketInput{
		Bucket: aws.String("notfound"),
	}).Return(nil, &types.NotFound{})
	g.Expect(s3client.BucketExists(context.TODO(), "notfound")).Should(Equal(false))
	//some error happened
	mockclient.EXPECT().HeadBucket(context.TODO(), &s3api.HeadBucketInput{
		Bucket: aws.String("errors"),
	}).Return(nil, &types.InvalidObjectState{})
	g.Expect(s3client.BucketExists(context.TODO(), "errors")).Error().Should(HaveOccurred())
}

func TestS3GetObject(t *testing.T) {
	ctl := gomock.NewController(t)
	defer ctl.Finish()
	var cfg aws.Config
	mockclient := mocks.NewMocks3Client(ctl)
	s3client := newMockS3(cfg, mockclient)
	mockclient.EXPECT().GetObject(gomock.Any(), gomock.Any()).Return(nil, errors.New(errors.UnrecognizedError, ""))
	_, err := s3client.Get(context.TODO(), "", "")
	assert.Error(t, err)

	mockclient.EXPECT().GetObject(gomock.Any(), gomock.Any()).Return(&s3api.GetObjectOutput{
		Body: nil,
	}, nil)
	_, err = s3client.Get(context.TODO(), "", "")
	assert.NoError(t, err)

}

func TestS3PutObject(t *testing.T) {
	ctl := gomock.NewController(t)
	defer ctl.Finish()
	var cfg aws.Config
	mockclient := mocks.NewMocks3Client(ctl)
	s3client := newMockS3(cfg, mockclient)
	mockclient.EXPECT().PutObject(gomock.Any(), gomock.Any()).Return(nil, errors.New(errors.UnrecognizedError, ""))
	err := s3client.Put(context.TODO(), "", "", nil)
	assert.Error(t, err)

	mockclient.EXPECT().PutObject(gomock.Any(), gomock.Any()).Return(&s3api.PutObjectOutput{}, nil)
	r := bytes.NewReader([]byte(""))
	err = s3client.Put(context.TODO(), "", "", r)
	assert.NoError(t, err)

}
