package openapiserverless

import (
	"testing"

	"github.com/spf13/viper"
	"github.com/stretchr/testify/suite"

	serverlessapi "github.com/tidbcloud/tidb-management-service/functional-test/pkg/serverlessapi/api"
	"github.com/tidbcloud/tidb-management-service/functional-test/pkg/serverlessapi/api/client/serverless_service"
	"github.com/tidbcloud/tidb-management-service/functional-test/pkg/serverlessapi/api/model"
	"github.com/tidbcloud/tidb-management-service/utilities/randstr"
)

// TestClusterFailures
func TestClusterFailures(t *testing.T) {
	testSuite := &TestClusterFailuresSuite{
		host:              viper.GetString("host"),
		apiKey:            viper.GetString("api_key"),
		prjID:             viper.GetString("prj_id"),
		clusterNamePrefix: viper.GetString("cluster_name_prefix"),

		region:     "regions/aws-us-east-1",
		fakeRegion: "regions/aws-af-south-1",
		spendLimit: 1000, // 10 USD
	}

	testSuite.clusterName = testSuite.clusterNamePrefix + randstr.GenWithCharset(4, randstr.AlphabetCharset)
	suite.Run(t, testSuite)
}

type TestClusterFailuresSuite struct {
	suite.Suite

	host   string
	apiKey string

	prjID             string
	clusterNamePrefix string

	region       string
	fakeRegion   string
	clusterName  string
	clusterNames []string
	spendLimit   int32

	serverlessCli *serverlessapi.Cli

	clusterID string
}

func (su *TestClusterFailuresSuite) TearDownSuite() {
	for _, clusterName := range su.clusterNames {
		su.serverlessCli.EnsureDeleteCluster(su.prjID, clusterName)
	}
}

func (su *TestClusterFailuresSuite) Test_01_InitializeClient() {
	var err error
	su.serverlessCli, err = serverlessapi.NewClient(&su.Suite, su.host, su.apiKey)
	su.Require().NoError(err)
	su.Require().NotNil(su.serverlessCli)
}

func (su *TestClusterFailuresSuite) Test_02_CreateClusters_InvalidName() {
	invalidClusterName := su.clusterNamePrefix + randstr.GenWithCharset(4, randstr.AlphabetCharset) + "-"
	su.clusterNames = append(su.clusterNames, invalidClusterName)

	resp, err := su.serverlessCli.CreateClusterAllowErr(
		&model.TidbCloudOpenApiserverlessv1beta1Cluster{
			DisplayName: &invalidClusterName,
			Region: &model.Commonv1beta1Region{
				Name: &su.region,
			},
			SpendingLimit: &model.ClusterSpendingLimit{
				Monthly: su.spendLimit,
			},
			Labels: map[string]string{
				"tidb.cloud/project": su.prjID,
			},
		},
	)

	su.Require().Error(err, "The error should not be nil")
	su.Require().Nil(resp, "The response should be nil")
	su.Require().
		Contains(err.Error(), "Cluster name not allowed", "The error msg should be contains 'Cluster name not allowed")
}

func (su *TestClusterFailuresSuite) Test_03_CreateClusters_DuplicatedName() {
	clusterName := su.clusterNamePrefix + randstr.GenWithCharset(4, randstr.AlphabetCharset)
	su.clusterNames = append(su.clusterNames, clusterName)

	resp1 := su.serverlessCli.CreateCluster(
		&model.TidbCloudOpenApiserverlessv1beta1Cluster{
			DisplayName: &clusterName,
			Region: &model.Commonv1beta1Region{
				Name: &su.region,
			},
			SpendingLimit: &model.ClusterSpendingLimit{
				Monthly: su.spendLimit,
			},
			Labels: map[string]string{
				"tidb.cloud/project": su.prjID,
			},
		},
	)

	su.Require().NotEmpty(resp1, "cluster id not found: %s")
	su.clusterID = resp1

	resp2, err := su.serverlessCli.CreateClusterAllowErr(
		&model.TidbCloudOpenApiserverlessv1beta1Cluster{
			DisplayName: &clusterName,
			Region: &model.Commonv1beta1Region{
				Name: &su.region,
			},
			SpendingLimit: &model.ClusterSpendingLimit{
				Monthly: su.spendLimit,
			},
			Labels: map[string]string{
				"tidb.cloud/project": su.prjID,
			},
		},
	)

	su.Require().Nil(resp2, "The Duplicate cluster  should be nil")
	su.Require().Error(err, "The error should not be nil")
	su.Require().
		Contains(err.Error(), "Duplicate displayName", "The error msg should be contains 'Duplicate displayName'")
}

func (su *TestClusterFailuresSuite) Test_04_CreateClusters_InvalidRegion() {
	clusterName := su.clusterNamePrefix + randstr.GenWithCharset(4, randstr.AlphabetCharset)
	su.clusterNames = append(su.clusterNames, clusterName)

	resp, err := su.serverlessCli.CreateClusterAllowErr(
		&model.TidbCloudOpenApiserverlessv1beta1Cluster{
			DisplayName: &clusterName,
			Region: &model.Commonv1beta1Region{
				Name: &su.fakeRegion,
			},
			SpendingLimit: &model.ClusterSpendingLimit{
				Monthly: su.spendLimit,
			},
			Labels: map[string]string{
				"tidb.cloud/project": su.prjID,
			},
		},
	)
	su.Require().Nil(resp, "The cluster should be nil")
	su.Require().Error(err, "The error should not be nil")
	su.Require().Contains(err.Error(), "Unsupported region", "The error msg should be contains 'Unsupported region'")
}

func (su *TestClusterFailuresSuite) Test_05_UpdateCluster_Labels_Not_Modified() {
	labels := map[string]string{
		"tidb.cloud/project": su.prjID,
	}

	cluster, err := su.serverlessCli.UpdateClusterAllowErr(su.clusterID, "labels",
		&serverless_service.ServerlessServicePartialUpdateClusterParamsBodyCluster{
			Labels: labels,
		},
	)

	su.Require().Nil(cluster, "The cluster should be nil")
	su.Require().Error(err, "The error should not be nil")
	su.Require().Contains(err.Error(), "Invalid labels", "The error msg should be contains 'Invalid labels'")
}

func (su *TestClusterFailuresSuite) Test_06_UpdateCluster_Unsupported_UpdateMask() {
	cluster, err := su.serverlessCli.UpdateClusterAllowErr(su.clusterID, "updateMask",
		&serverless_service.ServerlessServicePartialUpdateClusterParamsBodyCluster{},
	)

	su.Require().Nil(cluster, "The cluster should be nil")
	su.Require().Error(err, "The error should not be nil")
	su.Require().
		Contains(err.Error(), "Update mask is not supported", "The error msg should be contains 'Update mask is not supported'")
}
