package openapiserverless

import (
	"fmt"
	"testing"
	"time"

	"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/model"
	"github.com/tidbcloud/tidb-management-service/utilities/randstr"
)

func TestClusterList(t *testing.T) {
	testSuite := &TestClusterListSuite{
		host:              viper.GetString("host"),
		apiKey:            viper.GetString("api_key"),
		prjID:             viper.GetString("prj_id"),
		clusterNamePrefix: viper.GetString("cluster_name_prefix"),
		labelSuffix:       randstr.GenWithCharset(4, randstr.AlphabetCharset),

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

	suite.Run(t, testSuite)
}

type TestClusterListSuite struct {
	suite.Suite

	host   string
	apiKey string

	prjID             string
	clusterNamePrefix string
	labelSuffix       string

	region       string
	clusterNames []string
	spendLimit   int32

	serverlessCli *serverlessapi.Cli
}

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

func (su *TestClusterListSuite) 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 *TestClusterListSuite) Test_02_ListClusters() {
	resp := su.serverlessCli.ListClusters("", "", 10)
	su.Require().NotNil(resp)
}

func (su *TestClusterListSuite) Test_03_Create3Clusters() {
	cName1 := su.clusterNamePrefix + randstr.GenWithCharset(4, randstr.AlphabetCharset)
	su.clusterNames = append(su.clusterNames, cName1)
	c1Id := su.serverlessCli.CreateCluster(
		&model.TidbCloudOpenApiserverlessv1beta1Cluster{
			DisplayName: &cName1,
			Region: &model.Commonv1beta1Region{
				Name: &su.region,
			},
			SpendingLimit: &model.ClusterSpendingLimit{
				Monthly: su.spendLimit,
			},
			Labels: map[string]string{
				"tidb.cloud/project": su.prjID,
				"env":                fmt.Sprintf("dev-%s", su.labelSuffix),
				"project":            fmt.Sprintf("serverless-%s", su.labelSuffix),
				"e2e":                su.labelSuffix,
			},
		},
	)
	su.Require().NotEmpty(c1Id)

	cName2 := su.clusterNamePrefix + randstr.GenWithCharset(4, randstr.AlphabetCharset)
	su.clusterNames = append(su.clusterNames, cName2)
	c2Id := su.serverlessCli.CreateCluster(
		&model.TidbCloudOpenApiserverlessv1beta1Cluster{
			DisplayName: &cName2,
			Region: &model.Commonv1beta1Region{
				Name: &su.region,
			},
			SpendingLimit: &model.ClusterSpendingLimit{
				Monthly: su.spendLimit,
			},
			Labels: map[string]string{
				"tidb.cloud/project": su.prjID,
				"env":                fmt.Sprintf("staging-%s", su.labelSuffix),
				"project":            fmt.Sprintf("serverless-%s", su.labelSuffix),
				"e2e":                su.labelSuffix,
			},
		},
	)
	su.Require().NotEmpty(c2Id)

	cName3 := su.clusterNamePrefix + randstr.GenWithCharset(4, randstr.AlphabetCharset)
	su.clusterNames = append(su.clusterNames, cName3)
	c3Id := su.serverlessCli.CreateCluster(
		&model.TidbCloudOpenApiserverlessv1beta1Cluster{
			DisplayName: &cName3,
			Region: &model.Commonv1beta1Region{
				Name: &su.region,
			},
			SpendingLimit: &model.ClusterSpendingLimit{
				Monthly: su.spendLimit,
			},
			Labels: map[string]string{
				"tidb.cloud/project": su.prjID,
				"env":                fmt.Sprintf("prod-%s", su.labelSuffix),
				"project":            fmt.Sprintf("dedicated-%s", su.labelSuffix),
				"e2e":                su.labelSuffix,
			},
		},
	)
	su.Require().NotEmpty(c3Id)

	time.Sleep(30 * time.Second)
}

func (su *TestClusterListSuite) Test_04_ListCluster_filter_ProjectId() {
	clusters := su.serverlessCli.ListClusters(fmt.Sprintf("projectId = %s", su.prjID), "", 10)

	su.Require().NotNil(clusters)
	su.Require().GreaterOrEqualf(len(clusters), 3, "cluster count should be >= 3, but got %d", len(clusters))
}

func (su *TestClusterListSuite) Test_05_ListCluster_filter_TiDBProject_Label() {
	clusters := su.serverlessCli.ListClusters(fmt.Sprintf("labels.'tidb.cloud/project' = %q", su.prjID), "", 10)

	su.Require().NotNil(clusters)
	su.Require().GreaterOrEqualf(len(clusters), 3, "cluster count should be >= 3, but got %d", len(clusters))
}

func (su *TestClusterListSuite) Test_06_ListCluster_filter_Env_Label() {
	clusters1 := su.serverlessCli.ListClusters(fmt.Sprintf("labels.env = 'dev-%s'", su.labelSuffix), "", 10)

	su.Require().NotNil(clusters1)
	su.Require().Lenf(clusters1, 1, "cluster1 count should be 1, but got %d", len(clusters1))

	clusters2 := su.serverlessCli.ListClusters(fmt.Sprintf("labels.env = 'staging-%s'", su.labelSuffix), "", 10)

	su.Require().NotNil(clusters2)
	su.Require().Lenf(clusters2, 1, "cluster2 count should be 1, but got %d", len(clusters2))

	clusters3 := su.serverlessCli.ListClusters(fmt.Sprintf("labels.env = 'prod-%s'", su.labelSuffix), "", 10)

	su.Require().NotNil(clusters3)
	su.Require().Lenf(clusters3, 1, "cluster3 count should be 1, but got %d", len(clusters3))
}

func (su *TestClusterListSuite) Test_07_ListCluster_filter_Project_Label() {
	clusters1 := su.serverlessCli.ListClusters(fmt.Sprintf("labels.project = 'serverless-%s'", su.labelSuffix), "", 10)

	su.Require().NotNil(clusters1)
	su.Require().Lenf(clusters1, 2, "cluster1 count should be 2, but got %d", len(clusters1))

	clusters2 := su.serverlessCli.ListClusters(fmt.Sprintf("labels.project = 'dedicated-%s'", su.labelSuffix), "", 10)

	su.Require().NotNil(clusters2)
	su.Require().Len(clusters2, 1, "cluster2 count should be 1, but got %d", len(clusters2))
}

func (su *TestClusterListSuite) Test_08_ListCluster_With_PageSize() {
	resp1, err := su.serverlessCli.ListClustersAllowErr(
		fmt.Sprintf("labels.e2e = '%s' AND projectId = %s", su.labelSuffix, su.prjID),
		"",
		1,
	)

	su.Require().NoError(err)
	su.Require().NotNil(resp1)
	su.Require().Lenf(resp1.Clusters, 1, "clusters count should be 1, but got %d", len(resp1.Clusters))
	su.Require().Equal(int64(3), resp1.TotalSize, "clusters TotalSize should be 3, but got %d", resp1.TotalSize)
	su.Require().NotEmpty(resp1.NextPageToken, "clusters NextPageToken should not be empty")

	resp2, err := su.serverlessCli.ListClustersAllowErr(
		fmt.Sprintf("labels.e2e = '%s' AND projectId = %s", su.labelSuffix, su.prjID),
		resp1.NextPageToken,
		1,
	)

	su.Require().NoError(err)
	su.Require().NotNil(resp2)
	su.Require().Len(resp2.Clusters, 1, "clusters count should be 1, but got %d", len(resp2.Clusters))
	su.Require().Equal(int64(3), resp2.TotalSize, "clusters TotalSize should be 3, but got %d", resp2.TotalSize)
	su.Require().NotEmptyf(resp2.NextPageToken, "clusters NextPageToken should not be empty")

	resp3, err := su.serverlessCli.ListClustersAllowErr(
		fmt.Sprintf("labels.e2e = '%s' AND projectId = %s", su.labelSuffix, su.prjID),
		resp2.NextPageToken,
		1,
	)

	su.Require().NoError(err)
	su.Require().NotNil(resp3)
	su.Require().Lenf(resp3.Clusters, 1, "clusters count should be 1, but got %d", len(resp3.Clusters))
	su.Require().Equal(int64(3), resp3.TotalSize, "clusters TotalSize should be 3, but got %d", resp3.TotalSize)
	su.Require().Empty(resp3.NextPageToken, "clusters NextPageToken should be empty")

	su.Require().
		True(time.Time(resp1.Clusters[0].CreateTime).After(time.Time(resp2.Clusters[0].CreateTime)), "cluster1 should be created after cluster2")
	su.Require().
		True(time.Time(resp2.Clusters[0].CreateTime).After(time.Time(resp3.Clusters[0].CreateTime)), "cluster2 should be created after cluster3")
}
