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 TestClusterRBAC(t *testing.T) {
	testSuite := &TestClusterRBACSuite{
		host:              viper.GetString("host"),
		apiKey:            viper.GetString("api_key"),
		prjID:             viper.GetString("prj_id"),
		clusterNamePrefix: viper.GetString("cluster_name_prefix"),

		fakeApiKey: "fkke:fakepassword",
		fakePrjID:  "111",

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

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

type TestClusterRBACSuite struct {
	suite.Suite

	host   string
	apiKey string

	fakeApiKey string

	prjID             string
	clusterNamePrefix string

	fakePrjID string

	region      string
	clusterName string
	spendLimit  int32

	serverlessCli     *serverlessapi.Cli
	fakeServerlessCli *serverlessapi.Cli

	clusterID string
}

func (su *TestClusterRBACSuite) TearDownSuite() {
	if su.clusterID != "" {
		su.serverlessCli.EnsureDeleteCluster(su.prjID, su.clusterName)
	}
}

func (su *TestClusterRBACSuite) 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)

	su.fakeServerlessCli, err = serverlessapi.NewClient(&su.Suite, su.host, su.fakeApiKey)
	su.Require().NoError(err)
	su.Require().NotNil(su.serverlessCli)
}

func (su *TestClusterRBACSuite) Test_02_ListClusters_NoPermission() {
	resp, err := su.serverlessCli.ListClustersAllowErr(fmt.Sprintf("projectId = %s", su.fakePrjID), "", 10)

	su.Require().Error(err, "The error should not be nil")
	su.Require().Nil(resp, "The response should be nil")
	su.Require().Contains(err.Error(), "not have permission", "The error msg should be contains 'not have permission'")
	su.Require().Contains(err.Error(), "403", "The error msg should be contains '403'")
}

func (su *TestClusterRBACSuite) Test_03_CreateCluster_NoPermissionProject() {
	resp, err := su.serverlessCli.CreateClusterAllowErr(
		&model.TidbCloudOpenApiserverlessv1beta1Cluster{
			DisplayName: &su.clusterName,
			Region: &model.Commonv1beta1Region{
				Name: &su.region,
			},
			SpendingLimit: &model.ClusterSpendingLimit{
				Monthly: su.spendLimit,
			},
			Labels: map[string]string{
				"tidb.cloud/project": su.fakePrjID,
			},
		},
	)
	su.Require().Error(err, "The error should not be nil")
	su.Require().Nil(resp, "The response should be nil")
	su.Require().Contains(err.Error(), "not have permission", "The error msg should be contains 'not have permission'")
	su.Require().Contains(err.Error(), "403", "The error msg should be contains '403'")
}

func (su *TestClusterRBACSuite) Test_04_CreateCluster() {
	su.clusterID = su.serverlessCli.CreateCluster(
		&model.TidbCloudOpenApiserverlessv1beta1Cluster{
			DisplayName: &su.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(su.clusterID)
}

func (su *TestClusterRBACSuite) Test_05_PollClusterStatus() {
	// timeout: 10 minutes (600 seconds)
	for range 60 {
		state := su.serverlessCli.PollClusterStatus(su.clusterID)
		if *state == model.Commonv1beta1ClusterStateACTIVE {
			break
		}
		su.T().Logf("cluster status: %s", *state)
		time.Sleep(10 * time.Second)
	}
}

func (su *TestClusterRBACSuite) Test_06_GetCluster_NoPermission() {
	resp, err := su.fakeServerlessCli.GetClusterAllowErr(su.clusterID)

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

func (su *TestClusterRBACSuite) Test_07_DeleteCluster_NoPermission() {
	time.Sleep(3 * time.Second)
	resp, err := su.fakeServerlessCli.DeleteClusterAllowErr(su.clusterID)

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

func (su *TestClusterRBACSuite) Test_08_ListClusters_NoPermission_Project_Filter() {
	resp, err := su.serverlessCli.ListClustersAllowErr(fmt.Sprintf("projectId = %s", su.fakePrjID), "", 100)

	su.Require().Error(err, "The error should not be nil")
	su.Require().Nil(resp, "The response should be nil")
	su.Require().Contains(err.Error(), "not have permission", "The error msg should be contains 'not have permission'")
	su.Require().Contains(err.Error(), "403", "The error msg should be contains '403'")
}

func (su *TestClusterRBACSuite) Test_09_ListClusters_NoPermission_TiDBProject_Label_Filter() {
	resp, err := su.serverlessCli.ListClustersAllowErr(
		fmt.Sprintf("labels.'tidb.cloud/project' = %q", su.fakePrjID),
		"",
		100,
	)

	su.Require().Error(err, "The error should not be nil")
	su.Require().Nil(resp, "The response should be nil")
	su.Require().Contains(err.Error(), "not have permission", "The error msg should be contains 'not have permission'")
	su.Require().Contains(err.Error(), "403", "The error msg should be contains '403'")
}
