package common

import (
	"encoding/json"
	"fmt"
	"strconv"
	"strings"
	"time"

	l4g "github.com/alecthomas/log4go"
	"github.com/avast/retry-go"
	"github.com/dailymotion/allure-go"
	"github.com/gavv/httpexpect/v2"
	"github.com/stretchr/testify/assert"
	"github.com/tidbcloud/qa-PlatformAPIAuto/commondata"
	"github.com/tidbcloud/qa-PlatformAPIAuto/models"
)

func CheckClusterInGetClusters(resp *httpexpect.Response, clusterId string) {
	data := resp.JSON().Raw()
	var index = 0
	var cluster *models.CentralCluster
	var clusters = models.CentralClusters{}
	resByre, resByteErr := json.Marshal(data)
	if resByteErr == nil {
		jsonRes := json.Unmarshal(resByre, &clusters)
		if jsonRes == nil {
			for index, cluster = range clusters.Items {
				if *cluster.ID == clusterId {
					break
				}
			}
		} else {
			l4g.Error("CheckClusterInGetClusters Unmarshal error: %s", jsonRes)
		}

	} else {
		l4g.Error("CheckClusterInGetClusters Marshal error: %s", resByteErr)
	}
	path := fmt.Sprintf("$.items[%d].id", index)
	l4g.Info("CheckClusterInGetClusters path is: %s", path)
	resp.JSON().Path(path).String().Equal(clusterId)
}

func GetBackList(resp *httpexpect.Response) *models.CentralBackups {
	data := resp.JSON().Raw()
	var backupList = models.CentralBackups{}
	resByre, resByteErr := json.Marshal(data)
	if resByteErr != nil {
		l4g.Error("GetBackList Marshal error: %s", resByteErr)
	} else {
		jsonRes := json.Unmarshal(resByre, &backupList)
		if jsonRes != nil {
			l4g.Error("GetBackList Unmarshal error: %s", jsonRes)
		}
	}
	return &backupList
}

func GetLogBackupList(resp *httpexpect.Response) *models.DataflowLogBackupListResp {
	data := resp.JSON().Raw()
	var backupList = models.DataflowLogBackupListResp{}
	resByre, resByteErr := json.Marshal(data)
	if resByteErr != nil {
		l4g.Error("GetLogBackList Marshal error: %s", resByteErr)
	} else {
		jsonRes := json.Unmarshal(resByre, &backupList)
		if jsonRes != nil {
			l4g.Error("GetLogBackList Unmarshal error: %s", jsonRes)
		}
	}
	return &backupList
}

func GetRunningLogBackup(resp *httpexpect.Response) *models.DataflowLogBackup {
	data := resp.JSON().Raw()
	var backupList = models.DataflowLogBackupListResp{}
	resByre, resByteErr := json.Marshal(data)
	if resByteErr != nil {
		l4g.Error("GetLogBackList Marshal error: %s", resByteErr)
		return nil
	} else {
		jsonRes := json.Unmarshal(resByre, &backupList)
		if jsonRes != nil {
			l4g.Error("GetLogBackList Unmarshal error: %s", jsonRes)
			return nil
		}
	}

	for _, logbackup := range backupList.Items {
		if logbackup.Status != nil && *logbackup.Status == models.DataflowLogBackupStatusRUNNING {
			return logbackup
		}
	}
	return nil
}

func GetBackupId(resp *httpexpect.Response, backupName, data string) interface{} {
	backupList := GetBackList(resp)
	for _, backup := range backupList.Items {
		if *backup.Name == backupName {
			switch data {
			case "id":
				return *backup.ID
			case "status":
				return backup.Status
			}

		}
	}
	if data == "id" {
		return "000000"
	} else {
		return models.CentralBackupStatus("000000")
	}
}

func GetLastBackupId(resp *httpexpect.Response) (string, string) {
	backupList := GetBackList(resp)
	if *backupList.Total < 1 {
		return "000000", "000000"
	} else {
		return *backupList.Items[*backupList.Total-1].ID, *backupList.Items[*backupList.Total-1].Name
	}

}

func CheckBackupInfo(resp *httpexpect.Response, backupName, checkData, checkValue string) {
	backupList := GetBackList(resp)
	var index = 0
	var backup *models.CentralBackup
	for index, backup = range backupList.Items {
		if *backup.Name == backupName {
			break
		}
	}
	path := fmt.Sprintf("$.items[%d].%s", index, checkData)
	l4g.Info("CheckBackupInfo path is: %s", path)
	resp.JSON().Path(path).String().Equal(checkValue)
}

func CheckBackupSize(resp *httpexpect.Response, backupName string) int {
	backupList := GetBackList(resp)
	var index = 0
	var backup *models.CentralBackup
	for index, backup = range backupList.Items {
		if *backup.Name == backupName {
			break
		}
	}
	path := fmt.Sprintf("$.items[%d].size", index)
	l4g.Info("CheckBackupInfo path is: %s", path)
	num, err := strconv.Atoi(resp.JSON().Path(path).String().Raw())
	if err != nil {
		return 0
	}
	return num
}

func IsEnableDPFeature(resp *httpexpect.Response, featureName string) bool {
	data := resp.JSON().Raw()
	var featuresRes = models.DataflowEnabledDPFeaturesForClusterResp{}
	resByre, resByteErr := json.Marshal(data)
	if resByteErr != nil {
		l4g.Error("GetDPEnableFeautres Marshal error: %s", resByteErr)
	} else {
		jsonRes := json.Unmarshal(resByre, &featuresRes)
		if jsonRes != nil {
			l4g.Error("GetDPEnableFeautres Unmarshal error: %s", jsonRes)
		}
	}
	for _, enableFeature := range featuresRes.Features {
		if strings.EqualFold(enableFeature, featureName) {
			return true
		}
	}
	return false
}

func GetLatestPiTRTimeRange(resp *httpexpect.Response) (*models.DataflowPiTRTimeRange, error) {
	body, err := json.Marshal(resp.JSON().Raw())
	if err != nil {
		return nil, err
	}

	timeRangeResp := new(models.DataflowPiTRTimeRangeListResp)
	if err := json.Unmarshal(body, timeRangeResp); err != nil {
		return nil, err
	}
	if len(timeRangeResp.Items) == 0 || len(timeRangeResp.Items[0].TimeRanges) == 0 {
		return nil, fmt.Errorf("empty time range")
	}

	return timeRangeResp.Items[0].TimeRanges[0], nil
}

func GetPiTRRanges(resp *httpexpect.Response) (*models.DataflowPiTRTimeRangeListResp, error) {
	body, err := json.Marshal(resp.JSON().Raw())
	if err != nil {
		return nil, err
	}

	timeRangeResp := new(models.DataflowPiTRTimeRangeListResp)
	if err := json.Unmarshal(body, timeRangeResp); err != nil {
		return nil, err
	}
	if len(timeRangeResp.Items) == 0 {
		return nil, fmt.Errorf("empty time range")
	}

	return timeRangeResp, nil
}

func GetOptionalDualRegion(resp *httpexpect.Response) (string, *models.BackupOptionsRespDualRegionOptions) {
	data := resp.JSON().Raw()
	var optionsRes = models.DataflowBackupOptionsResp{}
	resByre, resByteErr := json.Marshal(data)
	if resByteErr != nil {
		l4g.Error("GetOptionalDualRegion Marshal error: %s", resByteErr)
		return "", nil
	} else {
		jsonRes := json.Unmarshal(resByre, &optionsRes)
		if jsonRes != nil {
			l4g.Error("GetOptionalDualRegion Unmarshal error: %s", jsonRes)
			return "", nil
		}
	}
	if optionsRes.DualRegionOptions != nil && len(optionsRes.DualRegionOptions.RegionOptions) > 0 {
		return optionsRes.DualRegionOptions.ClusterRegion.Name, optionsRes.DualRegionOptions
	}
	return "", nil
}

func GetDualRegionSeting(resp *httpexpect.Response) *models.DataflowBackupSettingResp {
	data := resp.JSON().Raw()
	var setingRes = models.DataflowBackupSettingResp{}
	resByre, resByteErr := json.Marshal(data)
	if resByteErr != nil {
		l4g.Error("GetDualRegionSeting Marshal error: %s", resByteErr)
		return nil
	} else {
		jsonRes := json.Unmarshal(resByre, &setingRes)
		if jsonRes != nil {
			l4g.Error("GetDualRegionSeting Unmarshal error: %s", jsonRes)
			return nil
		}
		return &setingRes
	}
	return nil
}

func CheckRecycleBinList(t assert.TestingT, pathParam, bodyParam map[string]string, clusterName string) {
	err := retry.Do(func() error {
		resp := dbassRequestInfo.DbassRequest(t, "getRecycleBin", nil, pathParam, bodyParam, nil)
		l4g.Info("get recycle bin list, body is: %+v, pathParam is: %s, getRecycleBinResp is %s", bodyParam, pathParam, resp.JSON().Raw())
		data := resp.JSON().Raw()
		resBytes, err := json.Marshal(data)
		if err != nil {
			return fmt.Errorf("marshal getRecycleBin resp error: %w", err)
		}

		var recycleBins = commondata.RecycleBins{}
		if err := json.Unmarshal(resBytes, &recycleBins); err != nil {
			return fmt.Errorf("unmarshal getRecycleBin resp error: %w", err)
		}

		clusterExist := false
		for i, recycleBin := range recycleBins.Items {
			if recycleBin.Name == clusterName {
				l4g.Info("CheckRecycleBinList success, cluster: %s, index: %d", clusterName, i)
				clusterExist = true
				break
			}
		}
		if !clusterExist {
			return fmt.Errorf("cluster %s is not found in recycle bin", clusterName)
		}
		return nil
	},
		retry.DelayType(retry.FixedDelay),
		retry.Delay(time.Second*3),
		retry.Attempts(5),
		retry.OnRetry(func(n uint, err error) {
			l4g.Info("check recycle bin list retrying, retry num: %d, err: %s\n", n, err)
		}))
	if err != nil {
		t.Errorf("check recycle bin list error: %s", err.Error())
	}
}

func CheckAwsVPC(resp *httpexpect.Response, tenantVpcId, checkData, checkValue string) {
	data := resp.JSON().Raw()
	var vpcPeerings = models.CentralAwsVpcPeerings{}
	var vpcPeering *models.CentralAwsVpcPeering
	var index = 0
	resByre, resByteErr := json.Marshal(data)
	if resByteErr == nil {
		jsonRes := json.Unmarshal(resByre, &vpcPeerings)
		if jsonRes == nil {
			for index, vpcPeering = range vpcPeerings.Items {
				if *vpcPeering.TenantVpcID == tenantVpcId {
					break
				}
			}
		} else {
			l4g.Error("CheckClusterInGetClusters Unmarshal error: %s", jsonRes)
		}

	} else {
		l4g.Error("CheckClusterInGetClusters Marshal error: %s", resByteErr)
	}
	path := fmt.Sprintf("$.items[%d].%s", index, checkData)
	l4g.Info("CheckAwsVPC path is: %s", path)
	resp.JSON().Path(path).String().Equal(checkValue)
}

func CheckGcpVPC(resp *httpexpect.Response, projectId, checkData, checkValue string) {
	data := resp.JSON().Raw()
	var vpcPeerings = models.CentralGcpVpcPeerings{}
	var vpcPeering *models.CentralGcpVpcPeering
	var index = 0
	resByre, resByteErr := json.Marshal(data)
	if resByteErr == nil {
		jsonRes := json.Unmarshal(resByre, &vpcPeerings)
		if jsonRes == nil {
			for index, vpcPeering = range vpcPeerings.Items {
				if *vpcPeering.ProjectID == projectId {
					break
				}
			}
		} else {
			l4g.Error("CheckClusterInGetClusters Unmarshal error: %s", jsonRes)
		}

	} else {
		l4g.Error("CheckClusterInGetClusters Marshal error: %s", resByteErr)
	}
	path := fmt.Sprintf("$.items[%d].%s", index, checkData)
	l4g.Info("CheckGcpVPC path is: %s", path)
	resp.JSON().Path(path).String().Equal(checkValue)
}

func GetBandCardInfo(resp *httpexpect.Response, bankId string) (string, string, string) {
	for _, bandCardData := range resp.JSON().Array().Iter() {
		var bankCard = StripePaymentMethod{}
		resByre, resByteErr := json.Marshal(bandCardData.Raw())
		if resByteErr == nil {
			jsonRes := json.Unmarshal(resByre, &bankCard)
			if jsonRes == nil {
				if bankCard.ID == bankId {
					return bankCard.Card.Brand, bankCard.Card.Fingerprint, bankCard.Card.Last4
				}
			} else {
				l4g.Error("CheckClusterInGetClusters Unmarshal error: %s", jsonRes)
			}
		} else {
			l4g.Error("CheckClusterInGetClusters Marshal error: %s", resByteErr)
		}

	}
	return "000000", "000000", "000000"
}

func GetAnotherBandCardInfo(resp *httpexpect.Response, bankId string) string {
	for _, bandCardData := range resp.JSON().Array().Iter() {
		var bankCard = StripePaymentMethod{}
		resByre, resByteErr := json.Marshal(bandCardData.Raw())
		if resByteErr == nil {
			jsonRes := json.Unmarshal(resByre, &bankCard)
			if jsonRes == nil {
				if bankCard.ID != bankId {
					l4g.Info("bankCard id is:%s", bankCard.ID)
					return bankCard.ID
				}
			} else {
				l4g.Error("CheckClusterInGetClusters Unmarshal error: %s", jsonRes)
			}
		} else {
			l4g.Error("CheckClusterInGetClusters Marshal error: %s", resByteErr)
		}

	}
	return "000000"
}

func CheckInviteAccess(resp *httpexpect.Response, visitorEmail, checkData string, checkValue string) string {
	data := resp.JSON().Raw()
	var centralUsers = models.CentralUsers{}
	var index = 0
	var centralUser *models.CentralUser

	resByre, resByteErr := json.Marshal(data)
	if resByteErr == nil {
		jsonRes := json.Unmarshal(resByre, &centralUsers)
		if jsonRes == nil {
			for index, centralUser = range centralUsers.Items {
				if *centralUser.Email == visitorEmail {
					break
				} else {
					if index == len(centralUsers.Items)-1 {
						// means didn't find the visitorEmail
						return ""
					}
				}
			}
		} else {
			l4g.Error("CheckInviteAccess Resp Unmarshal error: %s", jsonRes)
		}
	} else {
		l4g.Error("CheckInviteAccess Resp Marshal error: %s", resByteErr)
	}
	path := fmt.Sprintf("$.items[%d].%s", index, checkData)
	l4g.Info("CheckInviteAccess path is: %s", path)
	if checkValue == "false" {
		resp.JSON().Path(path).IsEqual(false)
	} else {
		resp.JSON().Path(path).String().IsEqual(checkValue)
	}
	return *centralUser.ID
}

func GetInviteStatus(resp *httpexpect.Response, visitorEmail string) string {
	data := resp.JSON().Raw()
	var centralUsers = models.CentralUsers{}
	var index = 0
	var email_index = -1
	var centralUser *models.CentralUser

	resByre, resByteErr := json.Marshal(data)
	if resByteErr == nil {
		jsonRes := json.Unmarshal(resByre, &centralUsers)
		if jsonRes == nil {
			for index, centralUser = range centralUsers.Items {
				if *centralUser.Email == visitorEmail {
					email_index = index
					break
				}
			}
		} else {
			l4g.Error("CheckClusterInGetClusters Unmarshal error: %s", jsonRes)
		}

	} else {
		l4g.Error("CheckClusterInGetClusters Marshal error: %s", resByteErr)
	}
	if email_index == -1 {
		return ""
	}
	path := fmt.Sprintf("$.items[%d].status", email_index)
	l4g.Info("CheckInviteAccess path is: %s", path)
	return resp.JSON().Path(path).String().Raw()
}

func GetInviteAccessId(resp *httpexpect.Response, visitorEmail string) string {
	data := resp.JSON().Raw()
	var centralUsers = models.CentralUsers{}
	resByre, resByteErr := json.Marshal(data)
	if resByteErr == nil {
		jsonRes := json.Unmarshal(resByre, &centralUsers)
		if jsonRes == nil {
			for _, centralUser := range centralUsers.Items {
				if *centralUser.Email == visitorEmail {
					return *centralUser.ID
				}
			}
		} else {
			l4g.Error("CheckClusterInGetClusters Unmarshal error: %s", jsonRes)
		}

	} else {
		l4g.Error("CheckClusterInGetClusters Marshal error: %s", resByteErr)
	}
	return "000000"
}

func GetPrice(t assert.TestingT, provider, region string, tidbCount, tikvCount, tiflashCount int, tikvStorage,
	tiflashStorage int, tidbProfileId, tikvProfileId, tiflashProfileId string, isDevTier bool) float64 {
	profilePathParam := map[string]string{"org_id": dbassRequestInfo.CommonEnv.OrgId, "project_id": dbassRequestInfo.CommonEnv.ProjectId}
	clusterFeeBody := NewClusterFee(provider, region, tidbCount, tikvCount, tiflashCount, tikvStorage, tiflashStorage, tidbProfileId, tikvProfileId, tiflashProfileId, isDevTier)
	//获取价格
	profilesResp := dbassRequestInfo.DbassRequest(t, "getClusterFee", clusterFeeBody, profilePathParam, nil, nil)
	l4g.Info("get price info, the body is %s, pathParam is %s, profilesResp is %+v", nil,
		profilePathParam, profilesResp.JSON().Raw())
	data := profilesResp.JSON().Raw()
	var centralProfile = models.CentralCalcClusterFeeResp{}
	resByre, resByteErr := json.Marshal(data)
	var price = 0.0
	if resByteErr == nil {
		jsonRes := json.Unmarshal(resByre, &centralProfile)
		if jsonRes == nil {
			for _, cost := range centralProfile.Costs {
				price = price + cost.Compute + cost.Storage
				l4g.Info("price:%s, compute:%s, storage:%s", price, cost.Compute, cost.Storage)
			}
		} else {
			l4g.Error("CheckClusterInGetClusters Unmarshal error: %s", jsonRes)
		}

	} else {
		l4g.Error("CheckClusterInGetClusters Marshal error: %s", resByteErr)
	}
	return price
}

func Get3Price(t assert.TestingT, provider, region string, tidbCount, tikvCount, tiflashCount int, tikvStorage,
	tiflashStorage int, tidbProfileId, tikvProfileId, tiflashProfileId string, isDevTier bool) (float64, float64, float64) {
	profilePathParam := map[string]string{"org_id": dbassRequestInfo.CommonEnv.OrgId, "project_id": dbassRequestInfo.CommonEnv.ProjectId}
	clusterFeeBody := NewClusterFee(provider, region, tidbCount, tikvCount, tiflashCount, tikvStorage, tiflashStorage, tidbProfileId, tikvProfileId, tiflashProfileId, isDevTier)
	//获取价格
	profilesResp := dbassRequestInfo.DbassRequest(t, "getClusterFee", clusterFeeBody, profilePathParam, nil, nil)
	l4g.Info("get price info, the body is %s, pathParam is %s, profilesResp is %+v", nil,
		profilePathParam, profilesResp.JSON().Raw())
	data := profilesResp.JSON().Raw()
	var centralProfile = models.CentralCalcClusterFeeResp{}
	resByre, resByteErr := json.Marshal(data)
	// use float instead of decimal.Decimal, because the type of price is float in Costs.
	var price float64
	var computePrice float64
	var storagePrice float64

	if resByteErr == nil {
		jsonRes := json.Unmarshal(resByre, &centralProfile)
		if jsonRes == nil {
			for _, cost := range centralProfile.Costs {
				price = price + cost.Compute + cost.Storage
				computePrice = computePrice + cost.Compute
				storagePrice = storagePrice + cost.Storage
				l4g.Info("price:%s, compute:%s, storage:%s", price, cost.Compute, cost.Storage)
			}
		} else {
			l4g.Error("CheckClusterInGetClusters Unmarshal error: %s", jsonRes)
		}

	} else {
		l4g.Error("CheckClusterInGetClusters Marshal error: %s", resByteErr)
	}
	return price, computePrice, storagePrice
}

func GetPricePerHours(t assert.TestingT, orgId, projectId string) float64 {
	var totalPrice = 0.0
	pathparam := map[string]string{"org_id": orgId, "project_id": projectId}

	//获取project下的cluster
	getClusterListResp := dbassRequestInfo.DbassRequest(t, "getClusterList", nil, pathparam, nil, nil)
	l4g.Info("getClusterListResp is: %s", getClusterListResp.JSON().Raw().(map[string]interface{}))

	var cluster *models.CentralCluster
	var clusters = models.CentralClusters{}
	resByre, resByteErr := json.Marshal(getClusterListResp.JSON().Raw())
	if resByteErr == nil {
		jsonRes := json.Unmarshal(resByre, &clusters)
		if jsonRes == nil {
			for _, cluster = range clusters.Items {
				if cluster.Status != "CREATING" {
					if cluster.ClusterComponents.Tiflash == nil {
						totalPrice += GetPrice(t, *cluster.Provider, *cluster.Region, int(*cluster.ClusterComponents.Tidb.Replicas), int(*cluster.ClusterComponents.Tikv.Replicas),
							0, int(*cluster.ClusterComponents.Tikv.NodeProfile.StorageGi), 0, *cluster.ClusterComponents.Tidb.NodeProfile.NodeProfileID,
							*cluster.ClusterComponents.Tikv.NodeProfile.NodeProfileID, "", cluster.IsDevTier)
					} else {
						totalPrice += GetPrice(t, *cluster.Provider, *cluster.Region, int(*cluster.ClusterComponents.Tidb.Replicas), int(*cluster.ClusterComponents.Tikv.Replicas),
							int(*cluster.ClusterComponents.Tiflash.Replicas), int(*cluster.ClusterComponents.Tikv.NodeProfile.StorageGi), int(*cluster.ClusterComponents.Tiflash.NodeProfile.StorageGi), *cluster.ClusterComponents.Tidb.NodeProfile.NodeProfileID,
							*cluster.ClusterComponents.Tikv.NodeProfile.NodeProfileID, *cluster.ClusterComponents.Tiflash.NodeProfile.NodeProfileID, cluster.IsDevTier)
					}

				}
			}
		}
	}
	return totalPrice
}

func GetProjectUsage(resp *httpexpect.Response, projectName string) float64 {
	data := resp.JSON().Raw()
	var bills = CostExplorerResult{}
	//var index = 0
	var usage float64
	resByre, resByteErr := json.Marshal(data)
	if resByteErr == nil {
		jsonRes := json.Unmarshal(resByre, &bills)
		if jsonRes == nil {

			for project, projectData := range bills.UnbindedInvoiceCosts.LabelsUnitsCosts {
				if project == "project" {
					for projectNm, projectUsage := range projectData {
						if projectNm == projectName {
							usage, _ = strconv.ParseFloat(projectUsage.CostPrice, 64)
							break
						}
					}

				}
			}
		} else {
			l4g.Error("GetProjectUsage Unmarshal error: %s", jsonRes)
		}

	} else {
		l4g.Error("GetProjectUsage Marshal error: %s", resByteErr)
	}
	return usage
}

func CheckConectorInConectors(t assert.TestingT, resp *httpexpect.Response, connectorId string, is_in bool) {
	data := resp.JSON().Raw()
	var connectors *models.CentralConnectors
	var connector *models.CentralConnector
	resByre, resByteErr := json.Marshal(data)
	var flag = false
	if resByteErr == nil {
		jsonRes := json.Unmarshal(resByre, &connectors)
		if jsonRes == nil {
			for _, connector = range connectors.Items {
				if *connector.ConnectorID == connectorId {
					l4g.Info("find the connectorId")
					flag = true
					break
				}
			}
		} else {
			l4g.Error("CheckConectorInConectors Unmarshal error: %s", jsonRes)
		}

	} else {
		l4g.Error("CheckConectorInConectors Marshal error: %s", resByteErr)
	}
	value := httpexpect.NewBoolean(t, flag)
	allure.Step(allure.Description(fmt.Sprintf("Check ConectorId is In Conectors, result is:%s, expect value is:%s", flag, is_in)),
		allure.Action(func() {
			value.Equal(is_in)
		}))

}

func GetProfileIdAndStorage(resp *httpexpect.Response, tidbCpu, tikvCpu, tiflashCpu, tidbMem, tikvMem, tiflashMem string, isDevTier bool) (int, int, string, string, string, int, string, string, string) {
	data := resp.JSON().Raw()
	var profiles = NodeProfilesResponse{}
	//var index = 0
	var tikvStorageData, tiflashStorageData int
	var tidbProfileIdData, tikvProfileIdData, tiflashProfileIdData, tidbProfileName string
	var tidbScaleProfileIdData, tikvScaleProfileIdData, tiflashScaleProfileIdData string
	var is_arm int
	resByre, resByteErr := json.Marshal(data)
	if resByteErr == nil {
		jsonRes := json.Unmarshal(resByre, &profiles)
		if jsonRes == nil {

			for _, nodeProfile := range profiles.NodeProfiles {
				if nodeProfile.ComponentType == "COMPONENT_TYPE_TIDB" {
					if isDevTier {
						tidbProfileIdData = nodeProfile.Profiles[0].Name
					} else {
						tempCPU, _ := strconv.Atoi(tidbCpu)
						for _, profileInfo := range nodeProfile.Profiles {
							if tidbProfileIdData != "" && tidbScaleProfileIdData != "" {
								break
							}
							l4g.Info("profileInfo:%v", profileInfo)

							if (profileInfo.CPU == tempCPU && strconv.Itoa(profileInfo.Memory) == tidbMem) ||
								(profileInfo.CPU == tempCPU && tidbMem == "") {
								tidbProfileIdData = profileInfo.Name
								tidbProfileName = profileInfo.Name
							} else if profileInfo.CPU > tempCPU {
								tidbScaleProfileIdData = profileInfo.Name
							}
						}

					}

				} else if nodeProfile.ComponentType == "COMPONENT_TYPE_TIKV" {
					if isDevTier {
						tikvProfileIdData = nodeProfile.Profiles[0].Name
						tikvStorageData = nodeProfile.Profiles[0].MinStorageSizeGi
					} else {
						tempCPU, _ := strconv.Atoi(tikvCpu)
						for _, profileInfo := range nodeProfile.Profiles {
							if tikvProfileIdData != "" && tikvScaleProfileIdData != "" {
								break
							}
							l4g.Info("profileInfo:%v", profileInfo)

							if (profileInfo.CPU == tempCPU && tikvCpu != "" && strconv.Itoa(profileInfo.Memory) == tikvMem) ||
								(profileInfo.CPU == tempCPU && tikvCpu != "" && tikvMem == "") {

								tikvProfileIdData = profileInfo.Name
								tikvStorageData = profileInfo.MinStorageSizeGi
							} else if profileInfo.CPU > tempCPU {
								tikvScaleProfileIdData = profileInfo.Name
							}

						}
					}

				} else if nodeProfile.ComponentType == "COMPONENT_TYPE_TIFLASH" {
					if isDevTier {
						tiflashProfileIdData = nodeProfile.Profiles[0].Name
						tiflashStorageData = nodeProfile.Profiles[0].MinStorageSizeGi
					} else {
						tempCPU, _ := strconv.Atoi(tiflashCpu)
						for _, profileInfo := range nodeProfile.Profiles {
							if tiflashProfileIdData != "" && tiflashScaleProfileIdData != "" {
								break
							}
							l4g.Info("profileInfo:%v", profileInfo)
							if (profileInfo.CPU == tempCPU && strconv.Itoa(profileInfo.Memory) == tiflashMem) ||
								(profileInfo.CPU == tempCPU && tiflashMem == "") {
								tiflashProfileIdData = profileInfo.Name
								tiflashStorageData = profileInfo.MinStorageSizeGi

							} else if profileInfo.CPU > tempCPU {
								tiflashScaleProfileIdData = profileInfo.Name
							}
						}
					}

				}
			}
		} else {
			l4g.Error("GetProfileIdAndStorage Unmarshal error: %s", jsonRes)
		}

	} else {
		l4g.Error("GetProfileIdAndStorage Marshal error: %s", resByteErr)
	}
	if strings.Contains(tidbProfileName, "ARM") {
		is_arm = 1
	}
	return tikvStorageData, tiflashStorageData, tidbProfileIdData, tikvProfileIdData, tiflashProfileIdData, is_arm, tidbScaleProfileIdData, tikvScaleProfileIdData, tiflashScaleProfileIdData
}

func GetScaleUpProfileIdAndStorage(resp *httpexpect.Response, tidbCpu, tikvCpu, tiflashCpu string, isDevTier bool) (int, int, string, string, string) {
	data := resp.JSON().Raw()
	var profiles = NodeProfilesResponse{}
	//var index = 0
	var tikvStorageData, tiflashStorageData int
	var tidbProfileIdData, tikvProfileIdData, tiflashProfileIdData string
	resByre, resByteErr := json.Marshal(data)
	if resByteErr == nil {
		jsonRes := json.Unmarshal(resByre, &profiles)
		if jsonRes == nil {

			for _, nodeProfile := range profiles.NodeProfiles {
				if nodeProfile.ComponentType == "COMPONENT_TYPE_TIDB" {
					if isDevTier {
						tidbProfileIdData = nodeProfile.Profiles[0].Id
					} else {
						for index, profileInfo := range nodeProfile.Profiles {
							if profileInfo.Name == tidbCpu {
								if index < len(nodeProfile.Profiles)-1 {
									//profile按cpuNum排序的，为了避开4k+4k的配置限制，取最后一个配置
									tidbProfileIdData = nodeProfile.Profiles[len(nodeProfile.Profiles)-1].Id
								} else {
									tidbProfileIdData = profileInfo.Id
								}
								break
							}
						}

					}

				} else if nodeProfile.ComponentType == "COMPONENT_TYPE_TIKV" {
					if isDevTier {
						tikvProfileIdData = nodeProfile.Profiles[0].Id
						tikvStorageData = nodeProfile.Profiles[0].MinStorageSizeGi
					} else {
						for index, profileInfo := range nodeProfile.Profiles {
							if profileInfo.Name == tikvCpu && tikvCpu != "" {
								if index < len(nodeProfile.Profiles)-1 {
									//profile按cpuNum排序的，为了避开4k+4k的配置限制，取最后一个配置
									tikvProfileIdData = nodeProfile.Profiles[len(nodeProfile.Profiles)-1].Id
									tikvStorageData = nodeProfile.Profiles[len(nodeProfile.Profiles)-1].MinStorageSizeGi + 1
								} else {
									tikvProfileIdData = profileInfo.Id
									tikvStorageData = profileInfo.MinStorageSizeGi + 1
								}
								break
							}
						}
					}

				} else if nodeProfile.ComponentType == "COMPONENT_TYPE_TIFLASH" {
					if isDevTier || tiflashCpu == "" {
						//如果是dev tier，或原本tiflashcpu == "",restore新增一个tiflash
						tiflashProfileIdData = nodeProfile.Profiles[0].Id
						tiflashStorageData = nodeProfile.Profiles[0].MinStorageSizeGi
					} else {
						for index, profileInfo := range nodeProfile.Profiles {
							if profileInfo.Name == tiflashCpu {
								if index < len(nodeProfile.Profiles)-1 {
									//profile按cpuNum排序的，为了避开4k+4k的配置限制，取最后一个配置
									tiflashProfileIdData = nodeProfile.Profiles[len(nodeProfile.Profiles)-1].Id
									tiflashStorageData = nodeProfile.Profiles[len(nodeProfile.Profiles)-1].MinStorageSizeGi + 1
								} else {
									tiflashProfileIdData = profileInfo.Id
									tiflashStorageData = profileInfo.MinStorageSizeGi + 1
								}
								break
							}
						}
					}

				}
			}
		} else {
			l4g.Error("GetProfileIdAndStorage Unmarshal error: %s", jsonRes)
		}

	} else {
		l4g.Error("GetProfileIdAndStorage Marshal error: %s", resByteErr)
	}
	return tikvStorageData, tiflashStorageData, tidbProfileIdData, tikvProfileIdData, tiflashProfileIdData
}

func GetProjectName(resp *httpexpect.Response, projectId string) string {
	data := resp.JSON().Raw()
	var projects = models.CentralProjects{}
	//var index = 0
	resByre, resByteErr := json.Marshal(data)
	if resByteErr == nil {
		jsonRes := json.Unmarshal(resByre, &projects)
		if jsonRes == nil {

			for _, project := range projects.Items {
				if *project.ID == projectId {
					return *project.Name
				}
			}
		} else {
			l4g.Error("GetProjectName Unmarshal error: %s", jsonRes)
		}

	} else {
		l4g.Error("GetProjectName Marshal error: %s", resByteErr)
	}
	return ""
}

func GetSecondaryClusterRegion(resp *httpexpect.Response, primaryRegion string) string {
	data := resp.JSON().Raw()
	var regions = models.CentralGetSecondaryClusterProfilesResp{}
	//var index = 0
	resByre, resByteErr := json.Marshal(data)
	if resByteErr == nil {
		jsonRes := json.Unmarshal(resByre, &regions)
		if jsonRes == nil {

			for _, region := range regions.SecondaryClusterProfiles {
				if *region.Region.Name != primaryRegion {
					l4g.Info("region is:%s", *region.Region.Name)
					return *region.Region.Name
				}
			}
		} else {
			l4g.Error("GetSecondaryClusterRegion Unmarshal error: %s", jsonRes)
		}

	} else {
		l4g.Error("GetSecondaryClusterRegion Marshal error: %s", resByteErr)
	}
	return ""
}

func GetReplicationId(resp *httpexpect.Response) (string, string) {
	data := resp.JSON().Raw()
	var cluster = models.CentralGetClusterResp{}
	//var index = 0
	resByre, resByteErr := json.Marshal(data)
	if resByteErr == nil {
		jsonRes := json.Unmarshal(resByre, &cluster)
		if jsonRes == nil {

			for _, replication := range cluster.Cluster.Replications {
				if replication.Status == "replicating" {
					return replication.SecondaryClusterID, replication.ReplicationID
				}
			}
		} else {
			l4g.Error("GetReplicationId Unmarshal error: %s", jsonRes)
		}

	} else {
		l4g.Error("GetReplicationId Marshal error: %s", resByteErr)
	}
	return "", ""
}

func CheckNodeCountAndStatus(tidbresp, tikvresp, tiflashresp *httpexpect.Response) (bool, string, int, int, int) {
	//遍历，查看所有节点的状态是否是 normal
	tidbdata := tidbresp.JSON().Raw()
	tikvdata := tikvresp.JSON().Raw()
	tiflashdata := tiflashresp.JSON().Raw()

	var tidbnodes = models.CentralTiDBNodes{}
	var tikvnodes = models.CentralTiKVNodes{}
	var tiflashnodes = models.CentralTiFlashNodes{}
	var tidbCount, tikvCount, tiflashCount int

	tidbresByre, tidbresByteErr := json.Marshal(tidbdata)
	if tidbresByteErr == nil {
		jsonRes := json.Unmarshal(tidbresByre, &tidbnodes)
		if jsonRes == nil {
			for _, tidbnode := range tidbnodes.Items {
				tidbCount++
				if *&tidbnode.Status != "AVAILABLE" {
					return false, fmt.Sprintf("tidb node is %s", tidbnode.Status), tidbCount, tikvCount, tiflashCount
				}
			}
		} else {
			l4g.Error("ChecktidbNodeStatus Unmarshal error: %s", jsonRes)
		}

	} else {
		l4g.Error("ChecktidbNodeStatus Marshal error: %s", tidbresByteErr)
	}

	tikvresByre, tikvresByteErr := json.Marshal(tikvdata)
	if tikvresByteErr == nil {
		jsonRes := json.Unmarshal(tikvresByre, &tikvnodes)
		if jsonRes == nil {
			for _, tikvnode := range tikvnodes.Items {
				tikvCount++
				if *&tikvnode.Status != "AVAILABLE" {
					return false, fmt.Sprintf("tikv node is %s", tikvnode.Status), tidbCount, tikvCount, tiflashCount
				}
			}
		} else {
			l4g.Error("ChecktikvNodeStatus Unmarshal error: %s", jsonRes)
		}

	} else {
		l4g.Error("ChecktikvNodeStatus Marshal error: %s", tikvresByteErr)
	}

	tiflashresByre, tiflashresByteErr := json.Marshal(tiflashdata)
	if tiflashresByteErr == nil {
		jsonRes := json.Unmarshal(tiflashresByre, &tiflashnodes)
		if jsonRes == nil {
			if *tiflashnodes.Total == 0 {
				return true, "", tidbCount, tikvCount, tiflashCount
			}
			for _, tiflashnode := range tiflashnodes.Items {
				tiflashCount++
				if *&tiflashnode.Status != "AVAILABLE" {
					return false, fmt.Sprintf("tiflash node is %s", tiflashnode.Status), tidbCount, tikvCount, tiflashCount
				}
			}
		} else {
			l4g.Error("ChecktiflashNodeStatus Unmarshal error: %s", jsonRes)
		}

	} else {
		l4g.Error("ChecktiflashNodeStatus Marshal error: %s", tiflashresByteErr)
	}
	return true, "", tidbCount, tikvCount, tiflashCount
}

func GetPrivateLinkStatus(resp *httpexpect.Response, clusterId, endpointId string) (int, string) {
	data := resp.JSON().Raw()
	var cluster = ListPrivateLinkEndpointsResp{}
	//var index = 0
	resByre, resByteErr := json.Marshal(data)
	if resByteErr == nil {
		jsonRes := json.Unmarshal(resByre, &cluster)
		if jsonRes == nil {
			l4g.Info("cluster is:%v", cluster)
			for index, endpoint := range cluster.Endpoints {
				if endpoint.ClusterId == clusterId && endpoint.EndpointId == endpointId {
					return index, endpoint.Status
				}
			}
		} else {
			l4g.Error("GetPrivateLinkStatus Unmarshal error: %s", jsonRes)
		}

	} else {
		l4g.Error("GetPrivateLinkStatus Marshal error: %s", resByteErr)
	}
	return 0, ""
}

func GetCookieFromResp(response *httpexpect.Response) map[string]string {
	auth0 := strings.Split(response.Cookie("auth0").Raw().String(), ";")[0]
	auth0_compat := strings.Split(response.Cookie("auth0_compat").Raw().String(), ";")[0]
	did := strings.Split(response.Cookie("did").Raw().String(), ";")[0]
	did_compat := strings.Split(response.Cookie("did_compat").Raw().String(), ";")[0]
	header := map[string]string{"cookie": auth0 + ";" + auth0_compat + ";" + did + ";" + did_compat}
	return header
}

func GetRowDataTypeAndHeader(resp *httpexpect.Response) ([]string, []string) {
	data := resp.JSON().Raw()
	var dataList []string
	var preview = PreviewResp{}
	//var index = 0
	resByre, resByteErr := json.Marshal(data)
	if resByteErr == nil {
		jsonRes := json.Unmarshal(resByre, &preview)
		if jsonRes == nil {
			l4g.Info("preview is:%v", preview)
			return preview.Table.RowDataTypes, preview.Table.RowHeaders
		} else {
			l4g.Error("GetRowDataTypeAndHeader Unmarshal error: %s", jsonRes)
		}

	} else {
		l4g.Error("GetRowDataTypeAndHeader Marshal error: %s", resByteErr)
	}
	return dataList, dataList
}

func GetRecycleBinProfile(resp *httpexpect.Response, backupId string) (string, string, string, int, int) {
	data := resp.JSON().Raw()
	var restoreResp = models.CentralBackups{}
	var tidbProfileId, tikvProfileId, tiflashProfileId string
	var tikvStorage, tiflashStorage int64
	resByre, resByteErr := json.Marshal(data)
	if resByteErr == nil {
		jsonRes := json.Unmarshal(resByre, &restoreResp)
		if jsonRes == nil {
			for _, backupInfo := range restoreResp.Items {
				if *backupInfo.ID == backupId {
					if backupInfo.Cluster.ClusterComponents == nil {
						return "", "", "", 0, 0
					}
					if backupInfo.Cluster.ClusterComponents.Tidb != nil && backupInfo.Cluster.ClusterComponents.Tikv != nil {
						tidbProfileId = *backupInfo.Cluster.ClusterComponents.Tidb.NodeProfile.Name
						tikvProfileId = *backupInfo.Cluster.ClusterComponents.Tikv.NodeProfile.Name
						tikvStorage = *backupInfo.Cluster.ClusterComponents.Tikv.NodeProfile.StorageGi
					}
					if backupInfo.Cluster.ClusterComponents.Tiflash != nil {
						tiflashProfileId = *backupInfo.Cluster.ClusterComponents.Tiflash.NodeProfile.Name
						tiflashStorage = *backupInfo.Cluster.ClusterComponents.Tiflash.NodeProfile.StorageGi
					}
					return tidbProfileId, tikvProfileId, tiflashProfileId, int(tikvStorage), int(tiflashStorage)
				}
			}
		} else {
			l4g.Error("GetRecycleBinProfile Unmarshal error: %s", jsonRes)
		}

	} else {
		l4g.Error("GetRecycleBinProfile Marshal error: %s", resByteErr)
	}
	return "", "", "", 0, 0
}

func GetOrgTimeZone(resp *httpexpect.Response, orgId string) int {
	data := resp.JSON().Raw()
	var orgs = OrgsInfo{}
	//var index = 0
	resByre, resByteErr := json.Marshal(data)
	if resByteErr == nil {
		jsonRes := json.Unmarshal(resByre, &orgs)
		if jsonRes == nil {
			l4g.Info("orgs is:%v", orgs)
			for _, org := range orgs.Org {
				if org.Id == orgId {
					return org.Timezone
				}
			}

		} else {
			l4g.Error("GetOrgTimeZone Unmarshal error: %s", jsonRes)
		}

	} else {
		l4g.Error("GetOrgTimeZone Marshal error: %s", resByteErr)
	}
	return 0
}

// support go version >= 1.18
func UnmarshalResult[T any](resp *httpexpect.Response, targetStruct T) (T, error) {
	var targetData T
	data := resp.JSON().Raw()
	resByre, resByteErr := json.Marshal(data)
	if resByteErr == nil {
		jsonRes := json.Unmarshal(resByre, &targetData)
		if jsonRes == nil {
			return targetData, nil
		} else {
			return targetData, l4g.Error("UnmarshalResult From HTTP Response Unmarshal error: %s", jsonRes)
		}
	} else {
		return targetData, l4g.Error("MarshalResult From HTTP Response Marshal error: %s", resByteErr)
	}
}
