package common

import (
	"encoding/json"
	"fmt"
	l4g "github.com/alecthomas/log4go"
	"github.com/dailymotion/allure-go"
	"github.com/gavv/httpexpect/v2"
	"github.com/stretchr/testify/assert"
	"github.com/tidbcloud/qa-PlatformAPIAuto/models"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"strconv"
	"strings"
	"time"
)

func GetLabel() string {
	return strings.Split(RunUser, ".")[0]
	//return "lurongrong"
}

func GetDeploymentName() string {
	return "eks-infra-provider-e2e-" + GetLabel()
}

func GetConfigMapName() string {
	return "eks-provider-root-config-e2e-" + GetLabel()
}

func SetEKSSeedContext(t assert.TestingT) {
	if Envn != "staging" {
		return
	}
	SetupEksProfile(t, "385595570414", "dbaas-dev")

	//change context
	seedContext := "kubectl ctx `kubectl config get-contexts --output=name | grep staging-seed`"
	rel, flag := runCmd(t, seedContext)

	l4g.Info("set eks seed context result is %s", rel)
	allure.Step(
		allure.Story("SetEKSSeedContext"),
		allure.Description(fmt.Sprintf("set eks seed context result is %s %s", rel)),
		allure.Action(func() {
			assert.True(t, flag)
		}))
}

func DeployEKSConfigMap(t assert.TestingT, configMapName, configLabel, featureGates, expFeatureGates string) {
	if Envn != "staging" {
		//only support staging env now
		return
	}

	var yamlData []byte
	var err error

	yamlData, err = ioutil.ReadFile("../conf/e2e-configmap-staging.yaml")
	if err != nil {
		fmt.Printf("Error reading YAML file: %v\n", err)
		return
	}
	var configMap models.ConfigMap
	err = yaml.Unmarshal(yamlData, &configMap)
	if err != nil {
		fmt.Printf("Error unmarshaling ConfigMap YAML: %v\n", err)
		return
	}

	// Process the ConfigMap data
	if configMapName != "" {
		configMap.Metadata.Name = configMapName
	}

	var configYaml models.ConfigYAML
	err = yaml.Unmarshal([]byte(configMap.Data.ConfigYAML), &configYaml)
	if err != nil {
		fmt.Printf("Error unmarshaling ConfigYAML: %v\n", err)
		return
	}
	for i, expr := range configYaml.Predicate.MatchExpressions {
		if expr.Key == "cloud.pingcap.com/reconciler" {
			expPoint := &configYaml.Predicate.MatchExpressions[i]
			expPoint.Values = []string{configLabel}
		}
	}

	if featureGates != "" {
		var featureGatesData map[string]bool
		err = json.Unmarshal([]byte(featureGates), &featureGatesData)
		if err != nil {
			fmt.Println("Error:", err)
			return
		}

		configYaml.FeatureGates = updateFeatureGates(t, configYaml.FeatureGates, featureGatesData)
		l4g.Info("config map feature gate is %s", configYaml.FeatureGates)
	}

	if expFeatureGates != "" {
		var expFeatureGatesData []models.ExpFeatureGatesReq
		err = json.Unmarshal([]byte(expFeatureGates), &expFeatureGatesData)
		if err != nil {
			fmt.Println("Error:", err)
			return
		}
		configYaml.ExpFeatureGates = updateExpFeatureGates(t, configYaml.ExpFeatureGates, expFeatureGatesData)
		l4g.Info("config map exp feature gates is %s", configYaml.ExpFeatureGates)
	}

	configYamlDataBytes, err := yaml.Marshal(configYaml)
	if err != nil {
		fmt.Println("Error marshaling ConfigYAML:", err)
		return
	}
	configMap.Data.ConfigYAML = string(configYamlDataBytes)

	configMapDataBytes, err := yaml.Marshal(configMap)
	if err != nil {
		fmt.Println("Error marshaling ConfigMap:", err)
		return
	}

	cmd := fmt.Sprintf("echo '%s' | kubectl apply -f -", string(configMapDataBytes))
	rel, flag := runCmd(t, cmd)

	l4g.Info("deploy config map result is %s", rel)
	allure.Step(
		allure.Story("DeployConfigMap"),
		allure.Description(fmt.Sprintf("deploy config map result is %s", rel)),
		allure.Action(func() {
			assert.True(t, flag)
		}))
}

func DeployEKSInfraProvider(t assert.TestingT, deployName, image, configMapName string) {
	if Envn != "staging" {
		//only support staging env now
		return
	}

	var yamlData []byte
	var err error

	yamlData, err = ioutil.ReadFile("../conf/e2e-deployment-staging.yaml")
	if err != nil {
		fmt.Printf("Error reading YAML file: %v\n", err)
		return
	}

	var deployment models.Deployment
	err = yaml.Unmarshal(yamlData, &deployment)
	if err != nil {
		fmt.Printf("Error unmarshaling Deployment YAML: %v\n", err)
		return
	}
	// Process the Deployment data
	if deployName != "" {
		deployment.Metadata.Labels.App = deployName
		deployment.Metadata.Name = deployName
		deployment.Spec.Selector.MatchLabels.App = deployName
		deployment.Spec.Template.Metadata.Labels.App = deployName
	}
	if image != "" {
		deployment.Spec.Template.Spec.Containers[0].Image = image
	}
	if configMapName != "" {
		for i, expr := range deployment.Spec.Template.Spec.Volumes {
			if expr.Name == "root-config" {
				expPoint := &deployment.Spec.Template.Spec.Volumes[i]
				expPoint.ConfigMap.Name = configMapName
			}
		}
	}

	deploymentDataBytes, err := yaml.Marshal(deployment)
	if err != nil {
		fmt.Println("Error marshaling deployment:", err)
		return
	}

	cmd := fmt.Sprintf("echo '%s' | kubectl apply -f -", string(deploymentDataBytes))
	_, flag := runCmd(t, cmd)

	l4g.Info("apply infra provider result is %s", flag)
	allure.Step(
		allure.Story("DeployEKSProvider"),
		allure.Description(fmt.Sprintf("deploy infra provider result is %t", flag)),
		allure.Action(func() {
			assert.True(t, flag)
		}))

	//check pod running
	deployFlag := false
	for round := 0; round <= 20; round += 1 {
		cmd = fmt.Sprintf("kubectl get pod -n infra | grep %s ", deployName)
		rel, _ := runCmd(t, cmd)
		if strings.Contains(rel, "Running") {
			deployFlag = true
			break
		}
		time.Sleep(time.Second * 30)
	}
	allure.Step(
		allure.Story("DeployEKSProvider"),
		allure.Description(fmt.Sprintf("infra provider pod status is running : %t", deployFlag)),
		allure.Action(func() {
			assert.True(t, deployFlag)
		}))
}

func CheckClusterUseNewEKSInfraProvider(t assert.TestingT, configLabel, deploymentName, clusterId string) {
	if Envn != "staging" {
		return
	}
	//add label
	kubefile := "infra-api-kubeconfig.yaml"
	if Envn == "dev" {
		kubefile = "infra-api-kubeconfig-dev.yaml"
	}

	dbassRequestInfo = GetDbassRequestInfo(t)
	projectId := dbassRequestInfo.CommonEnv.ProjectId
	region := dbassRequestInfo.CommonEnv.AwsRegion
	cmd := fmt.Sprintf(" KUBECONFIG=~/%s kubectl label network eks-%s cloud.pingcap.com/reconciler=%s -n staging-%s", kubefile, region, configLabel, projectId)
	_, flag := runCmd(t, cmd)
	allure.Step(
		allure.Story("ChangeNetworkLabel"),
		allure.Description(fmt.Sprintf("change network label result is:  %t", flag)),
		allure.Action(func() {
			assert.True(t, flag)
		}))

	cmd = fmt.Sprintf(" KUBECONFIG=~/%s kubectl label cluster %s cloud.pingcap.com/reconciler=%s -n staging-%s", kubefile, clusterId, configLabel, projectId)
	_, flag = runCmd(t, cmd)
	allure.Step(
		allure.Story("ChangeClusterLabel"),
		allure.Description(fmt.Sprintf("change cluster label result is:  %t", flag)),
		allure.Action(func() {
			assert.True(t, flag)
		}))

	//check upgrade
	cmd = fmt.Sprintf("kubectl get pods -l app=%s -o=jsonpath='{.items[*].metadata.name}' -n infra", deploymentName)
	podName, _ := runCmd(t, cmd)
	l4g.Info("eks provider pod name is %s", podName)

	cmd = fmt.Sprintf("kubectl logs %s -n infra | grep %s", podName, clusterId)
	//assert reconcile success,wait 10min
	reconcileFlag := false
	for round := 0; round <= 20; round += 1 {
		rel, _ := runCmd(t, cmd)
		if strings.Contains(rel, "ReconcileSuccess") {
			reconcileFlag = true
			break
		}
		time.Sleep(time.Second * 30)
	}
	l4g.Info("check cluster use new eks infra provider: %t", reconcileFlag)
	allure.Step(
		allure.Story("CheckClusterUseNewEKSInfraProvider"),
		allure.Description(fmt.Sprintf("check cluster use new eks infra provider reconcile success is:  %t", reconcileFlag)),
		allure.Action(func() {
			assert.True(t, reconcileFlag)
		}))
}

func IsTiProxyExist(t assert.TestingT, provider, clusterId string) (bool, error) {
	endTime := time.Now()
	startTime := endTime.Add(-2 * time.Minute)
	query := fmt.Sprintf("rate(process_cpu_seconds_total{component=\"tiproxy\"}[30s])")
	metricsResp, err := GetMetrics(t, provider, clusterId, query, startTime, endTime)
	if err != nil {
		return false, err
	}
	TiProxyCPUUsage := make(map[string][]float64)
	for _, metrics := range metricsResp.JSON().Path("$.data.result").Array().Iter() {
		instance := metrics.Path("$.metric.instance").String().Raw()
		l4g.Info("TiProxy cpu usage instance=%s, values=%s", instance, metrics.Path("$.values").Array())
		for _, values := range metrics.Path("$.values").Array().Iter() {
			value, err := strconv.ParseFloat(values.Array().Element(1).String().Raw(), 64)
			if err != nil {
				l4g.Error("parse metrics response error [err=%s]", err.Error())
				return false, err
			}
			TiProxyCPUUsage[instance] = append(TiProxyCPUUsage[instance], value)
		}
	}
	if len(TiProxyCPUUsage) == 0 {
		return false, nil
	}
	return true, nil
}

func GetTiDBUptime(t assert.TestingT, provider, clusterId string) (map[string][]float64, error) {
	endTime := time.Now()
	startTime := endTime.Add(-2 * time.Minute)
	query := fmt.Sprintf("(time() - process_start_time_seconds{job=~\".*tidb\"})")
	metricsResp, err := GetMetrics(t, provider, clusterId, query, startTime, endTime)
	if err != nil {
		return nil, err
	}
	TiDBUptimes := make(map[string][]float64)
	for _, metrics := range metricsResp.JSON().Path("$.data.result").Array().Iter() {
		instance := metrics.Path("$.metric.instance").String().Raw()
		l4g.Info("TiProxy cpu usage [instance=%v] [values=%v]", instance, metrics.Path("$.values").Array())
		for _, values := range metrics.Path("$.values").Array().Iter() {
			value, err := strconv.ParseFloat(values.Array().Element(1).String().Raw(), 64)
			if err != nil {
				return nil, err
			}
			TiDBUptimes[instance] = append(TiDBUptimes[instance], value)
		}
	}
	return TiDBUptimes, nil
}

func GetConnection(t assert.TestingT, provider, clusterId string) (map[string][]int64, error) {
	endTime := time.Now().Add(-30 * time.Second)
	startTime := endTime.Add(-1 * time.Minute)
	query := fmt.Sprintf("tidb_server_connections{}")
	metricsResp, err := GetMetrics(t, provider, clusterId, query, startTime, endTime)
	if err != nil {
		return nil, err
	}
	TiDBActiveConnection := make(map[string][]int64)
	for _, metrics := range metricsResp.JSON().Path("$.data.result").Array().Iter() {
		instance := metrics.Path("$.metric.instance").String().Raw()
		for _, values := range metrics.Path("$.values").Array().Iter() {
			value, err := strconv.ParseInt(values.Array().Element(1).String().Raw(), 10, 64)
			if err != nil {
				return nil, err
			}
			TiDBActiveConnection[instance] = append(TiDBActiveConnection[instance], value)
		}
	}
	return TiDBActiveConnection, nil
}

func CheckPodUptime(t assert.TestingT, provider, clusterId, podType, startTime string) {
	dbassRequestInfo = GetDbassRequestInfo(t)
	orgId := dbassRequestInfo.CommonEnv.OrgId
	projectId := dbassRequestInfo.CommonEnv.ProjectId

	// get cluster info
	clusterPathParam := map[string]string{"org_id": orgId, "project_id": projectId, "cluster_id": clusterId}
	getClusterInfoResp := dbassRequestInfo.DbassRequest(t, "getClusterInfo", nil, clusterPathParam, nil, nil)
	if startTime == "" {
		startTime = getClusterInfoResp.JSON().Object().Value("cluster").Object().Path("$.created_at").String().Raw()
	}
	// get prom cluster id
	token := PrepareToken(t)
	header := map[string]string{"Authorization": token, "Content-Type": "application/json"}
	var bizType string
	if Envn == "staging" {
		bizType = "staging-tidbcloud"
	} else if Envn == "dev" {
		bizType = "dev-tidbcloud"
	} else {
		bizType = "tidbcloud"
	}

	var region string
	if provider == "aws" {
		region = dbassRequestInfo.CommonEnv.AwsRegion
	}
	if provider == "gcp" {
		region = dbassRequestInfo.CommonEnv.GcpRegion
	}

	param := map[string]string{"tenantID": orgId, "projectID": projectId, "applicationID": clusterId, "vendor": provider, "region": region, "bizType": bizType}
	dbassRequestInfo.ResetHost("o11yGlobal", provider, region)

	var promResp *httpexpect.Response
	for i := 0; i < 30; i++ {
		promResp = dbassRequestInfo.DbassRequest(t, "getPromInfo", nil, nil, param, header)
		l4g.Info("get prom information param is %s, status code is %s", param, promResp.Raw().StatusCode)
		l4g.Info("prom information response is %s", promResp.JSON().Raw())
		if promResp.JSON().Object().Value("data").Object().Value("clusters").Array().Length().Raw() > 0 {
			break
		}
		time.Sleep(time.Second * 10)
	}
	allure.Step(
		allure.Story("CheckPromStatus"),
		allure.Description(fmt.Sprintf("get prom information status code is %d, response data is %s", promResp.Raw().StatusCode, promResp.JSON().Raw())),
		allure.Action(func() {
			promResp.Status(StatusOK)
		}))
	promId := promResp.JSON().Object().Value("data").Object().Value("clusters").Array().First().Object().Value("id").String().Raw()
	l4g.Info("The prom id is: ", promId)

	//uptime range query
	dbassRequestInfo.ResetHost("o11yRegional", provider, region)
	pathParam := map[string]string{"promId": promId}
	endTime := time.Now().Unix()

	//tidb pod
	if podType == TiDBType || podType == "all" {
		uptimeMetricName := fmt.Sprintf("avg by (cluster_id, instance) ((time() - process_start_time_seconds{component=\"%s\"}))", "tidb")
		param = map[string]string{"query": uptimeMetricName, "start": startTime, "end": strconv.FormatInt(endTime, 10), "step": "15s"}
		metricsResp := dbassRequestInfo.DbassRequest(t, "getInternalMetrics", nil, pathParam, param, nil)
		metricsResp.Status(StatusOK)
		l4g.Info("get metric pathparam is %s, status code is %s", param, metricsResp.Raw().StatusCode)
		restart := checkPodRestart(metricsResp)

		allure.Step(
			allure.Story("CheckTiDBPodUptime"),
			allure.Description("check tidb pod restart"),
			allure.Action(func() {
				for name, value := range restart {
					l4g.Info("%s pod restart is : %t", name, value)
					assert.True(t, !value)
				}
			}))
	}
	if podType == TiKVType || podType == "all" {
		//tikv pod
		uptimeMetricName := fmt.Sprintf("avg by (cluster_id, instance) ((time() - process_start_time_seconds{component=\"%s\"}))", "tikv")
		param = map[string]string{"query": uptimeMetricName, "start": startTime, "end": strconv.FormatInt(endTime, 10), "step": "15s"}
		metricsResp := dbassRequestInfo.DbassRequest(t, "getInternalMetrics", nil, pathParam, param, nil)
		l4g.Info("get metric pathparam is %s, status code is %s", param, metricsResp.Raw().StatusCode)
		metricsResp.Status(StatusOK)
		restart := checkPodRestart(metricsResp)

		allure.Step(
			allure.Story("CheckTiKVPodUptime"),
			allure.Description("check tikv pod restart"),
			allure.Action(func() {
				for name, value := range restart {
					l4g.Info("%s pod restart is : %t", name, value)
					assert.True(t, !value)
				}
			}))
	}

	if podType == TiFlashType || podType == "all" {
		component := getClusterInfoResp.JSON().Object().Value("cluster").Object().Value("cluster_components").Object().Raw()
		if component["tiflash"] != nil {
			dbassRequestInfo.ResetHost("o11yRegional", provider, region)

			//tiflash pod
			uptimeMetricName := fmt.Sprintf("avg by (cluster_id, instance) ((time() - process_start_time_seconds{component=\"%s\"}))", "tiflash")
			param = map[string]string{"query": uptimeMetricName, "start": startTime, "end": strconv.FormatInt(endTime, 10), "step": "15s"}
			metricsResp := dbassRequestInfo.DbassRequest(t, "getInternalMetrics", nil, pathParam, param, nil)
			metricsResp.Status(StatusOK)
			restart := checkPodRestart(metricsResp)

			allure.Step(
				allure.Story("CheckTiFlashPodUptime"),
				allure.Description("check tiflash pod restart"),
				allure.Action(func() {
					for name, value := range restart {
						l4g.Info("%s pod restart is : %t", name, value)
						assert.True(t, !value)
					}
				}))
		}
	}

	exist, err := IsTiProxyExist(t, provider, clusterId)
	assert.NoError(t, err)
	if exist {
		dbassRequestInfo.ResetHost("o11yRegional", provider, region)

		uptimeMetricName := fmt.Sprintf("avg by (cluster_id, instance) ((time() - process_start_time_seconds{component=\"%s\"}))", "tiproxy")
		param = map[string]string{"query": uptimeMetricName, "start": startTime, "end": strconv.FormatInt(endTime, 10), "step": "15s"}
		metricsResp := dbassRequestInfo.DbassRequest(t, "getInternalMetrics", nil, pathParam, param, nil)
		metricsResp.Status(StatusOK)
		l4g.Info("get metric pathparam is %s, status code is %s", param, metricsResp.Raw().StatusCode)
		restart := checkPodRestart(metricsResp)

		allure.Step(
			allure.Story("CheckTiProxyPodUptime"),
			allure.Description("check tiproxy pod restart"),
			allure.Action(func() {
				for name, value := range restart {
					l4g.Info("%s pod restart is : %t", name, value)
					assert.True(t, !value)
				}
			}))
	}

	dbassRequestInfo.ResetHost("", provider, region)
}

func CheckClusterResourceDeleteSuccessByInfraAPI(t assert.TestingT, clusterId string) {
	if Envn != "staging" {
		return
	}
	//add label
	kubefile := "infra-api-kubeconfig.yaml"
	if Envn == "dev" {
		kubefile = "infra-api-kubeconfig-dev.yaml"
	}

	dbassRequestInfo = GetDbassRequestInfo(t)
	projectId := dbassRequestInfo.CommonEnv.ProjectId

	//delete o11yCluster label
	cmd := fmt.Sprintf(" KUBECONFIG=~/%s kubectl label O11yCluster %s cloud.pingcap.com/reconciler- -n staging-%s", kubefile, clusterId, projectId)
	_, flag := runCmd(t, cmd)
	allure.Step(
		allure.Story("DeleteO11yClusterLabel"),
		allure.Description(fmt.Sprintf("delete o11yCluster label result is:  %t", flag)),
		allure.Action(func() {
			assert.True(t, flag)
		}))

	cmd = fmt.Sprintf(" KUBECONFIG=~/%s kubectl get cluster -n staging-%s", kubefile, projectId)
	//check cluster resource delete success,wait 15min
	deleteFlag := false
	for round := 0; round <= 15; round += 1 {
		rel, _ := runCmd(t, cmd)
		if !strings.Contains(rel, clusterId) {
			deleteFlag = true
			break
		}
		time.Sleep(time.Second * 60)
	}

	allure.Step(
		allure.Story("ChangeDeleteClusterResource"),
		allure.Description(fmt.Sprintf("delete cluster resource result is:  %t", deleteFlag)),
		allure.Action(func() {
			assert.True(t, deleteFlag)
		}))
}

func DeleteClusterLabel(t assert.TestingT, clusterId string) {
	if Envn != "staging" {
		return
	}
	//add label
	kubefile := "infra-api-kubeconfig.yaml"
	if Envn == "dev" {
		kubefile = "infra-api-kubeconfig-dev.yaml"
	}

	dbassRequestInfo = GetDbassRequestInfo(t)
	projectId := dbassRequestInfo.CommonEnv.ProjectId

	cmd := fmt.Sprintf(" KUBECONFIG=~/%s kubectl label cluster %s cloud.pingcap.com/reconciler- -n staging-%s", kubefile, clusterId, projectId)
	_, flag := runCmd(t, cmd)
	allure.Step(
		allure.Story("DeleteClusterLabel"),
		allure.Description(fmt.Sprintf("delete cluster label result is:  %t", flag)),
		allure.Action(func() {
			assert.True(t, flag)
		}))
}

func DeleteNetworkLabel(t assert.TestingT) {
	if Envn != "staging" {
		return
	}
	//add label
	kubefile := "infra-api-kubeconfig.yaml"
	if Envn == "dev" {
		kubefile = "infra-api-kubeconfig-dev.yaml"
	}

	dbassRequestInfo = GetDbassRequestInfo(t)
	projectId := dbassRequestInfo.CommonEnv.ProjectId
	region := dbassRequestInfo.CommonEnv.AwsRegion
	cmd := fmt.Sprintf(" KUBECONFIG=~/%s kubectl label network eks-%s cloud.pingcap.com/reconciler- -n staging-%s", kubefile, region, projectId)
	_, flag := runCmd(t, cmd)
	allure.Step(
		allure.Story("DeleteNetworkLabel"),
		allure.Description(fmt.Sprintf("delete network label result is:  %t", flag)),
		allure.Action(func() {
			assert.True(t, flag)
		}))
}

func DeleteEKSInfraProvider(t assert.TestingT, deploymentName string) {
	if Envn != "staging" {
		return
	}
	cmd := fmt.Sprintf("kubectl delete deployment %s -n infra", deploymentName)
	rel, flag := runCmd(t, cmd)

	l4g.Info("delete deployment result is %s", rel)
	allure.Step(
		allure.Story("DeployEKSProvider"),
		allure.Description(fmt.Sprintf("delete deployment result is %s", rel)),
		allure.Action(func() {
			assert.True(t, flag)
		}))
}

func DeleteEKSConfigMap(t assert.TestingT, configMapName string) {
	if Envn != "staging" {
		return
	}
	cmd := fmt.Sprintf("kubectl delete configmap %s -n infra", configMapName)
	rel, flag := runCmd(t, cmd)

	l4g.Info("delete configmap result is %s", rel)
	allure.Step(
		allure.Story("DeployEKSProvider"),
		allure.Description(fmt.Sprintf("delete configmap result is %s", rel)),
		allure.Action(func() {
			assert.True(t, flag)
		}))
}

func runCmd(t assert.TestingT, cmd string) (string, bool) {
	dbassRequestInfo = GetDbassRequestInfo(t)
	loginUser := dbassRequestInfo.CommonEnv.LoginUser
	ec2Host := dbassRequestInfo.CommonEnv.Ec2Host
	cerPath := dbassRequestInfo.CommonEnv.CerPath

	return ConnectInstance(ec2Host, cerPath, loginUser, cmd)
}

func updateFeatureGates(t assert.TestingT, oriFeatureGates map[string]models.FeatureGate, featureGates map[string]bool) map[string]models.FeatureGate {
	dbassRequestInfo = GetDbassRequestInfo(t)
	projectId := dbassRequestInfo.CommonEnv.ProjectId

	for key, value := range featureGates {
		find := false
		for k, expr := range oriFeatureGates {
			if k == key {
				gateData := expr
				if value {
					gateData.EnabledProjects = []string{projectId}
				} else {
					gateData.DisabledProjects = []string{projectId}
				}
				oriFeatureGates[k] = gateData
				find = true
				break
			}
		}
		if !find {
			// it is a new feature gate
			var gateData models.FeatureGate
			gateData.All = value
			if value {
				gateData.EnabledProjects = []string{projectId}
			} else {
				gateData.DisabledProjects = []string{projectId}
			}
			oriFeatureGates[key] = gateData
		}
	}
	return oriFeatureGates
}

func updateExpFeatureGates(t assert.TestingT, oriExpFeatureGates []models.ExpFeatureGates, expFeatureGates []models.ExpFeatureGatesReq) []models.ExpFeatureGates {
	dbassRequestInfo = GetDbassRequestInfo(t)
	projectId := dbassRequestInfo.CommonEnv.ProjectId

	for _, value := range expFeatureGates {
		find := false
		for i, expr := range oriExpFeatureGates {
			if expr.Name == value.Name {
				expFeatureGate := expr
				if value.Open {
					expFeatureGate.FeatureGate.EnabledProjects = []string{projectId}
				} else {
					expFeatureGate.FeatureGate.DisabledProjects = []string{projectId}
				}
				if value.MinSupportVersion != "" {
					expFeatureGate.MinSupportVersion = value.MinSupportVersion
				}
				if value.Command != "" {
					expFeatureGate.Command = value.Command
				}
				expFeatureGate.Name = value.Name
				oriExpFeatureGates[i] = expFeatureGate
				find = true
				break
			}
		}
		if !find {
			//it is a new exp feature gate
			var expFeatureGate models.ExpFeatureGates
			if value.Open {
				expFeatureGate.FeatureGate.EnabledProjects = []string{projectId}
			} else {
				expFeatureGate.FeatureGate.DisabledProjects = []string{projectId}
			}
			expFeatureGate.MinSupportVersion = value.MinSupportVersion
			expFeatureGate.Command = value.Command
			expFeatureGate.Name = value.Name
			oriExpFeatureGates = append(oriExpFeatureGates, expFeatureGate)
		}
	}
	return oriExpFeatureGates
}
func checkPodRestart(metricsResp *httpexpect.Response) map[string]bool {
	rel := make(map[string]bool)

	for _, metrics := range metricsResp.JSON().Path("$.data.result").Array().Iter() {
		name := metrics.Path("$.metric.instance").String().Raw()
		min := float64(0)
		for k, metric := range metrics.Path("$.values").Array().Iter() {
			value, _ := strconv.ParseFloat(metric.Array().Element(1).String().Raw(), 64)
			if k == 0 {
				min = value
			} else if value >= min {
				min = value
			} else {
				rel[name] = true
				continue
			}
		}
	}
	return rel
}

// GetMetrics get metrics from o11y web prometheus server
func GetMetrics(t assert.TestingT, provider, clusterId string, query string, startTime, endTime time.Time) (*httpexpect.Response, error) {
	dbassRequestInfo = GetDbassRequestInfo(t)
	orgId := dbassRequestInfo.CommonEnv.OrgId
	projectId := dbassRequestInfo.CommonEnv.ProjectId
	startUnixTime := strconv.FormatInt(startTime.Unix(), 10)
	token := PrepareToken(t)
	header := map[string]string{"Authorization": token, "Content-Type": "application/json"}
	var bizType string
	if Envn == "staging" {
		bizType = "staging-tidbcloud"
	} else if Envn == "dev" {
		bizType = "dev-tidbcloud"
	} else {
		bizType = "tidbcloud"
	}

	var region string
	if provider == "aws" {
		region = dbassRequestInfo.CommonEnv.AwsRegion
	}
	if provider == "gcp" {
		region = dbassRequestInfo.CommonEnv.GcpRegion
	}
	param := map[string]string{"tenantID": orgId, "projectID": projectId, "applicationID": clusterId, "vendor": provider, "region": region, "bizType": bizType}
	dbassRequestInfo.ResetHost("o11yGlobal", provider, region)

	var promResp *httpexpect.Response
	for i := 0; i < 30; i++ {
		promResp = dbassRequestInfo.DbassRequest(t, "getPromInfo", nil, nil, param, header)
		l4g.Info("get prom information param is %s, status code is %s", param, promResp.Raw().StatusCode)
		l4g.Info("prom information response is %s", promResp.JSON().Raw())
		if promResp.JSON().Object().Value("data").Object().Value("clusters").Array().Length().Raw() > 0 {
			break
		}
		time.Sleep(time.Second * 10)
	}
	if promResp.Raw().StatusCode != StatusOK {
		return nil, fmt.Errorf("prometheus request error [status code=%s]", promResp.Raw().StatusCode)
	}
	promId := promResp.JSON().Object().Value("data").Object().Value("clusters").Array().First().Object().Value("id").String().Raw()
	l4g.Info("The prom id is: %v", promId)
	dbassRequestInfo.ResetHost("o11yRegional", provider, region)
	pathParam := map[string]string{"promId": promId}
	endUnixTime := strconv.FormatInt(endTime.Unix(), 10)
	param = map[string]string{"query": query, "start": startUnixTime, "end": endUnixTime}
	metricsResp := dbassRequestInfo.DbassRequest(t, "getInternalMetrics", nil, pathParam, param, nil)
	dbassRequestInfo.ResetHost("", provider, region)
	if promResp.Raw().StatusCode != StatusOK {
		return nil, fmt.Errorf("prometheus request error [status code=%s]", promResp.Raw().StatusCode)
	} else {
		return metricsResp, nil
	}
}
