package clusteranomalydetection

import (
	"context"
	"fmt"
	"strings"

	"github.com/tidbcloud/devops/pkg/infra/log"

	"github.com/prometheus/common/model"
	"github.com/tidbcloud/devops/pkg/infra/clients/cliniccli"
)

type TopSQLDetectionReq struct {
	ClusterID string
	OrgID     string
	Duration  string
}

const RECENT_1H_STMT_PROMQL = "count_over_time(topsql_stmt_exec_count[1h]) > 0"
const RECENT_1H_TOP_SQL_PROMQL = "count_over_time(topsql_cpu_time_ms[1h]) >= 3000"

func (d *ClusterAnomalyDetection) GenerateLongUnfinishedSQLNotifyText(ctx context.Context, topSqlDetectionReq *TopSQLDetectionReq) (string, error) {
	// query top SQL metrics from clinic
	topSQLMetricsResp, err := d.cliniccli.QueryClusterMetrics(
		context.Background(),
		cliniccli.QueryClusterMetricsRequest{
			OrgID:     topSqlDetectionReq.OrgID,
			ClusterID: topSqlDetectionReq.ClusterID,
			PromQL:    RECENT_1H_TOP_SQL_PROMQL,
		},
	)
	if err != nil {
		return "", fmt.Errorf("query top SQL metrics failed: %w", err)
	}
	stmtMetricsResp, err := d.cliniccli.QueryClusterMetrics(
		context.Background(),
		cliniccli.QueryClusterMetricsRequest{
			OrgID:     topSqlDetectionReq.OrgID,
			ClusterID: topSqlDetectionReq.ClusterID,
			PromQL:    RECENT_1H_STMT_PROMQL,
		},
	)
	if err != nil {
		return "", fmt.Errorf("query stmt metrics failed: %w", err)
	}

	stmts := make(map[string]struct{})
	for _, result := range stmtMetricsResp.Data.Result {
		if len(result.Values) == 0 {
			continue
		}
		sqlDigestVal, ok := result.Metric["sql_digest"]
		if !ok {
			continue
		}
		stmts[string(sqlDigestVal)] = struct{}{}
	}

	sqlDigests := make(map[string]struct{})
	for _, result := range topSQLMetricsResp.Data.Result {
		if len(result.Values) == 0 {
			continue
		}
		sqlDigestVal, ok := result.Metric["sql_digest"]
		if !ok {
			continue
		}
		// always exist on top sql but not exist on finished executed stmts, then this query is not finished for 1h.
		if _, exist := stmts[string(sqlDigestVal)]; !exist {
			sqlDigests[string(sqlDigestVal)] = struct{}{}
		}
	}
	notifyTextList := make([]string, 0)
	for sqlDigest := range sqlDigests {
		sqlText, err := d.GetSQLTextByDigest(ctx, topSqlDetectionReq.ClusterID, topSqlDetectionReq.OrgID, sqlDigest)
		if err != nil {
			log.FromContext(ctx).Error(err, "get sql text by digest failed", "clusterID", topSqlDetectionReq.ClusterID, "sqlDigest", sqlDigest)
			continue
		}
		notifyTextList = append(notifyTextList, fmt.Sprintf("Long Unfinished SQL Text: %s", sqlText))

	}
	return strings.Join(notifyTextList, "\n"), nil
}

func (d *ClusterAnomalyDetection) GetSQLTextByDigest(ctx context.Context, clusterID, orgID, sqlDigest string) (string, error) {
	pql := fmt.Sprintf(`topk(1, topsql_sql_meta{cluster_id="%s",sql_digest="%s"}[2h])`, clusterID, sqlDigest)
	textResp, err := d.cliniccli.QueryClusterMetrics(
		context.Background(),
		cliniccli.QueryClusterMetricsRequest{
			OrgID:     orgID,
			ClusterID: clusterID,
			PromQL:    pql,
		},
	)
	if err != nil {
		return "", fmt.Errorf("query sql text by digest failed, clusterID: %s, sqlDigest: %s, error: %w", clusterID, sqlDigest, err)
	}
	result := textResp.Data.Result
	if len(result) == 0 {
		return "", fmt.Errorf("no result for query sql text by digest, clusterID: %s, sqlDigest: %s", clusterID, sqlDigest)
	}

	first := result[0]
	if val, ok := first.Metric[model.LabelName("normalized_sql")]; ok {
		return string(val), nil
	}

	for _, ss := range result {
		if val, ok := ss.Metric[model.LabelName("normalized_sql")]; ok {
			return string(val), nil
		}
	}
	return "", fmt.Errorf("no normalized_sql label found for query sql text by digest, clusterID: %s, sqlDigest: %s", clusterID, sqlDigest)
}
