package account

import (
	"context"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/common/constants"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/common/models"
	"github.com/tidbcloud/infra-provider/regional-server/test/resourcemanager/mockaccount"
	"go.uber.org/mock/gomock"
)

func Test_findBindingSharedBucket(t *testing.T) {
	ctl := gomock.NewController(t)
	sharedBucketRW := mockaccount.NewMockSharedBucketReaderWriter(ctl)
	sbSvc := SharedBucketService{
		sharedBucketRW: sharedBucketRW,
	}
	clusterTarget := models.SharedBucketBindingTarget{
		TargetType: constants.SharedBucketBindingCluster,
		TargetKey:  "cluster1",
	}
	projectTarget := models.SharedBucketBindingTarget{
		TargetType: constants.SharedBucketBindingProject,
		TargetKey:  "project1",
	}
	tenantTarget := models.SharedBucketBindingTarget{
		TargetType: constants.SharedBucketBindingTenantID,
		TargetKey:  "tenant1",
	}
	t.Run("GetBinding failed", func(t *testing.T) {
		sharedBucketRW.EXPECT().GetBinding(context.Background(), clusterTarget).Return("", assert.AnError).Times(1)
		_, err := sbSvc.findBindingSharedBucketKey(context.Background(), []models.SharedBucketBindingTarget{clusterTarget})
		assert.ErrorIs(t, err, assert.AnError)
	})
	t.Run("get cluster target success", func(t *testing.T) {
		sharedBucketRW.EXPECT().GetBinding(context.Background(), clusterTarget).Return("bucket1", nil).Times(1)
		bucketKey, err := sbSvc.findBindingSharedBucketKey(context.Background(), []models.SharedBucketBindingTarget{clusterTarget})
		assert.NoError(t, err)
		assert.Equal(t, "bucket1", bucketKey)
	})
	t.Run("get project target success", func(t *testing.T) {
		sharedBucketRW.EXPECT().GetBinding(context.Background(), clusterTarget).Return("", nil).Times(1)
		sharedBucketRW.EXPECT().GetBinding(context.Background(), projectTarget).Return("bucket2", nil).Times(1)
		bucketKey, err := sbSvc.findBindingSharedBucketKey(context.Background(), []models.SharedBucketBindingTarget{clusterTarget, projectTarget})
		assert.NoError(t, err)
		assert.Equal(t, "bucket2", bucketKey)
	})
	t.Run("get tenant target success", func(t *testing.T) {
		sharedBucketRW.EXPECT().GetBinding(context.Background(), clusterTarget).Return("", nil).Times(1)
		sharedBucketRW.EXPECT().GetBinding(context.Background(), projectTarget).Return("", nil).Times(1)
		sharedBucketRW.EXPECT().GetBinding(context.Background(), tenantTarget).Return("bucket3", nil).Times(1)
		bucketKey, err := sbSvc.findBindingSharedBucketKey(context.Background(), []models.SharedBucketBindingTarget{clusterTarget, projectTarget, tenantTarget})
		assert.NoError(t, err)
		assert.Equal(t, "bucket3", bucketKey)
	})
}

func TestAssignSharedBucket(t *testing.T) {
	ctl := gomock.NewController(t)
	sharedBucketRW := mockaccount.NewMockSharedBucketReaderWriter(ctl)
	clusterTarget := models.SharedBucketBindingTarget{
		TargetType: constants.SharedBucketBindingCluster,
		TargetKey:  "cluster1",
	}
	targetSharedBucket := models.SharedBucketInfo{
		BucketName:          "bucket1",
		AWSSharedBucketInfo: &models.AWSSharedBucketInfo{}}
	sbSvc := SharedBucketService{
		sharedBucketRW: sharedBucketRW,
	}
	account := models.DataPlaneAccountInfo{
		SharedBucketBundle: &models.SharedBucketBundle{
			DefaultBucketName: targetSharedBucket.BucketName,
			Buckets: []models.SharedBucketInfo{
				targetSharedBucket,
				{
					BucketName:          "bucket2",
					AWSSharedBucketInfo: &models.AWSSharedBucketInfo{},
				},
			},
		},
	}

	t.Run("findBindingSharedBucket failed", func(t *testing.T) {
		sharedBucketRW.EXPECT().GetBinding(context.Background(), gomock.Any()).Return("", assert.AnError).Times(1)
		_, err := sbSvc.AssignSharedBucket(context.Background(), account, "cluster1", "tenant1", "project1")
		assert.ErrorIs(t, err, assert.AnError)
	})
	t.Run("findBindingSharedBucket success", func(t *testing.T) {
		sharedBucketRW.EXPECT().GetBinding(context.Background(), clusterTarget).Return(targetSharedBucket.BucketName, nil).Times(1)
		sharedBucket, err := sbSvc.AssignSharedBucket(context.Background(), account, "cluster1", "tenant1", "project1")
		assert.NoError(t, err)
		assert.Equal(t, targetSharedBucket, sharedBucket)
	})
	t.Run("get default shared bucket failed", func(t *testing.T) {
		// set default bucket name to empty
		account.SharedBucketBundle.DefaultBucketName = ""

		sharedBucketRW.EXPECT().GetBinding(context.Background(), gomock.Any()).Return("", nil).Times(3)
		_, err := sbSvc.AssignSharedBucket(context.Background(), account, "cluster1", "tenant1", "project1")
		assert.ErrorContains(t, err, "the default sharedBucket is empty")
	})
	// restore the default bucket name
	account.SharedBucketBundle.DefaultBucketName = targetSharedBucket.BucketName

	t.Run("bind failed", func(t *testing.T) {
		sharedBucketRW.EXPECT().GetBinding(context.Background(), gomock.Any()).Return("", nil).Times(3)
		sharedBucketRW.EXPECT().Bind(context.Background(), targetSharedBucket, models.SharedBucketBindingTarget{
			TargetType: constants.SharedBucketBindingCluster,
			TargetKey:  "cluster1",
		}).Return(assert.AnError).Times(1)
		_, err := sbSvc.AssignSharedBucket(context.Background(), account, "cluster1", "tenant1", "project1")
		assert.ErrorIs(t, err, assert.AnError)
	})

	t.Run("bind success", func(t *testing.T) {
		sharedBucketRW.EXPECT().GetBinding(context.Background(), gomock.Any()).Return("", nil).Times(3)
		sharedBucketRW.EXPECT().Bind(context.Background(), targetSharedBucket, models.SharedBucketBindingTarget{
			TargetType: constants.SharedBucketBindingCluster,
			TargetKey:  "cluster1",
		}).Return(nil).Times(1)
		sharedBucket, err := sbSvc.AssignSharedBucket(context.Background(), account, "cluster1", "tenant1", "project1")
		assert.Nil(t, err)
		assert.Equal(t, targetSharedBucket, sharedBucket)
	})
}
