package regionalapi

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
	"time"

	corev1 "k8s.io/api/core/v1"

	v1 "github.com/tidbcloud/observability/api/regionalserver/resourceworker/v1"
	"k8s.io/apimachinery/pkg/api/resource"

	prometheusv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
	"github.com/tidbcloud/observability/pkg/common/configs"
	"github.com/tidbcloud/observability/pkg/common/metrics"
	"github.com/tidbcloud/observability/pkg/common/utils"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"

	"github.com/tidbcloud/observability/pkg/common/structs"

	"github.com/tidbcloud/observability/pkg/common/message"

	"github.com/tidbcloud/observability/pkg/common/errors"
	"github.com/tidbcloud/observability/pkg/resourcemanager"
	"github.com/tidbcloud/observability/test/mockresourcemanager"
	"go.uber.org/mock/gomock"

	"github.com/tidbcloud/observability/pkg/common/constants"

	"github.com/stretchr/testify/assert"
)

func TestCreateCluster(t *testing.T) {
	body, _ := json.Marshal(message.CreateClusterReq{
		ClusterBaseInfo: structs.ClusterBaseInfo{
			CommonReqInfo: structs.CommonReqInfo{
				Vendor: "aws",
				Region: "us-east-2",
			},
			TenantID:      "1",
			ProjectID:     "1",
			ApplicationID: "1",
			BizType:       "tidbcloud",
			ShareMode:     constants.SHARED,
			ID:            "aaa",
		},
	})
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

	clusterRW := mockresourcemanager.NewMockClusterReaderWriter(ctrl)
	resourcemanager.MockClusterReaderWriter(clusterRW)
	clusterRW.EXPECT().IsClusterIDExists(gomock.Any(), gomock.Any()).Return(false, errors.New(errors.ParameterInvalid, "")).AnyTimes()
	w := httptest.NewRecorder()

	req, _ := http.NewRequest("POST", "/api/v1/clusters/", strings.NewReader(string(body)))

	req.Header.Set(constants.AuthorizationHeader, fmt.Sprintf("%s %s", constants.BearerPrefix, stsToken))
	testEngine.ServeHTTP(w, req)

	assert.Equal(t, http.StatusBadRequest, w.Code)
}

func TestDetailCluster(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

	clusterRW := mockresourcemanager.NewMockClusterReaderWriter(ctrl)
	resourcemanager.MockClusterReaderWriter(clusterRW)
	clusterRW.EXPECT().GetCluster(gomock.Any(), gomock.Any()).Return(nil, errors.New(errors.ParameterInvalid, "")).Times(1)
	w := httptest.NewRecorder()

	req, _ := http.NewRequest("GET", "/api/v1/clusters/aaa?vendor=aws&region=us-west-2", nil)
	req.Header.Set(constants.AuthorizationHeader, fmt.Sprintf("%s %s", constants.BearerPrefix, stsToken))
	testEngine.ServeHTTP(w, req)

	assert.Equal(t, http.StatusBadRequest, w.Code)
}

func TestDeleteCluster(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

	clusterRW := mockresourcemanager.NewMockClusterReaderWriter(ctrl)
	resourcemanager.MockClusterReaderWriter(clusterRW)
	clusterRW.EXPECT().GetCluster(gomock.Any(), gomock.Any()).Return(nil, errors.New(errors.ParameterInvalid, "")).Times(1)
	w := httptest.NewRecorder()

	req, _ := http.NewRequest("DELETE", "/api/v1/clusters/aaa?vendor=aws&region=us-west-2", nil)
	req.Header.Set(constants.AuthorizationHeader, fmt.Sprintf("%s %s", constants.BearerPrefix, stsToken))
	testEngine.ServeHTTP(w, req)

	assert.Equal(t, http.StatusBadRequest, w.Code)
}

func TestPauseCluster(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

	clusterRW := mockresourcemanager.NewMockClusterReaderWriter(ctrl)
	resourcemanager.MockClusterReaderWriter(clusterRW)
	clusterRW.EXPECT().GetCluster(gomock.Any(), gomock.Any()).Return(nil, errors.New(errors.ParameterInvalid, "")).Times(1)
	w := httptest.NewRecorder()

	req, _ := http.NewRequest("PUT", "/api/v1/clusters/aaa/pause?vendor=aws&region=us-west-2", nil)
	req.Header.Set(constants.AuthorizationHeader, fmt.Sprintf("%s %s", constants.BearerPrefix, stsToken))
	testEngine.ServeHTTP(w, req)

	assert.Equal(t, http.StatusBadRequest, w.Code)
}

func TestResumeCluster(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

	clusterRW := mockresourcemanager.NewMockClusterReaderWriter(ctrl)
	resourcemanager.MockClusterReaderWriter(clusterRW)
	clusterRW.EXPECT().GetCluster(gomock.Any(), gomock.Any()).Return(nil, errors.New(errors.ParameterInvalid, "")).Times(1)
	w := httptest.NewRecorder()

	req, _ := http.NewRequest("PUT", "/api/v1/clusters/aaa/resume?vendor=aws&region=us-west-2", nil)
	req.Header.Set(constants.AuthorizationHeader, fmt.Sprintf("%s %s", constants.BearerPrefix, stsToken))
	testEngine.ServeHTTP(w, req)

	assert.Equal(t, http.StatusBadRequest, w.Code)
}

func TestQueryClusters(t *testing.T) {
	w := httptest.NewRecorder()

	req, _ := http.NewRequest("GET", "/api/v1/clusters/?vendor=aws&region=us-west-2&showDeleted=true", nil)
	req.Header.Set(constants.AuthorizationHeader, fmt.Sprintf("%s %s", constants.BearerPrefix, stsToken))
	testEngine.ServeHTTP(w, req)

	assert.Equal(t, http.StatusBadRequest, w.Code)
}

func TestModifyMonitorSpec(t *testing.T) {
	body, _ := json.Marshal(message.ModifyMonitorSpecReq{
		CommonReqInfo: structs.CommonReqInfo{
			Vendor: "aws",
			Region: "us-east-2",
		},
		SpecID: "aaa",
	})
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

	clusterRW := mockresourcemanager.NewMockClusterReaderWriter(ctrl)
	resourcemanager.MockClusterReaderWriter(clusterRW)
	clusterRW.EXPECT().GetCluster(gomock.Any(), gomock.Any()).Return(nil, errors.New(errors.ParameterInvalid, "")).Times(1)
	w := httptest.NewRecorder()

	req, _ := http.NewRequest("PUT", "/api/v1/clusters/aaa/monitor-spec/", strings.NewReader(string(body)))
	req.Header.Set(constants.AuthorizationHeader, fmt.Sprintf("%s %s", constants.BearerPrefix, stsToken))
	testEngine.ServeHTTP(w, req)

	assert.Equal(t, http.StatusBadRequest, w.Code)
}

func mockPrometheusRuleSpec(name string) prometheusv1.PrometheusRuleSpec {
	return prometheusv1.PrometheusRuleSpec{
		Groups: []prometheusv1.RuleGroup{
			0: {
				Name:     name,
				Interval: "5",
				Rules: []prometheusv1.Rule{
					{
						Alert: "HighRequestLatency",
						Expr:  intstr.FromString("request_latency_seconds:mean5m{job=\"myjob\"} > 0.5"),
						For:   "10m",
						Labels: map[string]string{
							"severity": "critical",
						},
						Annotations: map[string]string{
							"summary": "High request latency",
						},
					},
				},
			},
		},
	}
}
func mockPrometheusRule(id string, tenantID, projectID string) *prometheusv1.PrometheusRule {
	return &prometheusv1.PrometheusRule{
		ObjectMeta: metav1.ObjectMeta{
			Name:      id,
			Namespace: "default-" + tenantID,
			Labels: map[string]string{
				constants.LabelClusterID:     id,
				constants.LabelVendor:        "aws",
				constants.LabelRegion:        "us-east-2",
				constants.LabelTenantID:      tenantID,
				constants.LabelProjectID:     projectID,
				constants.LabelApplicationID: "default",
				constants.LabelBizType:       "default",
			},
			Annotations: map[string]string{
				constants.AnnotationNote: "note111",
			},
		},
		Spec: prometheusv1.PrometheusRuleSpec{
			Groups: []prometheusv1.RuleGroup{
				0: {
					Name:     "example",
					Interval: "5",
					Rules: []prometheusv1.Rule{
						{
							Alert: "HighRequestLatency",
							Expr:  intstr.FromString("request_latency_seconds:mean5m{job=\"myjob\"} > 0.5"),
							For:   "10m",
							Labels: map[string]string{
								"severity": "critical",
							},
							Annotations: map[string]string{
								"summary": "High request latency",
							},
						},
					},
				},
			},
		},
	}
}

func TestUpdateAlertRules(t *testing.T) {
	configs.Init(structs.ServiceInfo{
		AlertManagerInfo: structs.AlertManagerConfig{
			Name:      "alertmanager-operated",
			NameSpace: "default",
			Port:      "web",
		},
	})
	threshold := 0.9
	body, _ := json.Marshal(message.UpdateAlertRulesByCloudUserReq{
		CommonReqInfo: structs.CommonReqInfo{
			Vendor: "aws",
			Region: "us-west-2",
		},
		TenantID:      "1",
		ProjectID:     "1",
		ApplicationID: "1",
		BizType:       "1",
		AlertRules: map[string]structs.AlertRuleBase{
			"example:1": {
				AlertRuleID: "example:1",
				For:         utils.Int64Ptr(10),
				Status:      "disabled",
				Threshold:   &threshold,
			},
		},
	})
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	metrics.SetupMetrics()
	promeRuleRW := mockresourcemanager.NewMockPrometheusRuleReaderWriter(ctrl)
	resourcemanager.MockPrometheusRuleReaderWriter(promeRuleRW)

	clusterRW := mockresourcemanager.NewMockClusterReaderWriter(ctrl)
	resourcemanager.MockClusterReaderWriter(clusterRW)

	mockRule := mockOverlayPrometheusRule("id", "test", "test")
	promeRuleRW.EXPECT().GetPrometheusRule(gomock.Any(), gomock.Any()).Return(&mockRule, nil).Times(1)
	clusterRW.EXPECT().QueryClusters(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(
		&v1.ClusterList{
			Items: []v1.Cluster{
				{
					ObjectMeta: metav1.ObjectMeta{
						Name: "clsuter1",
						Annotations: map[string]string{
							constants.AnnotationShareMode: string(constants.SHARED),
						},
						Labels: map[string]string{
							constants.LabelTemplateID: "test",
							constants.LabelVendor:     "aws",
							constants.LabelRegion:     "us-west-2",
						},
					},
				},
			},
		}, nil).Times(1)
	promMock := mockRule
	promMock.Spec.Groups[0].Rules[0].Labels[constants.RuleLabelCanEdit] = constants.RULE_CANEDIT_ALL
	promMock.Spec.Groups[0].Rules[0].Labels[constants.RuleLabelOperator] = "test"
	promMock.Spec.Groups[0].Rules[0].Labels[constants.RuleLabelMonitortarget] = "test"
	promeRuleRW.EXPECT().GetPrometheusRule(gomock.Any(), gomock.Any()).Return(&promMock, nil).Times(1)
	promeRuleRW.EXPECT().UpdatePrometheusRule(gomock.Any(), gomock.Any()).Return(nil).Times(1)
	w := httptest.NewRecorder()

	req, _ := http.NewRequest("PUT", "/api/v1/external/tidbcloud/orgs/1/projects/1/clusters/1/monitor-alertrules", strings.NewReader(string(body)))
	req.Header.Set(constants.AuthorizationHeader, fmt.Sprintf("%s %s", constants.BearerPrefix, stsToken))
	testEngine.ServeHTTP(w, req)
	assert.Equal(t, http.StatusAccepted, w.Code)
}

func TestQueryAlertRules(t *testing.T) {
	configs.Init(structs.ServiceInfo{
		AlertManagerInfo: structs.AlertManagerConfig{
			Name:      "alertmanager-operated",
			NameSpace: "default",
			Port:      "web",
		},
		AlertRuleCacheConfig: structs.OverlayAlertRuleCacheConfig{
			ExpireTime:              3 * time.Second,
			MaxItems:                10,
			MaxAlertRulesPerCluster: 3,
			Enabled:                 true,
			SleepTime:               1 * time.Second,
			MaxDeletionPerCycle:     5,
		},
	})
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	metrics.SetupMetrics()
	promeRuleRW := mockresourcemanager.NewMockPrometheusRuleReaderWriter(ctrl)
	resourcemanager.MockPrometheusRuleReaderWriter(promeRuleRW)

	clusterRW := mockresourcemanager.NewMockClusterReaderWriter(ctrl)
	resourcemanager.MockClusterReaderWriter(clusterRW)
	mockRule := mockOverlayPrometheusRule("id", "test", "test")

	promeRuleRW.EXPECT().GetPrometheusRule(gomock.Any(), gomock.Any()).Return(&mockRule, errors.New(errors.PrometheusRuleNotFound, "unrecognized error")).Times(1)
	clusterRW.EXPECT().QueryClusters(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(
		&v1.ClusterList{
			Items: []v1.Cluster{
				{
					ObjectMeta: metav1.ObjectMeta{
						Name: "clsuter1",
						Annotations: map[string]string{
							constants.AnnotationShareMode: string(constants.SHARED),
						},
						Labels: map[string]string{
							constants.LabelTemplateID: "test",
							constants.LabelVendor:     "aws",
							constants.LabelRegion:     "us-west-2",
						},
					},
				},
			},
		}, nil).Times(1)
	promeRuleRW.EXPECT().GetPrometheusRule(gomock.Any(), gomock.Any()).Return(&mockRule, nil).Times(1)

	w := httptest.NewRecorder()

	req, _ := http.NewRequest("GET", "/api/v1/external/tidbcloud/orgs/1/projects/1/clusters/1/monitor-alertrules?vendor=aws&region=us-west-2", nil)
	req.Header.Set(constants.AuthorizationHeader, fmt.Sprintf("%s %s", constants.BearerPrefix, stsToken))
	testEngine.ServeHTTP(w, req)
	assert.Equal(t, http.StatusOK, w.Code)
}

func TestModifyAlertRulesTemplate(t *testing.T) {
	body, _ := json.Marshal(message.ModifyAlertRulesTemplateReq{
		CommonReqInfo: structs.CommonReqInfo{
			Vendor: "aws",
			Region: "us-east-2",
		},
		TemplateID:       "testTemplateID",
		MonitorPromRules: mockPrometheusRuleSpec("id"),
	})
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	metrics.SetupMetrics()
	promRule := mockresourcemanager.NewMockPrometheusRuleReaderWriter(ctrl)
	resourcemanager.MockPrometheusRuleReaderWriter(promRule)
	t.Run("update success", func(t *testing.T) {
		rule := mockPrometheusRule("id", "example", "example")
		promRule.EXPECT().GetPrometheusRule(gomock.Any(), gomock.Any()).Return(rule, nil).Times(1)
		promRule.EXPECT().UpdatePrometheusRule(gomock.Any(), gomock.Any()).Return(nil).Times(1)
		w := httptest.NewRecorder()

		req, _ := http.NewRequest("POST", "/api/v1/alertrule-template/", strings.NewReader(string(body)))
		req.Header.Set(constants.AuthorizationHeader, fmt.Sprintf("%s %s", constants.BearerPrefix, stsToken))
		testEngine.ServeHTTP(w, req)

		assert.Equal(t, http.StatusAccepted, w.Code)
	})

	t.Run("rule empty", func(t *testing.T) {
		body, _ := json.Marshal(message.ModifyAlertRulesTemplateReq{
			CommonReqInfo: structs.CommonReqInfo{
				Vendor: "aws",
				Region: "us-east-2",
			},
			TemplateID: "testTemplateID",
		})

		w := httptest.NewRecorder()

		req, _ := http.NewRequest("POST", "/api/v1/alertrule-template/", strings.NewReader(string(body)))
		req.Header.Set(constants.AuthorizationHeader, fmt.Sprintf("%s %s", constants.BearerPrefix, stsToken))
		testEngine.ServeHTTP(w, req)

		assert.Equal(t, http.StatusBadRequest, w.Code)
	})

	t.Run("templateID not exists", func(t *testing.T) {
		w := httptest.NewRecorder()
		promRule.EXPECT().GetPrometheusRule(gomock.Any(), gomock.Any()).Return(nil, errors.New(errors.PrometheusRuleNotFound, "rule not exists")).Times(1)
		promRule.EXPECT().Create(gomock.Any(), gomock.Any()).Return(nil).Times(1)
		req, _ := http.NewRequest("POST", "/api/v1/alertrule-template/", strings.NewReader(string(body)))
		req.Header.Set(constants.AuthorizationHeader, fmt.Sprintf("%s %s", constants.BearerPrefix, stsToken))
		testEngine.ServeHTTP(w, req)

		assert.Equal(t, http.StatusAccepted, w.Code)
	})

	t.Run("GetPrometheusRule failed", func(t *testing.T) {
		w := httptest.NewRecorder()
		promRule.EXPECT().GetPrometheusRule(gomock.Any(), gomock.Any()).Return(nil, errors.New(errors.UnrecognizedError, "")).Times(1)

		req, _ := http.NewRequest("POST", "/api/v1/alertrule-template/", strings.NewReader(string(body)))
		req.Header.Set(constants.AuthorizationHeader, fmt.Sprintf("%s %s", constants.BearerPrefix, stsToken))
		testEngine.ServeHTTP(w, req)

		assert.Equal(t, http.StatusInternalServerError, w.Code)
	})

}

func TestQueryAlertRuleTemplate(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	metrics.SetupMetrics()
	promRule := mockresourcemanager.NewMockPrometheusRuleReaderWriter(ctrl)
	resourcemanager.MockPrometheusRuleReaderWriter(promRule)
	t.Run("query succ", func(t *testing.T) {
		rule := mockPrometheusRule("id", "example", "example")
		promRule.EXPECT().GetPrometheusRule(gomock.Any(), gomock.Any()).Return(rule, nil).Times(1)

		w := httptest.NewRecorder()

		req, _ := http.NewRequest("GET", "/api/v1/alertrule-template/testID?vendor=aws&region=us-west-2", nil)
		req.Header.Set(constants.AuthorizationHeader, fmt.Sprintf("%s %s", constants.BearerPrefix, stsToken))
		testEngine.ServeHTTP(w, req)

		assert.Equal(t, http.StatusOK, w.Code)
	})

	t.Run("query failed", func(t *testing.T) {
		promRule.EXPECT().GetPrometheusRule(gomock.Any(), gomock.Any()).Return(nil, errors.New(errors.UnrecognizedError, "")).Times(1)

		w := httptest.NewRecorder()

		req, _ := http.NewRequest("GET", "/api/v1/alertrule-template/testID?vendor=aws&region=us-west-2", nil)
		req.Header.Set(constants.AuthorizationHeader, fmt.Sprintf("%s %s", constants.BearerPrefix, stsToken))
		testEngine.ServeHTTP(w, req)

		assert.Equal(t, http.StatusInternalServerError, w.Code)
	})

}

func TestDeleteAlertRuleTemplate(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	metrics.SetupMetrics()
	promRule := mockresourcemanager.NewMockPrometheusRuleReaderWriter(ctrl)
	resourcemanager.MockPrometheusRuleReaderWriter(promRule)
	t.Run("delete succ", func(t *testing.T) {
		rule := mockPrometheusRule("id", "example", "example")
		promRule.EXPECT().GetPrometheusRule(gomock.Any(), gomock.Any()).Return(rule, nil).Times(1)
		promRule.EXPECT().DeletePrometheusRules(gomock.Any(), gomock.Any()).Return(nil).Times(1)

		w := httptest.NewRecorder()

		req, _ := http.NewRequest("DELETE", "/api/v1/alertrule-template/testID?vendor=aws&region=us-west-2", nil)
		req.Header.Set(constants.AuthorizationHeader, fmt.Sprintf("%s %s", constants.BearerPrefix, stsToken))
		testEngine.ServeHTTP(w, req)

		assert.Equal(t, http.StatusAccepted, w.Code)
	})

	t.Run("rule not exists", func(t *testing.T) {
		promRule.EXPECT().GetPrometheusRule(gomock.Any(), gomock.Any()).Return(nil, errors.New(errors.PrometheusRuleNotFound, "")).Times(1)

		w := httptest.NewRecorder()

		req, _ := http.NewRequest("DELETE", "/api/v1/alertrule-template/testID?vendor=aws&region=us-west-2", nil)
		req.Header.Set(constants.AuthorizationHeader, fmt.Sprintf("%s %s", constants.BearerPrefix, stsToken))
		testEngine.ServeHTTP(w, req)

		assert.Equal(t, http.StatusNotFound, w.Code)
	})

}

func TestModifyMonitorStorage(t *testing.T) {
	q1, err := resource.ParseQuantity("4Gi")
	assert.NoError(t, err)
	q2, err := resource.ParseQuantity("8Gi")
	assert.NoError(t, err)

	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

	clusterRW := mockresourcemanager.NewMockClusterReaderWriter(ctrl)
	resourcemanager.MockClusterReaderWriter(clusterRW)
	pvcRW := mockresourcemanager.NewMockPVCReaderWriter(ctrl)
	resourcemanager.MockPVCReaderWriter(pvcRW)

	clusterRW.EXPECT().GetCluster(gomock.Any(), gomock.Any()).Return(&v1.Cluster{
		ObjectMeta: metav1.ObjectMeta{
			Name: "aaa",
			Annotations: map[string]string{
				"specID": "default",
			},
		},
	}, nil).Times(1)
	pvcRW.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(&corev1.PersistentVolumeClaim{
		Spec: corev1.PersistentVolumeClaimSpec{
			Resources: corev1.VolumeResourceRequirements{
				Requests: map[corev1.ResourceName]resource.Quantity{
					corev1.ResourceStorage: q1,
				},
			},
		},
	}, nil).Times(1)
	pvcRW.EXPECT().Update(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1)
	clusterRW.EXPECT().UpdateCluster(gomock.Any(), gomock.Any()).Return(nil).Times(1)
	w := httptest.NewRecorder()
	body, _ := json.Marshal(message.ModifyMonitorStorageReq{
		CommonReqInfo: structs.CommonReqInfo{
			Vendor: "aws",
			Region: "us-west-2",
		},
		ID:      "aaa",
		Request: q2,
	})
	req, _ := http.NewRequest("POST", "/api/v1/clusters/aaa/monitor-storage/?vendor=aws&region=us-west-2", strings.NewReader(string(body)))
	req.Header.Set(constants.AuthorizationHeader, fmt.Sprintf("%s %s", constants.BearerPrefix, stsToken))
	testEngine.ServeHTTP(w, req)

	assert.Equal(t, http.StatusAccepted, w.Code)
}

func TestModifyMonitorResource(t *testing.T) {
	q, err := resource.ParseQuantity("4Gi")
	assert.NoError(t, err)
	configs.Init(structs.ServiceInfo{
		MonitorSpec: map[string]structs.MonitorSpec{
			"default": {
				Cpu:            "1",
				Memory:         "200Mi",
				InitialStorage: "40Gi",
				OverSell:       1,
				IsDefault:      true,
			},
			"spec1": {
				Cpu:            "2",
				Memory:         "400Mi",
				InitialStorage: "80Gi",
				OverSell:       0.5,
			},
		},
	})

	t.Run("cpu alert", func(t *testing.T) {
		body, _ := json.Marshal(message.ModifyMonitorResourceReq{
			CommonReqInfo: structs.CommonReqInfo{
				Vendor: "aws",
				Region: "us-west-2",
			},
			ResourceType: corev1.ResourceCPU,
			ResourceMeta: map[corev1.ResourceName]message.ResourceMeta{
				corev1.ResourceCPU: {
					SpecID: "spec1",
				},
			},
		})
		ctrl := gomock.NewController(t)
		defer ctrl.Finish()

		clusterRW := mockresourcemanager.NewMockClusterReaderWriter(ctrl)
		resourcemanager.MockClusterReaderWriter(clusterRW)
		pvcRW := mockresourcemanager.NewMockPVCReaderWriter(ctrl)
		resourcemanager.MockPVCReaderWriter(pvcRW)

		clusterRW.EXPECT().GetCluster(gomock.Any(), gomock.Any()).Return(&v1.Cluster{
			ObjectMeta: metav1.ObjectMeta{
				Name: "aaa",
				Annotations: map[string]string{
					"specID": "default",
				},
			},
			Status: v1.ClusterStatus{
				Phase: constants.ACTIVE,
			},
		}, nil).Times(2)
		clusterRW.EXPECT().UpdateCluster(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
		w := httptest.NewRecorder()
		req, _ := http.NewRequest("POST", "/api/v1/clusters/aaa/monitor-resource/?vendor=aws&region=us-west-2", strings.NewReader(string(body)))
		req.Header.Set(constants.AuthorizationHeader, fmt.Sprintf("%s %s", constants.BearerPrefix, stsToken))
		testEngine.ServeHTTP(w, req)

		assert.Equal(t, http.StatusAccepted, w.Code)
	})

	t.Run("memory alert", func(t *testing.T) {
		body, _ := json.Marshal(message.ModifyMonitorResourceReq{
			CommonReqInfo: structs.CommonReqInfo{
				Vendor: "aws",
				Region: "us-west-2",
			},
			ResourceType: corev1.ResourceMemory,
			ResourceMeta: map[corev1.ResourceName]message.ResourceMeta{
				corev1.ResourceMemory: {
					SpecID: "spec1",
				},
			},
		})
		ctrl := gomock.NewController(t)
		defer ctrl.Finish()

		clusterRW := mockresourcemanager.NewMockClusterReaderWriter(ctrl)
		resourcemanager.MockClusterReaderWriter(clusterRW)
		pvcRW := mockresourcemanager.NewMockPVCReaderWriter(ctrl)
		resourcemanager.MockPVCReaderWriter(pvcRW)

		clusterRW.EXPECT().GetCluster(gomock.Any(), gomock.Any()).Return(&v1.Cluster{
			ObjectMeta: metav1.ObjectMeta{
				Name: "aaa",
				Annotations: map[string]string{
					"specID": "default",
				},
			},
			Status: v1.ClusterStatus{
				Phase: constants.ACTIVE,
			},
		}, nil).Times(2)
		clusterRW.EXPECT().UpdateCluster(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
		w := httptest.NewRecorder()
		req, _ := http.NewRequest("POST", "/api/v1/clusters/aaa/monitor-resource/?vendor=aws&region=us-west-2", strings.NewReader(string(body)))
		req.Header.Set(constants.AuthorizationHeader, fmt.Sprintf("%s %s", constants.BearerPrefix, stsToken))
		testEngine.ServeHTTP(w, req)

		assert.Equal(t, http.StatusAccepted, w.Code)
	})

	t.Run("storage alert", func(t *testing.T) {
		q1, err := resource.ParseQuantity("8Gi")
		assert.NoError(t, err)
		body, _ := json.Marshal(message.ModifyMonitorResourceReq{
			ID: "aaa",
			CommonReqInfo: structs.CommonReqInfo{
				Vendor: "aws",
				Region: "us-west-2",
			},
			ResourceType: corev1.ResourceStorage,
			ResourceMeta: map[corev1.ResourceName]message.ResourceMeta{
				corev1.ResourceStorage: {
					Namespace: "test-ns",
					PVCName:   "pvc",
				},
			},
		})
		ctrl := gomock.NewController(t)
		defer ctrl.Finish()

		clusterRW := mockresourcemanager.NewMockClusterReaderWriter(ctrl)
		resourcemanager.MockClusterReaderWriter(clusterRW)
		pvcRW := mockresourcemanager.NewMockPVCReaderWriter(ctrl)
		resourcemanager.MockPVCReaderWriter(pvcRW)

		clusterRW.EXPECT().GetCluster(gomock.Any(), gomock.Any()).Return(&v1.Cluster{
			ObjectMeta: metav1.ObjectMeta{
				Name: "aaa",
				Annotations: map[string]string{
					"specID": "default",
				},
			},
			Spec: v1.ClusterSpec{
				Prometheus: v1.Prometheus{
					Resources: corev1.ResourceRequirements{
						Requests: map[corev1.ResourceName]resource.Quantity{
							corev1.ResourceMemory: q,
						},
					},
				},
			},
		}, nil).Times(2)
		clusterRW.EXPECT().UpdateCluster(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
		pvcRW.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(&corev1.PersistentVolumeClaim{
			Spec: corev1.PersistentVolumeClaimSpec{
				Resources: corev1.VolumeResourceRequirements{
					Requests: map[corev1.ResourceName]resource.Quantity{
						corev1.ResourceStorage: q1,
					},
				},
			},
		}, nil).Times(2)
		pvcRW.EXPECT().Update(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1)
		w := httptest.NewRecorder()
		req, _ := http.NewRequest("POST", "/api/v1/clusters/aaa/monitor-resource/?vendor=aws&region=us-west-2", strings.NewReader(string(body)))
		req.Header.Set(constants.AuthorizationHeader, fmt.Sprintf("%s %s", constants.BearerPrefix, stsToken))
		testEngine.ServeHTTP(w, req)

		assert.Equal(t, http.StatusAccepted, w.Code)
	})
}

func mockOverlayPrometheusRule(id string, tenantID, projectID string) prometheusv1.PrometheusRule {
	return prometheusv1.PrometheusRule{
		ObjectMeta: metav1.ObjectMeta{
			Name:      id,
			Namespace: "default-" + tenantID,
			Labels: map[string]string{
				constants.LabelClusterID:     id,
				constants.LabelVendor:        "aws",
				constants.LabelRegion:        "us-east-2",
				constants.LabelTenantID:      tenantID,
				constants.LabelProjectID:     projectID,
				constants.LabelApplicationID: "default",
				constants.LabelBizType:       "default",
			},
			Annotations: map[string]string{
				constants.AnnotationNote: "note111",
			},
		},
		Spec: prometheusv1.PrometheusRuleSpec{
			Groups: []prometheusv1.RuleGroup{
				0: {
					Name:     "example",
					Interval: "5",
					Rules: []prometheusv1.Rule{
						{
							Alert: "HighRequestLatency",
							Expr:  intstr.FromString("request_latency_seconds:mean5m{job=\"myjob\"} > 0.3"),
							For:   "10m",
							Labels: map[string]string{
								"severity":     "critical",
								"alerttype":    "customer",
								"ruleID":       "1",
								"userModified": "7",
								"visibility":   "external",
							},
							Annotations: map[string]string{
								"summary": "High request latency",
							},
						},
					},
				},
			},
		},
	}
}
func TestApplyLogAlertRules(t *testing.T) {
	body, _ := json.Marshal(message.ApplyLogAlertRulesReq{
		CommonReqInfo: structs.CommonReqInfo{
			Vendor: "aws",
			Region: "us-east-1",
		},
		RulesConfig: prometheusv1.PrometheusRuleSpec{
			Groups: []prometheusv1.RuleGroup{
				{
					Name: "log-alerts",
					Rules: []prometheusv1.Rule{
						{
							Alert: "HighErrorRate",
							Expr:  intstr.FromString(`rate({job="app"} |= "error" [5m]) > 0.1`),
						},
					},
				},
			},
		},
	})

	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

	configMapRW := mockresourcemanager.NewMockConfigMapReaderWriter(ctrl)
	resourcemanager.MockConfigMapReaderWriter(configMapRW)

	t.Run("success", func(t *testing.T) {
		configMapRW.EXPECT().CreateOrUpdate(gomock.Any(), gomock.Any()).Return(nil).Times(1)

		w := httptest.NewRecorder()
		req, _ := http.NewRequest("POST", "/api/v1/alertrule-template/logging/", strings.NewReader(string(body)))
		req.Header.Set(constants.AuthorizationHeader, fmt.Sprintf("%s %s", constants.BearerPrefix, stsToken))
		testEngine.ServeHTTP(w, req)

		assert.Equal(t, http.StatusAccepted, w.Code)
	})

	t.Run("rule empty", func(t *testing.T) {
		body, _ := json.Marshal(message.ApplyLogAlertRulesReq{
			CommonReqInfo: structs.CommonReqInfo{
				Vendor: "aws",
				Region: "us-east-1",
			},
		})

		w := httptest.NewRecorder()
		req, _ := http.NewRequest("POST", "/api/v1/alertrule-template/logging/", strings.NewReader(string(body)))
		req.Header.Set(constants.AuthorizationHeader, fmt.Sprintf("%s %s", constants.BearerPrefix, stsToken))
		testEngine.ServeHTTP(w, req)

		assert.Equal(t, http.StatusBadRequest, w.Code)
	})

	t.Run("invalid json", func(t *testing.T) {
		w := httptest.NewRecorder()
		req, _ := http.NewRequest("POST", "/api/v1/alertrule-template/logging/", strings.NewReader("invalid json"))
		req.Header.Set(constants.AuthorizationHeader, fmt.Sprintf("%s %s", constants.BearerPrefix, stsToken))
		testEngine.ServeHTTP(w, req)

		assert.Equal(t, http.StatusInternalServerError, w.Code)
	})

	t.Run("configmap create error", func(t *testing.T) {
		configMapRW.EXPECT().CreateOrUpdate(gomock.Any(), gomock.Any()).Return(fmt.Errorf("create failed")).Times(1)

		w := httptest.NewRecorder()
		req, _ := http.NewRequest("POST", "/api/v1/alertrule-template/logging/", strings.NewReader(string(body)))
		req.Header.Set(constants.AuthorizationHeader, fmt.Sprintf("%s %s", constants.BearerPrefix, stsToken))
		testEngine.ServeHTTP(w, req)

		assert.Equal(t, http.StatusInternalServerError, w.Code)
	})
}
