package prometheus

import (
	"context"
	"errors"
	"fmt"
	"math"
	"strings"
	"time"

	"github.com/pingcap-inc/clinic/clinic_data_service/pkg/config"
	"github.com/pingcap-inc/clinic/clinic_data_service/pkg/log"
	"github.com/pingcap-inc/clinic/clinic_data_service/pkg/utils"
	"github.com/prometheus/common/model"
	"go.uber.org/zap"
)

// GetMaxCPUTimeIn5MinWindowRespResp Analysis result structure
type GetMaxCPUTimeIn5MinWindowRespResp struct {
	ClusterId    string
	Timestamp    time.Time
	Instance     string
	InstanceType string
	// CpuTime unit is min
	CpuTime               float64
	CpuTimeIn5MinOfDayPer float64
}

type CheckCpuResp struct {
	Digest                string
	MaxTotalCpuTime       float64
	MaxTotalCpuTimePer    float64
	MaxCpuTimeIn5MinOfDay float64
	CpuTimeIn5MinOfDayPer float64
}

type MaxCPUTimeIn5MinOfDay struct {
	ClusterId             string
	Instance              string
	InstanceType          string
	MaxCPUTimeIn5Min      float64
	Timestamp             string
	CpuTimeIn5MinOfDayPer float64
}
type GetMaxTotalCPUTimeFromClusterArrResp struct {
	MaxTotalCpuTime    float64
	MaxTotalCpuTimePer float64
	MaxClusterId       string
}

// GetMaxCPUTimeIn5MinWindow Get the maximum value of CPU usage accumulated in 5-minute cycles over the past day
func GetMaxCPUTimeIn5MinWindow(clusterId string, sqlDigest string, interval string, queryTime time.Time) (*GetMaxCPUTimeIn5MinWindowRespResp, error) {

	pc, err := CreatePrometheusClient(context.Background(), CreatePrometheusClientReq{ClusterID: clusterId})
	if err != nil {
		return nil, err
	}
	ctx := context.Background()
	return pc.GetMaxCPUTimeIn5MinWindow(ctx, clusterId, sqlDigest, interval, queryTime)
}
func (pc *PrometheusClient) GetMaxCPUTimeIn5MinWindow(ctx context.Context, clusterId string, sqlDigest string, interval string, queryTime time.Time) (*GetMaxCPUTimeIn5MinWindowRespResp, error) {
	// In Prometheus, sqlDigest is uppercase
	sqlDigest = strings.ToUpper(sqlDigest)

	// Set query context with increased timeout
	ctx, cancel := context.WithTimeout(ctx, 40*time.Second)
	defer cancel()

	// Use actual parameters from curl command
	query := fmt.Sprintf(`topk(1, max_over_time(sum_over_time(topsql_cpu_time_ms{sql_digest="%s"}[5m])[%s:5m]))`, sqlDigest, interval)

	// Parse the original date and add one day
	// queryTime := GetDayQueryTime(queryTimeStr...)
	log.Info(ctx, "prometheus MaxCPUTimeIn5MinWindow req ", zap.String("query", query), zap.Any("queryTime", queryTime))

	// Execute range query (corresponding to query_range in curl command)
	result, err := pc.Query(ctx, query, queryTime)
	if err != nil {
		log.Error(ctx, "GetMaxCPUTimeIn5MinWindow failed:"+query, zap.Any("queryTime", queryTime), zap.String("clusterId", clusterId), zap.Error(err))
		return nil, err
	}

	// Verify the result is not empty
	if result == nil {
		log.Error(ctx, "Query result is nil")
		return nil, errors.New("Query result is nil")
	}
	// log.PlainInfo("Query", zap.Any("result", result))

	// Extract instance and timestamp
	if vector, ok := result.(model.Vector); ok {
		for _, sample := range vector {
			instance := string(sample.Metric["instance"])
			instanceType := string(sample.Metric["instance_type"])
			timestamp := sample.Timestamp.Time()
			cpuTime := float64(sample.Value)
			// Convert to minutes
			cpuTimeMin := cpuTime / 1000 / 60
			cpuTimeMin = math.Round(cpuTimeMin*100) / 100
			resp := &GetMaxCPUTimeIn5MinWindowRespResp{
				Timestamp:    timestamp,
				Instance:     instance,
				InstanceType: instanceType,
				CpuTime:      cpuTimeMin,
				ClusterId:    clusterId,
			}
			log.Info(ctx, "prometheus MaxCPUTimeIn5MinWindow resp ", zap.String("query", query), zap.Any("queryTime", queryTime), zap.String("instance", instance), zap.Float64("cpuTimeMin", cpuTimeMin))

			return resp, nil
		}
	}
	return nil, err
}

/*
curl -G \
  'https://www.ds.us-east-1.aws.observability.tidbcloud.com/internal/metrics/bd692fb5-6454-464a-b40d-ca0d9f188c12/api/v1/query' \
   --data-urlencode 'query=tmax_over_time(sum_over_time(sum by (instance) (topsql_cpu_time_ms{sql_digest="5130A2E1FEA7F66A058AFBE47EA995720D35190AC6CBE9AB45EB747DA69FBC1B",instance="db-tikv-142.db-tikv-peer.tidb1379661944646416076.svc:20160"})[5m])[1d:1m])'\
   --data-urlencode 'time=1753920000' |jq -r

*/
// GetMax5MinWindowTime Get the maximum value of CPU usage accumulated in 5-minute cycles over the past day
func GetMax5MinWindowTime(clusterId string, sqlDigest string, instance string, queryTimeStr ...string) (*time.Time, error) {
	// In Prometheus, sqlDigest is uppercase
	sqlDigest = strings.ToUpper(sqlDigest)
	pc, err := CreatePrometheusClient(context.Background(), CreatePrometheusClientReq{ClusterID: clusterId})
	if err != nil {
		return nil, err
	}

	// Set query context with increased timeout
	ctx, cancel := context.WithTimeout(context.Background(), 40*time.Second)
	defer cancel()

	query := fmt.Sprintf(`tmax_over_time(sum_over_time(sum by (instance) (topsql_cpu_time_ms{sql_digest="%s",instance="%s"})[5m])[1d:1m])`, sqlDigest, instance)

	queryDayStart := GetDayQueryTime(queryTimeStr...)

	// Execute range query (corresponding to query_range in curl command)
	result, err := pc.Query(ctx, query, queryDayStart)
	if err != nil {
		log.Error(ctx, "QueryRange failed", zap.Error(err))
		return nil, err
	}

	// Verify the result is not empty
	if result == nil {
		log.Error(ctx, "Query result is nil")
		return nil, errors.New("Query result is nil")
	}

	// Extract instance and timestamp
	if vector, ok := result.(model.Vector); ok {
		if len(vector) == 0 {
			log.Info(ctx, "Query result is empty")
			return nil, nil
		}
		sample := vector[0]

		timestampNum := int64(sample.Value)
		// Convert to minutes
		t := time.Unix(timestampNum, 0)
		timestampStr := t.Format("2006-01-02 15:04:05")
		log.Info(ctx, "Max5MinWindowTime ", zap.String("query", query), zap.Any("queryDayStart", queryDayStart), zap.String("result timestampStr", timestampStr))
		// log.PlainInfo("Max 5 min window time", zap.String("instance", instance), zap.String("timestamp", timestampStr))
		return &t, nil
	}
	return nil, err
}

func GetMax5MinWindowTimePercent(clusterId string, queryTime time.Time, sqlDigest string, instance string) (float64, error) {

	sqlDigest = strings.ToUpper(sqlDigest)
	query := fmt.Sprintf(`sum_over_time(topsql_cpu_time_ms{sql_digest="%s",instance="%s"}[5m]) / sum(sum_over_time(topsql_cpu_time_ms{instance="%s"}[5m])) *100`, sqlDigest, instance, instance)
	return GetOneValueByTime(clusterId, queryTime, query)

}

//	curl -G \
//	   'https://www.ds.us-east-1.aws.observability.tidbcloud.com/internal/metrics/ed869d81-91c5-4a87-a28d-20795c738d3f/api/v1/query' \
//	   --data-urlencode 'query=sum(sum_over_time(topsql_cpu_time_ms{sql_digest="7D0F773E2A0D4428244D096C35471C2700925E1C3080A8B798BED0A29B47BA39",instance=~"db-tikv-1\\..*", instance_type="tikv"}[10m])) / sum(sum_over_time(topsql_cpu_time_ms{instance=~"db-tikv-1\\..*", instance_type="tikv"}[10m])) *100'  \
//	   --data-urlencode 'time=2025-12-11T05:16:11Z' |jq -r
func (pc *PrometheusClient) GetTikvCpuPercent(clusterId string, queryTime time.Time, sqlDigest string, instance string, interval string) (float64, error) {
	sqlDigest = strings.ToUpper(sqlDigest)
	query := fmt.Sprintf(`sum(sum_over_time(topsql_cpu_time_ms{sql_digest="%s",instance=~"%s", instance_type="tikv"}[%s])) / sum(sum_over_time(topsql_cpu_time_ms{instance=~"%s", instance_type="tikv"}[%s])) *100`, sqlDigest, instance, interval, instance, interval)
	return pc.GetOneValueByTime(clusterId, queryTime, query)

}

// GetMax5MinWindowTime Get the maximum value of CPU usage accumulated in 5-minute cycles over the past day
func GetAvg5MinWindowTime(clusterId string, sqlDigest string, queryTimeStr ...string) (*string, error) {
	// In Prometheus, sqlDigest is uppercase
	sqlDigest = strings.ToUpper(sqlDigest)
	pc, err := CreatePrometheusClient(context.Background(), CreatePrometheusClientReq{ClusterID: clusterId})
	if err != nil {
		return nil, err
	}

	// Set query context with increased timeout
	ctx, cancel := context.WithTimeout(context.Background(), 40*time.Second)
	defer cancel()

	query := fmt.Sprintf(`sum(sum_over_time(topsql_cpu_time_ms{instance_type="tikv",sql_digest="%s"}[1d])) / (24*60/5)`, sqlDigest)

	queryDayStart := GetDayQueryTime(queryTimeStr...)

	// Execute range query (corresponding to query_range in curl command)
	result, err := pc.Query(ctx, query, queryDayStart)
	if err != nil {
		log.Error(ctx, "QueryRange failed", zap.Error(err))
		return nil, err
	}

	// Verify the result is not empty
	if result == nil {
		log.Error(ctx, "Query result is nil")
		return nil, errors.New("Query result is nil")
	}

	// Extract instance and timestamp
	if vector, ok := result.(model.Vector); ok {
		sample := vector[0]

		timestampNum := int64(sample.Value)
		// Convert to minutes
		t := time.Unix(timestampNum, 0)
		timestampStr := t.Format("2006-01-02 15:04:05")
		log.Info(ctx, "Max5MinWindowTime ", zap.String("query", query), zap.Any("queryDayStart", queryDayStart), zap.String("result timestampStr", timestampStr))
		// log.PlainInfo("Max 5 min window time", zap.String("instance", instance), zap.String("timestamp", timestampStr))
		return &timestampStr, nil
	}
	return nil, err
}

func GetDayQueryTime(queryTimeStr ...string) time.Time {
	queryTime := config.AppConfig.Day
	if len(queryTimeStr) > 0 {
		queryTime = queryTimeStr[0]
	}
	// Parse the original date and add one day
	queryDayStart, err := getNextDayTime(queryTime)
	if err != nil {
		panic(err)
	}
	return queryDayStart
}

/*
Query the total CPU time used in the past day

	curl -G \
	'https://www.ds.us-east-1.aws.observability.tidbcloud.com/internal/metrics/bd692fb5-6454-464a-b40d-ca0d9f188c12/api/v1/query' \
	--data-urlencode 'query=sum(sum_over_time(topsql_cpu_time_ms{sql_digest="9D5A0F1A8C1C0E1FD5F0955DA80B305EAE920A2AB897319C6D2D8DE5D890BEDC"}[1d]))' \
	--data-urlencode 'time=1751299200'

	{
	  "status": "success",
	  "isPartial": false,
	  "data": {
	    "resultType": "vector",
	    "result": [
	      {
	        "metric": {},
	        "value": [1751299200, "245031"]
	      }
	    ]
	  },
	  "stats": {
	    "seriesFetched": "21",
	    "executionTimeMsec": 47
	  }
	}
*/
// report total cpu time used in the past day
func GetTotalCpuTimeByDigest(clusterId string, sqlDigest string, dayStr ...string) (*float64, error) {
	// In Prometheus, sqlDigest is uppercase
	sqlDigest = strings.ToUpper(sqlDigest)
	pc, err := CreatePrometheusClient(context.Background(), CreatePrometheusClientReq{ClusterID: clusterId})
	if err != nil {
		return nil, err
	}

	// Set query context with increased timeout
	ctx, cancel := context.WithTimeout(context.Background(), 40*time.Second)
	defer cancel()

	// Parse the original date and add one day
	queryTime := GetDayQueryTime(dayStr...)
	query := fmt.Sprintf(`sum(sum_over_time(topsql_cpu_time_ms{sql_digest="%s"}[1d]))`, sqlDigest)
	// query := `sum(sum_over_time(topsql_cpu_time_ms{}[1d]))`

	// Execute range query (corresponding to query_range in curl command)
	result, err := pc.Query(ctx, query, queryTime)
	if err != nil {
		log.Error(ctx, "QueryRange failed", zap.Error(err))
		return nil, err
	}

	// Verify the result is not empty
	if result == nil {
		log.Error(ctx, "Query result is nil")
		return nil, errors.New("Query result is nil")
	}

	// Extract instance and timestamp
	if vector, ok := result.(model.Vector); ok {
		for _, sample := range vector {
			// timestamp := sample.Timestamp.Time()
			totalcpuTime := float64(sample.Value)
			// // Convert to minutes
			// totalcpuTime = totalcpuTime / 1000 / 60
			// totalMin := math.Round(totalcpuTime*100) / 100

			// msg := fmt.Sprintf(" prometheus result, sqlDigest: %s, TotalCPUTime: %f min", sqlDigest, totalMin)
			// log.PlainInfo(msg)
			log.Info(ctx, "TotalCPUTime ", zap.String("query", query), zap.Any("queryTime", queryTime), zap.Float64("totalcpuTime（ms）", totalcpuTime))
			return &totalcpuTime, nil
		}
	}
	return nil, err
}
func GetTotalCpuTimeByDigest2(clusterId string, sqlDigest string, dayStr ...string) (*float64, error) {
	// In Prometheus, sqlDigest is uppercase
	sqlDigest = strings.ToUpper(sqlDigest)
	pc, err := CreatePrometheusClient(context.Background(), CreatePrometheusClientReq{ClusterID: clusterId})
	if err != nil {
		return nil, err
	}

	// Set query context with increased timeout
	ctx, cancel := context.WithTimeout(context.Background(), 40*time.Second)
	defer cancel()

	// Parse the original date and add one day
	queryTime := GetDayQueryTime(dayStr...)
	query := fmt.Sprintf(`sum(sum_over_time(topsql_cpu_time_ms:sum_over_time_5m:sum_sql_digest{sql_digest="%s"}[1d:5m]))`, sqlDigest)
	// query := `sum(sum_over_time(topsql_cpu_time_ms{}[1d]))`

	// Execute range query (corresponding to query_range in curl command)
	result, err := pc.Query(ctx, query, queryTime)
	if err != nil {
		log.Error(ctx, "QueryRange failed", zap.Error(err))
		return nil, err
	}

	// Verify the result is not empty
	if result == nil {
		log.Error(ctx, "Query result is nil")
		return nil, errors.New("Query result is nil")
	}

	// Extract instance and timestamp
	if vector, ok := result.(model.Vector); ok {
		for _, sample := range vector {
			// timestamp := sample.Timestamp.Time()
			totalcpuTime := float64(sample.Value)
			// Convert to minutes
			totalcpuTime = totalcpuTime / 1000 / 60
			totalMin := math.Round(totalcpuTime*100) / 100

			// msg := fmt.Sprintf(" prometheus result, sqlDigest: %s, TotalCPUTime: %f min", sqlDigest, totalMin)
			// log.PlainInfo(msg)
			log.Info(ctx, "TotalCPUTime ", zap.String("query", query), zap.Any("queryTime", queryTime), zap.Float64("totalMin", totalMin))
			return &totalMin, nil
		}
	}
	return nil, err
}

/*
// CPUTimeAnalysis Analysis result structure
type CPUTimeAnalysis struct {
	MaxValue    float64
	MaxInstance string
	MaxTime     time.Time
	AllResults  model.Value
}

 // analyzeMaxCPUTime Analyze query results to find the maximum value
func AnalyzeMaxCPUTime(result model.Value) *CPUTimeAnalysis {
	analysis := &CPUTimeAnalysis{
		MaxValue:   -1,
		AllResults: result,
	}

	if matrix, ok := result.(model.Matrix); ok {
		for _, sample := range matrix {
			instance := string(sample.Metric["instance"])
			for _, value := range sample.Values {
				// Convert string value to float64
				val, err := strconv.ParseFloat(value.Value.String(), 64)
				if err != nil {
					continue // Skip values that cannot be parsed
				}

				if val > analysis.MaxValue {
					analysis.MaxValue = val
					analysis.MaxInstance = instance
					analysis.MaxTime = value.Timestamp.Time()
				}
			}
		}
	}

	return analysis
}
*/
// getNextDayTime Parse the original date and return a time object with one day added
func getNextDayTime(dayStr string) (time.Time, error) {
	originalDate, err := time.ParseInLocation("20060102", dayStr, time.UTC)
	if err != nil {
		return time.Time{}, err
	}
	nextDay := originalDate.AddDate(0, 0, 1)
	return nextDay, nil
}

func GetMaxTotalCPUTimeFromClusterArr(clusterIdArr []string, sqlDigest string, queryDay ...string) (*GetMaxTotalCPUTimeFromClusterArrResp, error) {
	maxCPUTime := float64(0)
	maxClusterId := ""

	for _, clusterId := range clusterIdArr {
		clusterId = strings.TrimSpace(clusterId)
		// ms
		totalMin, err := GetTotalCpuTimeByDigest(clusterId, sqlDigest, queryDay...)
		if err != nil {
			log.PlainError("GetTotalCPUTime failed", zap.Error(err))
			return nil, err
		}

		if totalMin != nil && *totalMin > maxCPUTime {
			maxCPUTime = *totalMin
			maxClusterId = clusterId

		}

	}

	if len(clusterIdArr) > 0 && maxClusterId == "" {
		log.PlainInfo("GetMaxTotalCPUTimeFromClusterArr maxClusterId is empty, use first clusterId in clusterIdArr", zap.Any("clusterIdArr", clusterIdArr))
		maxClusterId = clusterIdArr[0]
	}
	//
	queryTime := GetDayQueryTime(queryDay...)
	interval := "1d"

	totalCpuTimeDay, err := GetAllSQLTotalCPUTime(maxClusterId, interval, queryTime, CpuTimeUnitMin)
	if err != nil {
		log.PlainError("GetAllSQLTotalCPUTime failed", zap.Error(err))
		return nil, err
	}

	//  ms Convert to minutes
	maxCPUTime = maxCPUTime / 1000 / 60
	maxCPUTime = math.Round(maxCPUTime*100) / 100

	resp := &GetMaxTotalCPUTimeFromClusterArrResp{
		MaxClusterId:    maxClusterId,
		MaxTotalCpuTime: maxCPUTime,
		// MaxTotalCpuTimePer: *utils.CalculatePercent(maxCPUTime, totalCpuTimeDay),
	}
	if maxCPUTime != 0 && totalCpuTimeDay != 0 {
		resp.MaxTotalCpuTimePer = *utils.CalculatePercent(maxCPUTime, totalCpuTimeDay)
	}

	return resp, nil
}

// @todo wlet
func GetMaxCPUTimeIn5MinWindowFromClusterArr(clusterIdArr []string, sqlDigest string, queryTimeStr ...string) (MaxCPUTimeIn5MinOfDay, error) {
	resp := MaxCPUTimeIn5MinOfDay{}
	maxCPUTimeResp := GetMaxCPUTimeIn5MinWindowRespResp{}

	for _, clusterId := range clusterIdArr {
		clusterId = strings.TrimSpace(clusterId)
		queryTime := GetDayQueryTime(queryTimeStr...)
		cpuTimeResp, err := GetMaxCPUTimeIn5MinWindow(clusterId, sqlDigest, "1d", queryTime)
		if err != nil {
			log.PlainError("GetMaxCPUTimeIn5MinWindow failed", zap.Error(err))
			return resp, err
		}

		if cpuTimeResp != nil && cpuTimeResp.CpuTime > maxCPUTimeResp.CpuTime {
			maxCPUTimeResp = *cpuTimeResp

		}

	}

	// log.PlainInfo("maxCPUTimeResp", zap.Any("maxCPUTimeResp", maxCPUTimeResp))
	if maxCPUTimeResp.CpuTime == 0 {
		return resp, nil
	}

	timePtr, err := GetMax5MinWindowTime(maxCPUTimeResp.ClusterId, sqlDigest, maxCPUTimeResp.Instance, queryTimeStr...)
	if err != nil {
		log.PlainError("GetMax5MinWindowTime failed", zap.Error(err))
		return resp, err
	}
	if timePtr == nil {
		return resp, nil
	}

	queryTime := (*timePtr).Add(150 * time.Second)

	cpuTimeIn5MinOfDayPer, err := GetMax5MinWindowTimePercent(maxCPUTimeResp.ClusterId, queryTime, sqlDigest, maxCPUTimeResp.Instance)
	if err != nil {
		return resp, err
	}
	cpuTimeIn5MinOfDayPer = math.Round(cpuTimeIn5MinOfDayPer*100) / 100

	resp.ClusterId = maxCPUTimeResp.ClusterId
	resp.Instance = maxCPUTimeResp.Instance
	resp.InstanceType = maxCPUTimeResp.InstanceType
	resp.MaxCPUTimeIn5Min = maxCPUTimeResp.CpuTime
	resp.Timestamp = (*timePtr).Format("2006-01-02 15:04:05")
	resp.CpuTimeIn5MinOfDayPer = cpuTimeIn5MinOfDayPer

	// log.PlainInfo("Max5MinWindowTime", zap.Any("Max5MinWindowTime", timeStr))

	return resp, nil
}

func GetTotalCountForDay(clusterId string, dayStr string) (float64, error) {
	pc, err := CreatePrometheusClient(context.Background(), CreatePrometheusClientReq{ClusterID: clusterId})
	if err != nil {
		return 0, err
	}

	startTime, err := time.ParseInLocation("20060102", dayStr, time.UTC)
	if err != nil {
		return 0, fmt.Errorf("failed to parse day string: %w", err)
	}

	var total float64
	// query := `count(count_over_time(topsql_cpu_time_ms[1h]))`
	query := `count(count_over_time(topsql_cpu_time_ms[1h]) by (sql_digest))`

	for i := 0; i < 24; i++ {
		queryTime := startTime.Add(time.Duration(i) * time.Hour)

		ctx, cancel := context.WithTimeout(context.Background(), 40*time.Second)

		result, err := pc.Query(ctx, query, queryTime)
		cancel()
		if err != nil {
			log.Error(ctx, "Query failed", zap.Time("queryTime", queryTime), zap.Error(err))
			continue // or return 0, err based on desired error handling
		}

		if result == nil {
			log.Warn(ctx, "Query result is nil", zap.Time("queryTime", queryTime))
			continue
		}

		if vector, ok := result.(model.Vector); ok {
			if len(vector) > 0 {
				value := float64(vector[0].Value)
				total += value
			}
		} else {
			log.Warn(ctx, "Query result is not a vector", zap.Time("queryTime", queryTime))
		}
	}

	return total, nil
}

func GetTotalDigestForDay(clusterId string, dayStr string) (float64, float64, error) {
	pc, err := CreatePrometheusClient(context.Background(), CreatePrometheusClientReq{ClusterID: clusterId})
	if err != nil {
		return 0, 0, err
	}

	startTime, err := time.ParseInLocation("20060102", dayStr, time.UTC)
	if err != nil {
		return 0, 0, fmt.Errorf("failed to parse day string: %w", err)
	}

	var totalDataCount float64
	var totalNoDigestCount float64
	digestSet := make(map[string]struct{})
	// query := `count by (sql_digest) (count_over_time(topsql_cpu_time_ms[1h]))`
	query := `count(count by (sql_digest) (topsql_cpu_time_ms[1h]))`

	for i := 0; i < 24; i++ {
		queryTime := startTime.Add(time.Duration(i) * time.Hour)

		ctx, cancel := context.WithTimeout(context.Background(), 40*time.Second)

		result, err := pc.Query(ctx, query, queryTime)
		cancel()
		if err != nil {
			log.Error(ctx, "Query failed", zap.Time("queryTime", queryTime), zap.Error(err))
			continue
		}

		if result == nil {
			log.Warn(ctx, "Query result is nil", zap.Time("queryTime", queryTime))
			continue
		}

		if vector, ok := result.(model.Vector); ok {
			for _, sample := range vector {
				// Only count metrics that have a sql_digest label.
				if sqlDigest, ok := sample.Metric["sql_digest"]; ok {
					digestSet[string(sqlDigest)] = struct{}{}
					value := float64(sample.Value)
					totalDataCount += value
				} else {
					totalNoDigestCount++
					log.Warn(ctx, "Query result metric does not have sql_digest label", zap.Any("sample", sample))
				}
			}
		} else {
			log.Warn(ctx, "Query result is not a vector", zap.Time("queryTime", queryTime))
		}
	}
	log.Info(context.Background(), "GetTotalDigestForDay", zap.Float64("totalDataCount", totalDataCount), zap.Float64("totalNoDigestCount", totalNoDigestCount), zap.Int("digestCount", len(digestSet)))
	return float64(len(digestSet)), totalDataCount, nil
}

func CheckCPU(clusterIds []string, dayStr string, digest string) (*CheckCpuResp, error) {
	resp := &CheckCpuResp{
		Digest: digest,
	}

	// 1、 CPUTimeIn5MinWindow
	cpuIn5MinOfDayResp, err := GetMaxCPUTimeIn5MinWindowFromClusterArr(clusterIds, digest, dayStr)
	if err != nil {
		log.PlainError("GetMaxCPUTimeIn5MinWindow failed", zap.Error(err))
		return nil, err
	}
	resp.MaxCpuTimeIn5MinOfDay = cpuIn5MinOfDayResp.MaxCPUTimeIn5Min
	resp.CpuTimeIn5MinOfDayPer = cpuIn5MinOfDayResp.CpuTimeIn5MinOfDayPer

	// 2、 TotalCPUTimeInDay
	totalCPUTimeResp, err := GetMaxTotalCPUTimeFromClusterArr(clusterIds, digest, dayStr)
	if err != nil {
		log.PlainError("GetMaxTotalCPUTimeFromClusterArr failed", zap.Error(err))
		return nil, err
	}

	// totalCpuTime, err := prometheus.GetAllSQLTotalCPUTime(clusterId, day)
	//  MaxValue > 16 min   or totalCPUTime > 4000 min
	if cpuIn5MinOfDayResp.MaxCPUTimeIn5Min > 16 || totalCPUTimeResp.MaxTotalCpuTime > 4000 {
		log.Info(context.Background(), "CheckCPU result hit parttern", zap.Any("maxCPUTimeIn5MinOfDay", cpuIn5MinOfDayResp), zap.Float64("totalCPUTime", totalCPUTimeResp.MaxTotalCpuTime))

	}
	resp.MaxTotalCpuTime = totalCPUTimeResp.MaxTotalCpuTime
	resp.MaxTotalCpuTimePer = totalCPUTimeResp.MaxTotalCpuTimePer
	log.PlainInfo("CheckCPU result not  hit parttern", zap.Any("maxCPUTimeIn5MinOfDay(min)", cpuIn5MinOfDayResp.MaxCPUTimeIn5Min), zap.Float64("totalCPUTime(min)", totalCPUTimeResp.MaxTotalCpuTime))
	log.PlainInfo("MaxInstance:" + cpuIn5MinOfDayResp.Instance)
	return resp, nil
}
