package prometheus

import (
	"context"
	"fmt"
	"strings"
	"time"

	"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"
)

// GetAvgLatency calculates the average execution time (milliseconds) for a specific SQL Digest over the entire given date.
// The logic corresponds to the following PromQL:
// (sum(sum_over_time(topsql_stmt_duration_sum_ns{sql_digest="<digest>"}[1d]))/1e6) /
// sum(sum_over_time(topsql_stmt_duration_count{sql_digest="<digest>"}[1d]))
// The query time is set to 00:00:00Z of the next day of dayStr, so that [1d] covers the entire day of dayStr.
// func GetAvgLatency(clusterId string, dayStr string, sqlDigest string) (float64, error) {
// 	query := fmt.Sprintf(`(sum(sum_over_time(topsql_stmt_duration_sum_ns{sql_digest="%s"}[1d]))/1e6)/sum(sum_over_time(topsql_stmt_duration_count{sql_digest="%s"}[1d]))`, sqlDigest, sqlDigest)
// 	return GetOneValueByDay(clusterId, dayStr, query)

// }

// GetAvgLatency calculates the average execution time (milliseconds) for a specific SQL Digest over the entire given date.
// The logic corresponds to the following PromQL:
// (sum(sum_over_time(topsql_stmt_duration_sum_ns{instance_type="tidb",sql_digest="%s"}[1d]))/1e6) /
// sum(sum_over_time(topsql_stmt_duration_count{instance_type="tidb",sql_digest="%s"}[1d]))
// The query time is set to 00:00:00Z of the next day of dayStr, so that [1d] covers the entire day of dayStr.
func (pc *PrometheusClient) GetAvgLatency(clusterId string, queryTime time.Time, interval string, sqlDigest string) (float64, error) {
	// sqlDigest = strings.ToUpper(sqlDigest)
	query := fmt.Sprintf(`(sum(sum_over_time(topsql_stmt_duration_sum_ns{instance_type="tidb",sql_digest="%s"}[%s]))/1e6)/sum(sum_over_time(topsql_stmt_duration_count{instance_type="tidb",sql_digest="%s"}[%s]))`, sqlDigest, interval, sqlDigest, interval)
	return pc.GetOneValueByTime(clusterId, queryTime, query)

}

func GetQps(clusterId string, dayStr string, sqlDigest string) (float64, error) {
	sqlDigest = strings.ToUpper(sqlDigest)
	query := fmt.Sprintf(`sum(sum_over_time(topsql_stmt_exec_count{sql_digest="%s"}[1d]))/(24*60*60)`, sqlDigest)
	return GetOneValueByDay(clusterId, dayStr, query)

}
func GetTotalCpu(clusterId string, dayStr string, sqlDigest string) (float64, error) {
	sqlDigest = strings.ToUpper(sqlDigest)
	query := fmt.Sprintf(`sum(sum_over_time(topsql_cpu_time_ms{instance_type="tidb",sql_digest="%s"}[1d]))`, sqlDigest)
	return GetOneValueByDay(clusterId, dayStr, query)

}

// GetTotalCpuByTime calculates the total CPU time (minute) for a specific SQL Digest over a given time interval.
// The logic corresponds to the following PromQL:
// sum(sum_over_time(topsql_cpu_time_ms{instance_type="tidb",sql_digest="%s"}[%s]))/(1000*60)
func (pc *PrometheusClient) GetTotalCpuByTime(clusterId string, queryTime time.Time, interval string, sqlDigest string, instanceType string) (float64, error) {
	sqlDigest = strings.ToUpper(sqlDigest)
	query := fmt.Sprintf(`sum(sum_over_time(topsql_cpu_time_ms{instance_type="%s",sql_digest="%s"}[%s]))/(1000*60)`, instanceType, sqlDigest, interval)
	cpuTime, err := pc.GetOneValueByTime(clusterId, queryTime, query)
	if err != nil {
		return 0, err
	}
	cpuTime = utils.RoundFloat(cpuTime) // minute
	return cpuTime, nil

}

// unit: minute
func GetDigestsCpu(clusterId string, queryTime time.Time, interval string, sqlDigests []string, instanceType string) (map[string]TopSqlDigest, error) {

	digestsRegex := buildDigestRegex(sqlDigests)
	query := fmt.Sprintf(`sum by (sql_digest)(sum_over_time( topsql_cpu_time_ms{instance_type="%s",sql_digest=~"%s"}[%s])) / (1000*60)`, instanceType, digestsRegex, interval)
	digestArray, err := GetDigestArray(clusterId, queryTime, query)
	if err != nil {
		return nil, err
	}

	digestMap := make(map[string]TopSqlDigest, len(digestArray))
	for _, v := range digestArray {
		digestMap[v.Digest] = TopSqlDigest{
			Digest: v.Digest,
			Value:  utils.RoundFloat(v.Value, 4),
		}
	}

	log.PlainInfo("GetDigestTotalCpu", zap.String("query", query), zap.Any("digestArray", digestArray))
	return digestMap, nil

}
func (pc *PrometheusClient) GetDigestsCpu(clusterId string, queryTime time.Time, interval string, sqlDigests []string, instanceType string) (map[string]TopSqlDigest, error) {

	digestsRegex := buildDigestRegex(sqlDigests)
	query := fmt.Sprintf(`sum by (sql_digest)(sum_over_time( topsql_cpu_time_ms{instance_type="%s",sql_digest=~"%s"}[%s])) / (1000*60)`, instanceType, digestsRegex, interval)
	digestArray, err := pc.GetDigestArray(clusterId, queryTime, query)
	if err != nil {
		return nil, err
	}

	digestMap := make(map[string]TopSqlDigest, len(digestArray))
	for _, v := range digestArray {
		digestMap[v.Digest] = TopSqlDigest{
			Digest: v.Digest,
			Value:  utils.RoundFloat(v.Value, 4),
		}
	}

	log.PlainInfo("GetDigestTotalCpu", zap.String("query", query), zap.Any("digestArray", digestArray))
	return digestMap, nil

}

// unit: milliseconds
func GetDigestsAvgLatency(clusterId string, queryTime time.Time, interval string, sqlDigests []string) (map[string]TopSqlDigest, error) {

	digestsRegex := buildDigestRegex(sqlDigests)

	// query := fmt.Sprintf(`(sum(sum_over_time(topsql_stmt_duration_sum_ns{instance_type="tidb",sql_digest="%s"}[%s]))/1e6)/sum(sum_over_time(topsql_stmt_duration_count{instance_type="tidb",sql_digest="%s"}[%s]))`, sqlDigest, interval, sqlDigest, interval)

	query := fmt.Sprintf(`(sum by (sql_digest)(sum_over_time(topsql_stmt_duration_sum_ns{instance_type="tidb",sql_digest=~"%s"}[%s]))/1e6)/sum by (sql_digest)(sum_over_time(topsql_stmt_duration_count{instance_type="tidb",sql_digest=~"%s"}[%s]))
`, digestsRegex, interval, digestsRegex, interval)

	digestArray, err := GetDigestArray(clusterId, queryTime, query)
	if err != nil {
		return nil, err
	}
	digestMap := make(map[string]TopSqlDigest, len(digestArray))
	for _, v := range digestArray {
		digestMap[v.Digest] = TopSqlDigest{
			Digest: v.Digest,
			Value:  utils.RoundFloat(v.Value, 4),
		}
	}
	log.PlainInfo("GetDigestsAvgLatency", zap.String("query", query), zap.Any("digestArray", digestArray))
	return digestMap, nil

}

// unit: milliseconds
func (pc *PrometheusClient) GetDigestsAvgLatency(clusterId string, queryTime time.Time, interval string, sqlDigests []string) (map[string]TopSqlDigest, error) {

	digestsRegex := buildDigestRegex(sqlDigests)

	// query := fmt.Sprintf(`(sum(sum_over_time(topsql_stmt_duration_sum_ns{instance_type="tidb",sql_digest="%s"}[%s]))/1e6)/sum(sum_over_time(topsql_stmt_duration_count{instance_type="tidb",sql_digest="%s"}[%s]))`, sqlDigest, interval, sqlDigest, interval)

	// numerator: sum_over_time of duration_sum_ns (ms)
	numeratorQuery := fmt.Sprintf(`sum by (sql_digest)(sum_over_time(topsql_stmt_duration_sum_ns{instance_type="tidb",sql_digest=~"%s"}[%s]))/1e6`, digestsRegex, interval)
	// denominator: sum_over_time of duration_count
	denominatorQuery := fmt.Sprintf(`sum by (sql_digest)(sum_over_time(topsql_stmt_duration_count{instance_type="tidb",sql_digest=~"%s"}[%s]))`, digestsRegex, interval)

	numeratorArr, err := pc.GetDigestArray(clusterId, queryTime, numeratorQuery)
	if err != nil {
		return nil, err
	}
	denominatorArr, err := pc.GetDigestArray(clusterId, queryTime, denominatorQuery)
	if err != nil {
		return nil, err
	}
	denMap := make(map[string]float64, len(denominatorArr))
	for _, v := range denominatorArr {
		denMap[v.Digest] = v.Value
	}
	out := make(map[string]TopSqlDigest, len(numeratorArr))
	for _, num := range numeratorArr {
		den := denMap[num.Digest]
		val := 0.0
		if den > 0 {
			val = utils.RoundFloat(num.Value/den, 4)
		}
		out[num.Digest] = TopSqlDigest{Digest: num.Digest, Value: val}
	}
	log.PlainInfo("GetDigestsAvgLatency", zap.String("numeratorQuery", numeratorQuery), zap.String("denominatorQuery", denominatorQuery), zap.Any("numeratorArr", numeratorArr), zap.Any("denominatorArr", denominatorArr))
	return out, nil

}

// unit: milliseconds
func (pc *PrometheusClient) GetDigestsAvgLatency_back(clusterId string, queryTime time.Time, interval string, sqlDigests []string) (map[string]TopSqlDigest, error) {

	digestsRegex := buildDigestRegex(sqlDigests)

	// query := fmt.Sprintf(`(sum(sum_over_time(topsql_stmt_duration_sum_ns{instance_type="tidb",sql_digest="%s"}[%s]))/1e6)/sum(sum_over_time(topsql_stmt_duration_count{instance_type="tidb",sql_digest="%s"}[%s]))`, sqlDigest, interval, sqlDigest, interval)

	query := fmt.Sprintf(`(sum by (sql_digest)(sum_over_time(topsql_stmt_duration_sum_ns{instance_type="tidb",sql_digest=~"%s"}[%s]))/1e6)/sum by (sql_digest)(sum_over_time(topsql_stmt_duration_count{instance_type="tidb",sql_digest=~"%s"}[%s]))
`, digestsRegex, interval, digestsRegex, interval)

	digestArray, err := pc.GetDigestArray(clusterId, queryTime, query)
	if err != nil {
		return nil, err
	}
	digestMap := make(map[string]TopSqlDigest, len(digestArray))
	for _, v := range digestArray {
		digestMap[v.Digest] = TopSqlDigest{
			Digest: v.Digest,
			Value:  utils.RoundFloat(v.Value, 4),
		}
	}
	log.PlainInfo("GetDigestsAvgLatency", zap.String("query", query), zap.Any("digestArray", digestArray))
	return digestMap, nil

}

//    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=max by (sql_digest, normalized_sql) (topsql_sql_meta{cluster_id="1379661944646416027",sql_digest=~"1806064037961470F9E325D5AC7322437501C18A61816E551814E673AC97BEE9|6CFE2342F13988FEC332EB0C73390684258BC02E2937E5B98D3A813EC833D5F2"})' \
//   --data-urlencode 'time=2025-12-09T02:00:00Z' |jq -r

func (pc *PrometheusClient) GetDigestsSql(clusterId string, queryTime time.Time, sqlDigests []string) (map[string]TopSqlDigestSql, error) {

	digestsRegex := buildDigestRegex(sqlDigests)

	query := fmt.Sprintf(`max by (sql_digest, normalized_sql) (topsql_sql_meta{cluster_id="%s",sql_digest=~"%s"})
`, clusterId, digestsRegex)

	digestMap := make(map[string]TopSqlDigestSql, 0)
	// pc, err := CreatePrometheusClient(context.Background(), CreatePrometheusClientReq{ClusterID: clusterId})
	// if err != nil {
	// 	return digestMap, err
	// }
	ctx, cancel := context.WithTimeout(context.Background(), 40*time.Second)
	defer cancel()

	log.PlainInfo("Querying",
		zap.String("query", query),
		zap.String("date", queryTime.Format(time.RFC3339)),
		zap.Any("queryTime", queryTime))

	result, err := pc.Query(ctx, query, queryTime)
	if err != nil {
		log.PlainError("Query failed", zap.Time("queryTime", queryTime), zap.Error(err))
		return digestMap, err
	}
	if result == nil {
		log.PlainWarn("Query result is nil", zap.Time("queryTime", queryTime))
		return digestMap, nil
	}

	if vector, ok := result.(model.Vector); ok {
		for _, sample := range vector {
			// Get sql_digest from Metric labels
			digest := string(sample.Metric[model.LabelName("sql_digest")])
			sql := string(sample.Metric[model.LabelName("normalized_sql")])

			// Accumulate in map
			digestMap[digest] = TopSqlDigestSql{
				Digest: digest,
				Sql:    sql,
			}
		}
	} else {
		log.PlainWarn("Query result is not a vector", zap.Time("queryTime", queryTime))
	}

	log.PlainInfo("GetDigestsSql", zap.String("query", query), zap.Any("digestMap", digestMap))
	return digestMap, nil

}

func buildDigestRegex(sqlDigests []string) string {
	var queryDigests []string
	for _, v := range sqlDigests {
		queryDigests = append(queryDigests, strings.ToUpper(v))
	}

	regex := strings.Join(queryDigests, "|")
	return regex
}

func GetMaxCpu(clusterId string, dayStr string, sqlDigest string) (float64, error) {
	query := fmt.Sprintf(`max(max_over_time(topsql_cpu_time_ms{sql_digest="%s"}[1d]))`, sqlDigest)
	return GetOneValueByDay(clusterId, dayStr, query)

}

func GetAvgCpu(clusterId string, dayStr string, sqlDigest string) (float64, error) {
	query := fmt.Sprintf(`avg(avg_over_time(topsql_cpu_time_ms{sql_digest="%s"}[1d]))`, sqlDigest)
	return GetOneValueByDay(clusterId, dayStr, query)

}

func GetTikvAvgCpuIn5Min(clusterId string, dayStr string, sqlDigest string) (float64, error) {
	query := fmt.Sprintf(`sum(sum_over_time(topsql_cpu_time_ms{instance_type="tikv",sql_digest="%s"}[1d])) / (24*60/5)`, sqlDigest)
	return GetOneValueByDay(clusterId, dayStr, query)

}
func GetTiDBAvgCpuIn5Min(clusterId string, dayStr string, sqlDigest string) (float64, error) {
	query := fmt.Sprintf(`sum(sum_over_time(topsql_cpu_time_ms{instance_type="tidb",sql_digest="%s"}[1d])) / (24*60/5)`, sqlDigest)
	return GetOneValueByDay(clusterId, dayStr, query)

}

// func getQueryTime(dayStr string) (time.Time, error) {
// 	startTime, err := time.ParseInLocation("20060102", dayStr, time.UTC)
// 	if err != nil {
// 		return time.Time{},  err
// 	}
// 	queryTime := startTime.Add(24 * time.Hour)
// 	return queryTime, nil
// }
