package tidbautoscaling

import (
	"context"
	"fmt"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	. "github.com/onsi/gomega"
	serverlessv1alpha1 "github.com/tidbcloud/aws-shared-provider/apis/serverless/v1alpha1"
	"github.com/tidbcloud/aws-shared-provider/pkg/utils/query"
)

func TestTidbAutoscaling_query(t *testing.T) {
	tests := []struct {
		name        string
		promHandler http.HandlerFunc
		metrics     []serverlessv1alpha1.Metric
		want        podMetrics
	}{
		{
			name: "normal",
			promHandler: func(w http.ResponseWriter, r *http.Request) {
				if strings.HasPrefix(r.URL.Query().Get("query"), "query1") {
					fmt.Fprintln(w, `{"status":"success","data":{"resultType":"matrix","result":[`+
						`{"metric":{"pod":"pod1"},"values":[[1667460382,"0.3"]]},`+
						`{"metric":{"pod":"pod2"},"values":[[1667460382,"0.65"]]},`+
						`{"metric":{"pod":"pod3"},"values":[[1667460382,"0.95"]]}]}}`)
				} else {
					fmt.Fprintln(w, `{"status":"success","data":{"resultType":"matrix","result":[`+
						`{"metric":{"pod":"pod1"},"values":[[1667460382,"0.5"]]},`+
						`{"metric":{"pod":"pod2"},"values":[[1667460382,"0.75"]]},`+
						`{"metric":{"pod":"pod3"},"values":[[1667460382,"0.9"]]}]}}`)
				}
			},
			metrics: []serverlessv1alpha1.Metric{
				{PromSql: "query1", PodLabel: "pod"},
				{PromSql: "query2", PodLabel: "pod"},
			},
			want: podMetrics{
				"pod1": []float64OrNil{{value: 0.3, isValid: true}, {value: 0.5, isValid: true}},
				"pod2": []float64OrNil{{value: 0.65, isValid: true}, {value: 0.75, isValid: true}},
				"pod3": []float64OrNil{{value: 0.95, isValid: true}, {value: 0.9, isValid: true}},
			},
		},
		{
			name: "query2 no pod2 data",
			promHandler: func(w http.ResponseWriter, r *http.Request) {
				if strings.HasPrefix(r.URL.Query().Get("query"), "query1") {
					fmt.Fprintln(w, `{"status":"success","data":{"resultType":"matrix","result":[`+
						`{"metric":{"pod":"pod1"},"values":[[1667460382,"0.3"]]},`+
						`{"metric":{"pod":"pod2"},"values":[[1667460382,"0.65"]]},`+
						`{"metric":{"pod":"pod3"},"values":[[1667460382,"0.95"]]}]}}`)
				} else {
					fmt.Fprintln(w, `{"status":"success","data":{"resultType":"matrix","result":[`+
						`{"metric":{"pod":"pod1"},"values":[[1667460382,"0.5"]]},`+
						`{"metric":{"pod":"pod3"},"values":[[1667460382,"0.9"]]}]}}`)
				}
			},
			metrics: []serverlessv1alpha1.Metric{
				{PromSql: "query1", PodLabel: "pod"},
				{PromSql: "query2", PodLabel: "pod"},
			},
			want: podMetrics{
				"pod1": []float64OrNil{{value: 0.3, isValid: true}, {value: 0.5, isValid: true}},
				"pod2": []float64OrNil{{value: 0.65, isValid: true}, {}},
				"pod3": []float64OrNil{{value: 0.95, isValid: true}, {value: 0.9, isValid: true}},
			},
		},
		{
			name: "query2 pod2 values is empty",
			promHandler: func(w http.ResponseWriter, r *http.Request) {
				if strings.HasPrefix(r.URL.Query().Get("query"), "query1") {
					fmt.Fprintln(w, `{"status":"success","data":{"resultType":"matrix","result":[`+
						`{"metric":{"pod":"pod1"},"values":[[1667460382,"0.3"]]},`+
						`{"metric":{"pod":"pod2"},"values":[[1667460382,"0.65"]]},`+
						`{"metric":{"pod":"pod3"},"values":[[1667460382,"0.95"]]}]}}`)
				} else {
					fmt.Fprintln(w, `{"status":"success","data":{"resultType":"matrix","result":[`+
						`{"metric":{"pod":"pod1"},"values":[[1667460382,"0.5"]]},`+
						`{"metric":{"pod":"pod2"},"values":[]},`+
						`{"metric":{"pod":"pod3"},"values":[[1667460382,"0.9"]]}]}}`)
				}
			},
			metrics: []serverlessv1alpha1.Metric{
				{PromSql: "query1", PodLabel: "pod"},
				{PromSql: "query2", PodLabel: "pod"},
			},
			want: podMetrics{
				"pod1": []float64OrNil{{value: 0.3, isValid: true}, {value: 0.5, isValid: true}},
				"pod2": []float64OrNil{{value: 0.65, isValid: true}, {}},
				"pod3": []float64OrNil{{value: 0.95, isValid: true}, {value: 0.9, isValid: true}},
			},
		},
		{
			name: "query2 pod2 metric label is abnormal",
			promHandler: func(w http.ResponseWriter, r *http.Request) {
				if strings.HasPrefix(r.URL.Query().Get("query"), "query1") {
					fmt.Fprintln(w, `{"status":"success","data":{"resultType":"matrix","result":[`+
						`{"metric":{"pod":"pod1"},"values":[[1667460382,"0.3"]]},`+
						`{"metric":{"pod":"pod2"},"values":[[1667460382,"0.65"]]},`+
						`{"metric":{"pod":"pod3"},"values":[[1667460382,"0.95"]]}]}}`)
				} else {
					fmt.Fprintln(w, `{"status":"success","data":{"resultType":"matrix","result":[`+
						`{"metric":{"pod":"pod1"},"values":[[1667460382,"0.5"]]},`+
						`{"metric":{"podn":"pod2"},"values":[[1667460382,"0.75"]]},`+
						`{"metric":{"pod":"pod3"},"values":[[1667460382,"0.9"]]}]}}`)
				}
			},
			metrics: []serverlessv1alpha1.Metric{
				{PromSql: "query1", PodLabel: "pod"},
				{PromSql: "query2", PodLabel: "pod"},
			},
			want: podMetrics{
				"pod1": []float64OrNil{{value: 0.3, isValid: true}, {value: 0.5, isValid: true}},
				"pod2": []float64OrNil{{value: 0.65, isValid: true}, {}},
				"pod3": []float64OrNil{{value: 0.95, isValid: true}, {value: 0.9, isValid: true}},
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			g := NewGomegaWithT(t)
			promServer := httptest.NewServer(tt.promHandler)
			defer promServer.Close()

			ta := &TidbAutoscaling{
				ctx:         context.Background(),
				o11yQuerier: query.NewPromQueryer(nil, promServer.URL),
			}
			g.Expect(ta.query(tt.metrics)).To(Equal(tt.want))
		})
	}
}

func TestTidbAutoscaling_queryScaleDownCheck(t *testing.T) {
	tests := []struct {
		name                  string
		promHandler           http.HandlerFunc
		ScaleDownCheckMetrics []serverlessv1alpha1.CheckMetric
		want                  map[string]bool
	}{
		{
			name: "normal",
			promHandler: func(w http.ResponseWriter, r *http.Request) {
				if strings.HasPrefix(r.URL.Query().Get("query"), "sql1") {
					fmt.Fprintln(w, `{"status":"success","data":{"resultType":"matrix","result":[{"metric":{"token":"token1"},"values":[[1667460382,"1"]]}]}}`)
				} else {
					fmt.Fprintln(w, `{"status":"success","data":{"resultType":"matrix","result":[{"metric":{"keyspace_token":"token2"},"values":[[1667460382,"1"]]}]}}`)
				}
			},
			ScaleDownCheckMetrics: []serverlessv1alpha1.CheckMetric{
				{PromSql: "sql1", TokenLabel: "token"},
				{PromSql: "sql2", TokenLabel: "keyspace_token"},
			},
			want: map[string]bool{"token1": true, "token2": true},
		},
		{
			name: "no token",
			promHandler: func(w http.ResponseWriter, r *http.Request) {
				fmt.Fprintln(w, `{"status":"success","data":{"resultType":"matrix","result":[]}}`)
			},
			ScaleDownCheckMetrics: []serverlessv1alpha1.CheckMetric{
				{PromSql: "sql1", TokenLabel: "token"},
				{PromSql: "sql2", TokenLabel: "token"},
			},
			want: map[string]bool{},
		},
		{
			name: "no promSqls",
			want: map[string]bool{},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			g := NewGomegaWithT(t)
			promServer := httptest.NewServer(tt.promHandler)
			defer promServer.Close()

			ta := &TidbAutoscaling{
				ctx:         context.Background(),
				o11yQuerier: query.NewPromQueryer(nil, promServer.URL),
			}
			g.Expect(ta.queryScaleDownCheck(tt.ScaleDownCheckMetrics)).To(Equal(tt.want))
		})
	}
}
