package webapiserverless

import (
	"testing"
	"time"

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

	"github.com/tidbcloud/tidb-management-service/functional-test/pkg/coreportalapi"
	"github.com/tidbcloud/tidb-management-service/functional-test/pkg/coreportalapi/model"
	"github.com/tidbcloud/tidb-management-service/functional-test/pkg/tidbcloudlogin"
	"github.com/tidbcloud/tidb-management-service/utilities/randstr"
)

func TestServerlessClusterRBAC(t *testing.T) {
	testSuite := &TestServerlessClusterRBACSuite{
		WebApiLoginContext: &tidbcloudlogin.WebApiLoginContext{
			Host:              viper.GetString("host"),
			Auth0Domain:       viper.GetString("auth0_domain"),
			Auth0ClientID:     viper.GetString("auth0_client_id"),
			Auth0ClientSecret: viper.GetString("auth0_client_secret"),
			UserEmail:         viper.GetString("user_email"),
			BearerToken:       viper.GetString("bearer_token"),
		},

		orgID:             viper.GetString("org_id"),
		prjID:             viper.GetString("prj_id"),
		clusterNamePrefix: viper.GetString("cluster_name_prefix"),

		fakePrjID: "1",
		fakeOrgID: "1",

		nodeProfileName: "Shared0",
		provider:        "aws",
		region:          "us-east-1",
		password:        randstr.GenWithCharset(16, randstr.AlphabetCharset),
		spendLimit:      1000, // 10 USD
	}

	testSuite.clusterName = testSuite.clusterNamePrefix + randstr.GenWithCharset(4, randstr.AlphabetCharset)
	testSuite.components = []*model.CentralClusterComponent{
		{
			ComponentType:   lo.ToPtr(model.CentralComponentTypeCOMPONENTTYPETIDB),
			NodeProfileName: lo.ToPtr(testSuite.nodeProfileName),
			Replicas:        lo.ToPtr(int32(1)),
			StorageSizeGi:   lo.ToPtr(int32(0)),
		},
		{
			ComponentType:   lo.ToPtr(model.CentralComponentTypeCOMPONENTTYPETIKV),
			NodeProfileName: lo.ToPtr(testSuite.nodeProfileName),
			Replicas:        lo.ToPtr(int32(1)),
			StorageSizeGi:   lo.ToPtr(int32(1)),
		},
		{
			ComponentType:   lo.ToPtr(model.CentralComponentTypeCOMPONENTTYPETIFLASH),
			NodeProfileName: lo.ToPtr(testSuite.nodeProfileName),
			Replicas:        lo.ToPtr(int32(1)),
			StorageSizeGi:   lo.ToPtr(int32(1)),
		},
	}

	suite.Run(t, testSuite)
}

type TestServerlessClusterRBACSuite struct {
	suite.Suite

	*tidbcloudlogin.WebApiLoginContext

	orgID             string
	prjID             string
	clusterNamePrefix string

	fakePrjID string
	fakeOrgID string

	nodeProfileName string
	provider        string
	region          string
	password        string
	clusterName     string
	components      []*model.CentralClusterComponent
	spendLimit      int64
	cmCli           *coreportalapi.Cli

	clusterID string
}

func (su *TestServerlessClusterRBACSuite) TearDownSuite() {
	if su.clusterID != "" {
		su.cmCli.EnsureDeleteCluster(su.orgID, su.prjID, su.clusterName)
	}
}

func (su *TestServerlessClusterRBACSuite) Test_01_LoginAndInitializeClient() {
	token, err := su.LoginAndGetToken(su.orgID)
	su.Require().NoError(err)

	su.cmCli, err = coreportalapi.NewClient(&su.Suite, su.Host, token)
	su.Require().NoError(err)
	su.Require().NotNil(su.cmCli)
}

func (su *TestServerlessClusterRBACSuite) Test_02_ListClusters_NoPermissionProject() {
	resp := su.cmCli.ListClustersAllowBizErr(su.orgID, su.fakePrjID, 1, 10, nil)

	su.Require().Equalf("49900003", resp.BaseResp.ErrCode, "The error code should be 49900003")
}

func (su *TestServerlessClusterRBACSuite) Test_03_CreateCluster_NoPermissionOrg() {
	su.clusterName = su.clusterNamePrefix + randstr.GenWithCharset(4, randstr.AlphabetCharset)
	resp := su.cmCli.CreateClusterAllowBizErr(
		su.fakeOrgID,
		su.prjID,
		su.provider,
		su.region,
		su.clusterName,
		4000,
		su.components,
		true,
		su.spendLimit,
	)

	su.Require().Equalf("49900003", resp.BaseResp.ErrCode, "The error code should be 49900003")
}

func (su *TestServerlessClusterRBACSuite) Test_04_ListClusters_NoPermissionOrg() {
	resp := su.cmCli.ListClustersAllowBizErr(su.fakeOrgID, su.prjID, 1, 10, nil)

	su.Require().Equalf("49900003", resp.BaseResp.ErrCode, "The error code should be 49900003")
}

func (su *TestServerlessClusterRBACSuite) Test_05_CreateCluster_NoPermissionProject() {
	su.clusterName = su.clusterNamePrefix + randstr.GenWithCharset(4, randstr.AlphabetCharset)
	resp := su.cmCli.CreateClusterAllowBizErr(
		su.orgID,
		su.fakePrjID,
		su.provider,
		su.region,
		su.clusterName,
		4000,
		su.components,
		true,
		su.spendLimit,
	)

	su.Require().Equalf("49900003", resp.BaseResp.ErrCode, "The error code should be 49900003")
}

func (su *TestServerlessClusterRBACSuite) Test_06_CreateCluster() {
	su.clusterID = *su.cmCli.CreateCluster(su.orgID, su.prjID, su.provider, su.region, su.clusterName, 4000, su.components, true, su.spendLimit)
	su.Require().NotEmpty(su.clusterID)
}

func (su *TestServerlessClusterRBACSuite) Test_07_PollClusterStatus() {
	// timeout: 10 minutes (600 seconds)
	for range 60 {
		status := su.cmCli.PollClusterStatus(su.orgID, su.prjID, su.clusterID)
		if *status == model.CentralClusterStatusNORMAL {
			break
		}
		su.T().Logf("cluster status: %s", *status)
		time.Sleep(10 * time.Second)
	}
}

func (su *TestServerlessClusterRBACSuite) Test_08_GetCluster_NoPermissionProject() {
	resp := su.cmCli.GetClusterAllowBizErr(su.orgID, su.fakePrjID, su.clusterID)

	su.Require().Equalf("49900003", resp.BaseResp.ErrCode, "The error code should be 49900003")
}

func (su *TestServerlessClusterRBACSuite) Test_09_GetCluster_NoPermissionOrg() {
	resp := su.cmCli.GetClusterAllowBizErr(su.fakeOrgID, su.prjID, su.clusterID)

	su.Require().Equalf("49900003", resp.BaseResp.ErrCode, "The error code should be 49900003")
}

func (su *TestServerlessClusterRBACSuite) Test_10_ChangeHasSetPwd_NoPermissionProject() {
	time.Sleep(3 * time.Second)
	resp := su.cmCli.SetPasswordAllowBizErr(su.orgID, su.fakePrjID, su.clusterID, su.password)

	su.Require().Equalf("49900003", resp.BaseResp.ErrCode, "The error code should be 49900003")
}

func (su *TestServerlessClusterRBACSuite) Test_11_ChangeHasSetPwd_NoPermissionOrg() {
	time.Sleep(3 * time.Second)
	resp := su.cmCli.SetPasswordAllowBizErr(su.fakeOrgID, su.prjID, su.clusterID, su.password)

	su.Require().Equalf("49900003", resp.BaseResp.ErrCode, "The error code should be 49900003")
}

func (su *TestServerlessClusterRBACSuite) Test_12_DeleteCluster_NoPermissionProject() {
	time.Sleep(3 * time.Second)
	resp := su.cmCli.DeleteClusterAllowBizErr(su.orgID, su.fakePrjID, su.clusterID)

	su.Require().Equalf("49900003", resp.BaseResp.ErrCode, "The error code should be 49900003")
}

func (su *TestServerlessClusterRBACSuite) Test_13_DeleteCluster_NoPermissionOrg() {
	time.Sleep(3 * time.Second)
	resp := su.cmCli.DeleteClusterAllowBizErr(su.fakeOrgID, su.prjID, su.clusterID)

	su.Require().Equalf("49900003", resp.BaseResp.ErrCode, "The error code should be 49900003")
}
