package webapidedicated

import (
	"strings"
	"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/openapiv1beta"
	"github.com/tidbcloud/tidb-management-service/functional-test/pkg/tidbcloudlogin"
	"github.com/tidbcloud/tidb-management-service/utilities/randstr"
)

func TestDedicatedClusterAccessPoint(t *testing.T) {
	testSuite := &TestDedicatedClusterAccessPointSuite{
		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"),

		provider: "aws",
		region:   "us-west-2",
		password: randstr.GenWithCharset(16, randstr.AlphabetCharset),
	}

	testSuite.clusterName = testSuite.clusterNamePrefix + randstr.GenWithCharset(4, randstr.AlphabetCharset)

	suite.Run(t, testSuite)
}

type TestDedicatedClusterAccessPointSuite struct {
	suite.Suite

	*tidbcloudlogin.WebApiLoginContext

	orgID             string
	prjID             string
	clusterNamePrefix string

	provider    string
	region      string
	password    string
	clusterName string
	components  []*model.CentralClusterComponent

	portalCli *coreportalapi.Cli

	clusterID string

	openApiCli *openapiv1beta.Cli
}

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

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

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

	apikey := viper.GetString("open_api_key")
	keyPair := strings.Split(apikey, ":")
	su.openApiCli, err = openapiv1beta.NewClient(&su.Suite, viper.GetString("open_api_host"), keyPair[0], keyPair[1])
	su.Require().NoError(err)
	su.Require().NotNil(su.openApiCli)
}

func (su *TestDedicatedClusterAccessPointSuite) Test_02_ListClusters() {
	_ = su.portalCli.ListClusters(su.orgID, su.prjID, 1, 100)
}

func (su *TestDedicatedClusterAccessPointSuite) Test_03_ListProvidersAndSelectAwsUsWest2() {
	providers := su.portalCli.ListProviders(su.orgID, su.prjID, false)

	p, ok := lo.Find(providers, func(p *model.CentralCloudProvider) bool {
		return p != nil && p.Name != nil && *p.Name == su.provider
	})
	su.Require().True(ok)
	su.Require().NotNil(p.Regions)
	_, ok = lo.Find(p.Regions, func(r *model.CentralRegion) bool {
		return r != nil && r.Name != nil && *r.Name == su.region
	})
	su.Require().True(ok)
}

func (su *TestDedicatedClusterAccessPointSuite) Test_04_CheckProjectRegionCidr() {
	su.portalCli.CheckProjectRegionCidr(su.orgID, su.prjID, su.provider, su.region)
}

func (su *TestDedicatedClusterAccessPointSuite) Test_05_GenerateClusterName() {
	_ = su.portalCli.GenerateClusterName(su.orgID, su.prjID)
}

func (su *TestDedicatedClusterAccessPointSuite) Test_06_QueryNodeProfiles() {
	profiles := su.portalCli.QueryNodeProfiles(su.orgID, su.prjID, su.provider, su.region, false)

	var (
		tidbProfile *model.CentralClusterComponent
		tikvProfile *model.CentralClusterComponent
	)

	// Select the node profile with the minimal CPU cores for TiDB and TiKV.
	for _, profile := range profiles {
		switch *profile.ComponentType {
		case model.CentralComponentTypeCOMPONENTTYPETIDB:
			for _, p := range profile.Profiles {
				if tidbProfile == nil {
					if strings.Contains(p.Name, "8C") {
						tidbProfile = &model.CentralClusterComponent{
							ComponentType:   lo.ToPtr(model.CentralComponentTypeCOMPONENTTYPETIDB),
							NodeProfileName: lo.ToPtr(p.Name),
							Replicas:        lo.ToPtr(int32(1)),
						}
					}
				}
			}
		case model.CentralComponentTypeCOMPONENTTYPETIKV:
			for _, p := range profile.Profiles {
				if tikvProfile == nil {
					if strings.Contains(p.Name, "8C") {
						tikvProfile = &model.CentralClusterComponent{
							ComponentType:   lo.ToPtr(model.CentralComponentTypeCOMPONENTTYPETIKV),
							NodeProfileName: lo.ToPtr(p.Name),
							Replicas:        lo.ToPtr(int32(3)),
							StorageSizeGi:   lo.ToPtr(p.MinStorageSizeGi),
						}
					}
				}
			}
		}
	}

	su.components = []*model.CentralClusterComponent{tidbProfile, tikvProfile}
}

func (su *TestDedicatedClusterAccessPointSuite) Test_07_EstimateClusterPrice() {
	_ = su.portalCli.EstimateClusterPrice(su.orgID, su.prjID, su.provider, su.region, su.components)
}

func (su *TestDedicatedClusterAccessPointSuite) Test_08_CreateCluster() {
	su.clusterID = *su.portalCli.CreateCluster(su.orgID, su.prjID, su.provider, su.region, su.clusterName, 4000, su.components, false, 0)
	su.Require().NotEmpty(su.clusterID)

	resp := su.portalCli.GetCluster(su.orgID, su.prjID, su.clusterID)
	su.Require().Equal(1, int(*resp.ClusterComponents.Tidb.Replicas))
}

func (su *TestDedicatedClusterAccessPointSuite) Test_09_PollClusterStatus() {
	// timeout: 30 minutes (1800 seconds)
	for range 180 {
		status := su.portalCli.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 *TestDedicatedClusterAccessPointSuite) Test_10_CreateAccessPoint() {
	_, err := su.openApiCli.CreateAccessPoint(su.prjID, su.clusterID)
	su.Require().Error(err)
	// new dedicated cluster does not support access point with 0 count
	// so drop this test case
}

// func (su *TestDedicatedClusterAccessPointSuite) Test_11_ScaleDefaultAccessPoint() {
// 	resp, err := su.openApiCli.ScaleClusterByAccessPoint(su.prjID, su.clusterID, su.defaultPointID, &idl.ScaleTiDBConfig{
// 		NodeQuantity: 2,
// 	})
// 	su.Require().True(resp.IsSuccess())
// 	su.Require().NoError(err)

// 	// timeout: 30 minutes (1800 seconds)
// 	for i := 0; i < 180; i++ {
// 		status := su.portalCli.PollClusterStatus(su.orgID, su.prjID, su.clusterID)
// 		if *status == model.CentralClusterStatusNORMAL {
// 			break
// 		}
// 		su.T().Logf("cluster status: %s", *status)
// 		time.Sleep(10 * time.Second)
// 	}

// 	clusterResp := su.portalCli.GetCluster(su.orgID, su.prjID, su.clusterID)
// 	su.Require().Equal(2, int(*clusterResp.ClusterComponents.Tidb.Replicas))
// }

// func (su *TestDedicatedClusterAccessPointSuite) Test_12_ScaleWithAccessPoint() {
// 	resp, err := su.openApiCli.ScaleClusterByAccessPoint(su.prjID, su.clusterID, su.accessPointID, &idl.ScaleTiDBConfig{
// 		NodeQuantity: 1,
// 	})
// 	su.Require().True(resp.IsSuccess())
// 	su.Require().NoError(err)

// 	// timeout: 30 minutes (1800 seconds)
// 	for i := 0; i < 180; i++ {
// 		status := su.portalCli.PollClusterStatus(su.orgID, su.prjID, su.clusterID)
// 		if *status == model.CentralClusterStatusNORMAL {
// 			break
// 		}
// 		su.T().Logf("cluster status: %s", *status)
// 		time.Sleep(10 * time.Second)
// 	}

// 	clusterResp := su.portalCli.GetCluster(su.orgID, su.prjID, su.clusterID)
// 	su.Require().Equal(3, int(*clusterResp.ClusterComponents.Tidb.Replicas))
// }

// func (su *TestDedicatedClusterAccessPointSuite) Test_13_ScaleWithNodeSize() {
// 	_, err := su.openApiCli.ScaleClusterByAccessPoint(su.prjID, "3840808", su.accessPointID, &idl.ScaleTiDBConfig{
// 		NodeQuantity: 2,
// 		NodeSize:     "8C16G",
// 	})
// 	su.Require().NoError(err)
// }

// func (su *TestDedicatedClusterAccessPointSuite) Test_14_CreateMultiAccessPoint() {
// 	accessPointResp := *su.openApiCli.CreateAccessPoint(su.prjID, su.clusterID)
// 	su.Require().NotNil(accessPointResp)

// 	resp, err := su.openApiCli.ScaleClusterByAccessPoint(su.prjID, su.clusterID, accessPointResp.ID, &idl.ScaleTiDBConfig{
// 		NodeQuantity: 2,
// 	})
// 	su.Require().True(resp.IsSuccess())
// 	su.Require().NoError(err)

// 	// timeout: 30 minutes (1800 seconds)
// 	for i := 0; i < 180; i++ {
// 		status := su.portalCli.PollClusterStatus(su.orgID, su.prjID, su.clusterID)
// 		if *status == model.CentralClusterStatusNORMAL {
// 			break
// 		}
// 		su.T().Logf("cluster status: %s", *status)
// 		time.Sleep(10 * time.Second)
// 	}
// 	clusterResp := su.portalCli.GetCluster(su.orgID, su.prjID, su.clusterID)
// 	su.Require().Equal(5, int(*clusterResp.ClusterComponents.Tidb.Replicas))

// 	su.openApiCli.DeleteAccessPoint(su.prjID, su.clusterID, accessPointResp.ID)

// 	// timeout: 30 minutes (1800 seconds)
// 	for i := 0; i < 180; i++ {
// 		status := su.portalCli.PollClusterStatus(su.orgID, su.prjID, su.clusterID)
// 		if *status == model.CentralClusterStatusNORMAL {
// 			break
// 		}
// 		su.T().Logf("cluster status: %s", *status)
// 		time.Sleep(10 * time.Second)
// 	}
// 	clusterResp = su.portalCli.GetCluster(su.orgID, su.prjID, su.clusterID)
// 	su.Require().Equal(3, int(*clusterResp.ClusterComponents.Tidb.Replicas))
// }

// func (su *TestDedicatedClusterAccessPointSuite) Test_15_SetPassword() {
// 	su.portalCli.SetPassword(su.orgID, su.prjID, su.clusterID, su.password)
// }

// func (su *TestDedicatedClusterAccessPointSuite) Test_16_SetPublicInternetAccess() {
// 	resp, err := su.portalCli.SetPublicInternetAccess(su.orgID, su.prjID, su.clusterID)
// 	su.Require().NoError(err)
// 	su.Require().NotNil(resp.Payload)
// 	su.Require().Equal("0", resp.Payload.BaseResp.ErrCode, "check error code")
// }

// func (su *TestDedicatedClusterAccessPointSuite) Test_17_ExecuteSql() {
// 	for range 30 {
// 		time.Sleep(10 * time.Second)
// 		err := su.portalCli.ExecuteSql(su.orgID, su.prjID, su.clusterID, "", su.password, "skip-verify")
// 		switch {
// 		case err == nil:
// 			// Success.
// 			return
// 		case strings.Contains(err.Error(), "connection refused"),
// 			strings.Contains(err.Error(), "no such host"),
// 			strings.Contains(err.Error(), "i/o timeout"):
// 			// Retry if the connection is refused.
// 			// Dns propagation may take some time.
// 		default:
// 			su.Fail(err.Error())
// 		}
// 	}
// }

// func (su *TestDedicatedClusterAccessPointSuite) Test_18_ExecuteAccessPointSql() {
// 	accessPointBody := su.openApiCli.GetAccessPoint(su.prjID, su.clusterID, su.accessPointID)
// 	err := sql.ExecuteSqlWithUrl(context.Background(), accessPointBody.PublicEndpoint.Host, "", su.password, "skip-verify", accessPointBody.PublicEndpoint.Port)
// 	su.Require().NoError(err)
// }

// func (su *TestDedicatedClusterAccessPointSuite) Test_19_DeleteAccessPoint() {
// 	resp, err := su.openApiCli.ScaleClusterByAccessPoint(su.prjID, su.clusterID, su.defaultPointID, &idl.ScaleTiDBConfig{
// 		NodeQuantity: 1,
// 	})
// 	su.Require().True(resp.IsSuccess())
// 	su.Require().NoError(err)

// 	// timeout: 30 minutes (1800 seconds)
// 	for i := 0; i < 180; i++ {
// 		status := su.portalCli.PollClusterStatus(su.orgID, su.prjID, su.clusterID)
// 		if *status == model.CentralClusterStatusNORMAL {
// 			break
// 		}
// 		su.T().Logf("cluster status: %s", *status)
// 		time.Sleep(10 * time.Second)
// 	}

// 	clusterResp := su.portalCli.GetCluster(su.orgID, su.prjID, su.clusterID)
// 	su.Require().Equal(2, int(*clusterResp.ClusterComponents.Tidb.Replicas))

// 	su.openApiCli.DeleteAccessPoint(su.prjID, su.clusterID, su.accessPointID)
// 	// timeout: 30 minutes (1800 seconds)
// 	for i := 0; i < 180; i++ {
// 		status := su.portalCli.PollClusterStatus(su.orgID, su.prjID, su.clusterID)
// 		if *status == model.CentralClusterStatusNORMAL {
// 			break
// 		}
// 		su.T().Logf("cluster status: %s", *status)
// 		time.Sleep(10 * time.Second)
// 	}
// 	clusterResp = su.portalCli.GetCluster(su.orgID, su.prjID, su.clusterID)
// 	su.Require().Equal(1, int(*clusterResp.ClusterComponents.Tidb.Replicas))
// }

// func (su *TestDedicatedClusterAccessPointSuite) Test_20_ScaleTiKV() {
// 	profiles := su.portalCli.QueryNodeProfiles(su.orgID, su.prjID, su.provider, su.region, false)

// 	var (
// 		tikvProfile *model.CentralClusterComponent
// 		tidbProfile *model.CentralClusterComponent
// 	)

// 	// Select the node profile with the minimal CPU cores for TiDB and TiKV.
// 	for _, profile := range profiles {
// 		switch *profile.ComponentType {
// 		case model.CentralComponentTypeCOMPONENTTYPETIDB:
// 			for _, p := range profile.Profiles {
// 				if strings.Contains(p.Name, "16C") {
// 					tidbProfile = &model.CentralClusterComponent{
// 						ComponentType:   lo.ToPtr(model.CentralComponentTypeCOMPONENTTYPETIKV),
// 						NodeProfileName: lo.ToPtr(p.Name),
// 						StorageSizeGi:   lo.ToPtr(p.MinStorageSizeGi),
// 					}
// 					break
// 				}
// 			}
// 		case model.CentralComponentTypeCOMPONENTTYPETIKV:
// 			for _, p := range profile.Profiles {
// 				if strings.Contains(p.Name, "16C") {
// 					tikvProfile = &model.CentralClusterComponent{
// 						ComponentType:   lo.ToPtr(model.CentralComponentTypeCOMPONENTTYPETIKV),
// 						NodeProfileName: lo.ToPtr(p.Name),
// 						Replicas:        lo.ToPtr(int32(3)),
// 						StorageSizeGi:   lo.ToPtr(p.MinStorageSizeGi),
// 					}
// 					break
// 				}
// 			}
// 		}
// 	}

// 	if tikvProfile == nil || tidbProfile == nil {
// 		return
// 	}

// 	resp, err := su.openApiCli.ScaleCluster(su.prjID, su.clusterID, &cluster.UpdateClusterParamsBodyConfigComponents{
// 		Tidb: &cluster.UpdateClusterParamsBodyConfigComponentsTidb{
// 			NodeSize: *tidbProfile.NodeProfileName,
// 		},
// 		Tikv: &cluster.UpdateClusterParamsBodyConfigComponentsTikv{
// 			NodeSize:     *tikvProfile.NodeProfileName,
// 			NodeQuantity: 3,
// 		},
// 	})
// 	su.Require().True(resp.IsSuccess())
// 	su.Require().NoError(err)

// 	// timeout: 30 minutes (1800 seconds)
// 	for i := 0; i < 180; i++ {
// 		status := su.portalCli.PollClusterStatus(su.orgID, su.prjID, su.clusterID)
// 		if *status == model.CentralClusterStatusNORMAL {
// 			break
// 		}
// 		su.T().Logf("cluster status: %s", *status)
// 		time.Sleep(10 * time.Second)
// 	}

// 	clusterResp := su.portalCli.GetCluster(su.orgID, su.prjID, su.clusterID)
// 	su.Require().Equal(3, int(*clusterResp.ClusterComponents.Tikv.Replicas))
// }
