package prometheus

import (
	"fmt"
	"math"
	"time"

	"github.com/pingcap-inc/clinic/clinic_data_service/pkg/dto"
	"github.com/pingcap-inc/clinic/clinic_data_service/pkg/utils"
)

/*
curl -G \
  'https://www.ds.us-east-1.aws.observability.tidbcloud.com/internal/metrics/bd692fb5-6454-464a-b40d-ca0d9f188c12/api/v1/query_range' \
  --data-urlencode 'query=sum(rate(process_cpu_seconds_total{tidb_cluster_id="1379661944646416076", instance=~"db-tikv-1", component="tikv"}[2m])) by (instance)' \
  --data-urlencode 'start=2025-09-05T04:00:00Z' \
  --data-urlencode 'end=2025-09-05T05:00:00Z' \
  --data-urlencode 'step=120s' | jq -r
*/

func (pc *PrometheusClient) getTikvMaxCpuByRange(clusterId, instance string, startTime, endTime time.Time) (*float64, error) {
	// query := fmt.Sprintf(`histogram_quantile(0.99, sum(rate(tikv_grpc_msg_duration_seconds_bucket{tidb_cluster_id="%s",instance=~"db-tikv-.*",type!="kv_gc", type ="coprocessor"}[2m])) by (le, instance))`, clusterId)
	query := fmt.Sprintf(`sum(rate(process_cpu_seconds_total{tidb_cluster_id="%s", instance=~"%s", component="tikv"}[2m])) by (instance)`, clusterId, instance)
	step := 2 * time.Minute

	matrix, err := pc.QueryRangeMatrix(clusterId, query, startTime, endTime, step)
	if err != nil {
		return nil, err
	}

	var maxCpu float64

	for _, sampleStream := range matrix {
		// instance := string(sampleStream.Metric["instance"])
		for _, samplePair := range sampleStream.Values {
			cpuVal := float64(samplePair.Value)

			if cpuVal > maxCpu {
				maxCpu = cpuVal
			}
		}
	}
	if maxCpu == 0 {
		return nil, nil
	}
	maxCpu = math.Round(maxCpu*100) / 100

	return &maxCpu, nil
}

func (pc *PrometheusClient) setTikvMaxCpuAndDiff(clusterId, instance string, latencyWindow *LatencyWindow) error {
	startTime := latencyWindow.LatencyWindow.Start
	endTime := latencyWindow.LatencyWindow.End
	maxCpu, err := pc.getTikvMaxCpuByRange(clusterId, instance, startTime, endTime)
	if err != nil {
		return err
	}

	intervalDuration := endTime.Sub(startTime)

	previousStartTime := startTime.Add(-intervalDuration)
	previousEndTime := startTime

	previousMaxCpu, err := pc.getTikvMaxCpuByRange(clusterId, instance, previousStartTime, previousEndTime)
	if err != nil {
		return err
	}

	latencyWindow.InstanceMaxCpu = *maxCpu
	latencyWindow.InstanceMaxCpuDiff = *utils.CalculateDiff(float64(*maxCpu), float64(*previousMaxCpu))

	return nil
}

func (pc *PrometheusClient) GetHighCpuByRange(clusterId string, startTime, endTime time.Time, threshold float64) (map[string][]dto.SamplePair, error) {
	query := fmt.Sprintf(`sum(rate(process_cpu_seconds_total{tidb_cluster_id="%s",instance=~"db-tikv-.*",component="tikv"}[2m])) by (instance)`, clusterId)
	step := 2 * time.Minute
	matrix, err := pc.QueryRangeMatrix(clusterId, query, startTime, endTime, step)
	if err != nil {
		return nil, err
	}
	highCpuInstances := make(map[string][]dto.SamplePair)
	for _, sampleStream := range matrix {
		instance := string(sampleStream.Metric["instance"])
		for _, sp := range sampleStream.Values {
			cpuVal := float64(sp.Value)
			if cpuVal > threshold {
				highCpuInstances[instance] = append(highCpuInstances[instance], dto.SamplePair{Timestamp: sp.Timestamp.Time().UTC(), Value: cpuVal})
			}
		}
	}
	return highCpuInstances, nil
}

// GetHighCpuUtilByRange queries TiKV instance CPU utilization percentage over a time range.
// Utilization formula (per PDF spec):
//
//	util_i(t) = (sum(rate(process_cpu_seconds_total{component="tikv"}[2m])) by (instance)
//	            / (sum(tikv_server_cpu_cores_quota{component=~".*tikv"}) by (instance) > 0)) * 100
//
// Returns:
//
//	map[instance][]dto.SamplePair where SamplePair.Value is utilization (%) at SamplePair.Timestamp (UTC).
//
// Notes:
//   - step is fixed at 2m to align with rate window
//   - values are raw percentages; downstream filters decide thresholds (absolute / relative)
func (pc *PrometheusClient) GetHighCpuUtilByRange(clusterId string, startTime, endTime time.Time) (map[string][]dto.SamplePair, error) {
	query := fmt.Sprintf(`(sum(rate(process_cpu_seconds_total{tidb_cluster_id="%s",component="tikv"}[2m])) by (instance) / (sum(tikv_server_cpu_cores_quota{tidb_cluster_id="%s",component=~".*tikv"} ) by (instance) > 0)) * 100`, clusterId, clusterId)
	step := 2 * time.Minute
	matrix, err := pc.QueryRangeMatrix(clusterId, query, startTime, endTime, step)
	if err != nil {
		return nil, err
	}
	utilMap := make(map[string][]dto.SamplePair)
	for _, sampleStream := range matrix {
		instance := string(sampleStream.Metric["instance"])
		for _, sp := range sampleStream.Values {
			val := float64(sp.Value)
			utilMap[instance] = append(utilMap[instance], dto.SamplePair{Timestamp: sp.Timestamp.Time().UTC(), Value: val})
		}
	}
	return utilMap, nil
}
