//go:build ops
// +build ops

package ops

import (
	"fmt"
	"github.com/stretchr/testify/assert"
	"github.com/tidbcloud/observability/pkg/common/constants"
	"github.com/tidbcloud/observability/pkg/common/message"
	"github.com/tidbcloud/observability/pkg/common/structs"
	"github.com/tidbcloud/observability/test/ft/utils"
	"io"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	"net/http"
	"testing"
)

func TestModifyClusterSpec(t *testing.T) {
	cluster := utils.GetCluster(specTestID)
	resp := utils.ModifySpec(specTestID, "t1g.small")
	fmt.Println(resp.Status)
	newCluster := utils.GetCluster(specTestID)

	assert.Equal(t, http.StatusAccepted, resp.StatusCode)
	assert.NotEqual(t, cluster.MonitorInfo.SpecID, newCluster.MonitorInfo.SpecID)
	assert.NotEqual(t, cluster.LastScaleTime["spec"], newCluster.LastScaleTime["spec"])
}

func TestModifyClusterStorage(t *testing.T) {
	cluster := utils.GetCluster(storageTestID)

	ns := fmt.Sprintf("%s-%s", cluster.BizType, cluster.TenantID)
	pvcName := fmt.Sprintf("prometheus-%s-db-prometheus-%s-0", storageTestID, storageTestID)

	request, err := resource.ParseQuantity("16Gi")
	assert.NoError(t, err)
	resp := utils.ModifyMonitorStorage(storageTestID, ns, pvcName, request)
	assert.Equal(t, http.StatusAccepted, resp.StatusCode)
	newCluster := utils.GetCluster(storageTestID)

	assert.NotEqual(t, cluster.LastScaleTime["storage"], newCluster.LastScaleTime["storage"])
}

func TestModifyClusterResource(t *testing.T) {
	cluster := utils.GetCluster(resourceTestID)
	ns := fmt.Sprintf("%s-%s", cluster.BizType, cluster.TenantID)
	pvcName := fmt.Sprintf("prometheus-%s-db-prometheus-%s-0", resourceTestID, resourceTestID)

	request, err := resource.ParseQuantity("10Gi")
	assert.NoError(t, err)
	getCluster := utils.GetCluster(resourceTestID)
	assert.Equal(t, constants.ACTIVE, getCluster.Status)

	fmt.Println("----")
	resp3 := utils.ModifyMonitorResource(resourceTestID, corev1.ResourceStorage, ns, "t1g.micro", pvcName, request)
	respContent, err := io.ReadAll(resp3.Body)
	assert.NoError(t, err)
	newCluster1 := utils.GetCluster(resourceTestID)
	fmt.Println("request\n", resp3.Request)
	fmt.Println("resp3\n", string(respContent))
	fmt.Println("create cluster, specID is:", cluster.MonitorInfo.SpecID)
	fmt.Println("create cluster, lastScaleTime is:", cluster.LastScaleTime["storage"])
	fmt.Println("update cluster, lastScaleTime is:", newCluster1.LastScaleTime["storage"])
	assert.Equal(t, http.StatusAccepted, resp3.StatusCode)
	assert.NotEqual(t, cluster.LastScaleTime["storage"], newCluster1.LastScaleTime["storage"])

	fmt.Println("----")
	resp2 := utils.ModifyMonitorResource(resourceTestID, corev1.ResourceMemory, ns, "t1g.micro", pvcName, request)
	respContent, _ = io.ReadAll(resp2.Body)
	newCluster2 := utils.GetCluster(resourceTestID)
	fmt.Println("old specID is:", newCluster1.MonitorInfo.SpecID)
	fmt.Println("old lastScaleTime is:", newCluster1.LastScaleTime["spec"])
	fmt.Println("update cluster, specID is:", newCluster2.MonitorInfo.SpecID)
	fmt.Println("update cluster, lastScaleTime is:", newCluster2.LastScaleTime["spec"])
	assert.Equal(t, http.StatusAccepted, resp2.StatusCode)
	assert.Equal(t, "t1g.micro", newCluster2.MonitorInfo.SpecID)
	assert.NotEqual(t, newCluster1.LastScaleTime["spec"], newCluster2.LastScaleTime["spec"])

	fmt.Println("----")
	resp1 := utils.ModifyMonitorResourceV2(resourceTestID, corev1.ResourceCPU, ns, "t1g.small", pvcName, request)
	respContent, err = io.ReadAll(resp1.Body)
	newCluster3 := utils.GetCluster(resourceTestID)
	assert.NoError(t, err)
	fmt.Println("old specID is:", newCluster2.MonitorInfo.SpecID)
	fmt.Println("old lastScaleTime is:", newCluster2.LastScaleTime["spec"])
	fmt.Println("update cluster, specID is:", newCluster3.MonitorInfo.SpecID)
	fmt.Println("update cluster, lastScaleTime is:", newCluster3.LastScaleTime["spec"])
	assert.Equal(t, http.StatusAccepted, resp1.StatusCode)
	assert.Equal(t, "t1g.small", newCluster3.MonitorInfo.SpecID)
	assert.NotEqual(t, newCluster2.LastScaleTime["spec"], newCluster3.LastScaleTime["spec"])
}

func TestHandleAlert_CPU(t *testing.T) {
	cluster := utils.GetCluster(alertCpuTestID)
	ns := fmt.Sprintf("%s-%s", cluster.BizType, cluster.TenantID)
	fmt.Printf("kg cluster %s -n %s -oyaml\n", alertCpuTestID, ns)

	fmt.Println("create cluster last scale time:", cluster.LastScaleTime["spec"])
	fmt.Println("create cluster specID:", cluster.MonitorInfo.SpecID)
	t.Run("normal", func(t *testing.T) {
		req := message.GlobalHandleAlertReq{
			Status: "firing",
			Alerts: []structs.Alert{
				{
					Status: "firing",
					Labels: map[string]string{
						"alertname":              "PrometheusCPUThrottlingHigh",
						constants.O11YK8SIDLabel: "o11y-staging-regional-" + cluster.Region,
						"component":              "observability-regional-server",
						"namespace":              ns,
						"pod":                    "prometheus-" + alertCpuTestID + "-0",
						"severity":               "critical",
						"container":              "prometheus",
					},
					Annotations: map[string]string{},
				},
			},
		}
		request := utils.HandleAlertReq(req)
		resp, err := http.DefaultClient.Do(request)

		assert.Equal(t, http.StatusOK, resp.StatusCode)
		assert.NoError(t, err)
		newCluster := utils.GetCluster(alertCpuTestID)
		fmt.Println("update cluster last scale time:", newCluster.LastScaleTime["spec"])
		fmt.Println("update cluster specID:", newCluster.MonitorInfo.SpecID)
		assert.NotEqual(t, cluster.LastScaleTime["spec"], newCluster.LastScaleTime["spec"])
		assert.NotEqual(t, cluster.MonitorInfo.SpecID, newCluster.MonitorInfo.SpecID)
	})
}

func TestHandleAlert_Memory(t *testing.T) {
	cluster := utils.GetCluster(alertMemTestID)
	ns := fmt.Sprintf("%s-%s", cluster.BizType, cluster.TenantID)

	c := utils.GetCluster(alertMemTestID)
	fmt.Println(c.LastScaleTime["spec"])
	fmt.Println(c.MonitorInfo.SpecID)

	req := mockMemoryAlertReq(alertMemTestID, cluster.Region, ns)
	request := utils.HandleAlertReq(req)
	resp, err := http.DefaultClient.Do(request)
	fmt.Println(resp)

	assert.Equal(t, http.StatusOK, resp.StatusCode)
	assert.NoError(t, err)

	newCluster := utils.GetCluster(alertMemTestID)
	fmt.Println(newCluster.LastScaleTime["spec"])
	fmt.Println(newCluster.MonitorInfo.SpecID)
	assert.NotEqual(t, cluster.MonitorInfo.SpecID, newCluster.MonitorInfo.SpecID)
	assert.NotEqual(t, c.MonitorInfo.SpecID, newCluster.MonitorInfo.SpecID)
	t.Run("within an hour", func(t *testing.T) {
		cluster = utils.GetCluster(alertMemTestID)
		req = mockMemoryAlertReq(alertMemTestID, cluster.Region, ns)
		request = utils.HandleAlertReq(req)
		resp, err = http.DefaultClient.Do(request)

		assert.Equal(t, http.StatusMethodNotAllowed, resp.StatusCode)
		assert.NoError(t, err)
		newCluster = utils.GetCluster(alertMemTestID)
		fmt.Println("old cluster lastScaleTime: ", cluster.LastScaleTime["spec"])
		fmt.Println("old cluster specID: ", cluster.MonitorInfo.SpecID)
		fmt.Println("new cluster lastScaleTime: ", newCluster.LastScaleTime["spec"])
		fmt.Println("new cluster specID: ", newCluster.MonitorInfo.SpecID)
		assert.Equal(t, cluster.MonitorInfo.SpecID, newCluster.MonitorInfo.SpecID)
		assert.Equal(t, cluster.LastScaleTime["spec"], newCluster.LastScaleTime["spec"])
	})

}

func TestHandleAlert_Storage(t *testing.T) {
	cluster := utils.GetCluster(alertStorageTestID)

	ns := fmt.Sprintf("%s-%s", cluster.BizType, cluster.TenantID)
	pvcName := fmt.Sprintf("prometheus-%s-db-prometheus-%s-0", alertStorageTestID, alertStorageTestID)

	fmt.Printf("kg pvc %s -n %s -oyaml\n", pvcName, ns)

	req := mockStorageAlertReq(ns, cluster.Region, pvcName)
	request := utils.HandleAlertReq(req)
	resp, err := http.DefaultClient.Do(request)
	fmt.Println(resp)

	assert.Equal(t, http.StatusOK, resp.StatusCode)
	assert.NoError(t, err)
	newCluster := utils.GetCluster(alertStorageTestID)
	fmt.Println("create cluster, specID is:", cluster.MonitorInfo.SpecID)
	fmt.Println("create cluster, lastScaleTime is:", cluster.LastScaleTime["storage"])
	fmt.Println("update cluster, lastScaleTime is:", newCluster.LastScaleTime["storage"])

	assert.NotEqual(t, cluster.LastScaleTime["storage"], newCluster.LastScaleTime["storage"])
}

func TestHandleAlert_maxSpec(t *testing.T) {
	cluster := utils.GetCluster(alertMaxSpecTestID)
	ns := fmt.Sprintf("%s-%s", cluster.BizType, cluster.TenantID)

	req := mockCPUAlertReq(alertMaxSpecTestID, cluster.Region, ns)
	request := utils.HandleAlertReq(req)
	resp, err := http.DefaultClient.Do(request)
	body, err := io.ReadAll(resp.Body)
	fmt.Println("body: ", string(body))
	assert.Equal(t, http.StatusOK, resp.StatusCode)
	assert.NoError(t, err)
	newCluster := utils.GetCluster(alertMaxSpecTestID)
	fmt.Println("old cluster lastScaleTime: ", cluster.LastScaleTime["spec"])
	fmt.Println("old cluster specID: ", cluster.MonitorInfo.SpecID)
	fmt.Println("new cluster lastScaleTime: ", newCluster.LastScaleTime["spec"])
	fmt.Println("new cluster specID: ", newCluster.MonitorInfo.SpecID)
	assert.Equal(t, cluster.MonitorInfo.SpecID, newCluster.MonitorInfo.SpecID)
	assert.Equal(t, cluster.LastScaleTime["spec"], newCluster.LastScaleTime["spec"])
}

func TestHandleAlert_other(t *testing.T) {
	cluster := utils.GetCluster(alertMemTestID)

	req := mockOtherMemoryAlertReq()
	request := utils.HandleAlertReq(req)
	resp, err := http.DefaultClient.Do(request)

	assert.Equal(t, http.StatusOK, resp.StatusCode)
	assert.NoError(t, err)
	newCluster := utils.GetCluster(alertMemTestID)
	fmt.Println("old cluster lastScaleTime: ", cluster.LastScaleTime["spec"])
	fmt.Println("old cluster specID: ", cluster.MonitorInfo.SpecID)
	fmt.Println("new cluster lastScaleTime: ", newCluster.LastScaleTime["spec"])
	fmt.Println("new cluster specID: ", newCluster.MonitorInfo.SpecID)
	assert.Equal(t, cluster.MonitorInfo.SpecID, newCluster.MonitorInfo.SpecID)
	assert.Equal(t, cluster.LastScaleTime["spec"], newCluster.LastScaleTime["spec"])
}

func mockCPUAlertReq(id string, region string, ns string) message.GlobalHandleAlertReq {
	return message.GlobalHandleAlertReq{
		Status: "firing",
		Alerts: []structs.Alert{
			{
				Status: "firing",
				Labels: map[string]string{
					"alertname":              "PrometheusCPUThrottlingHigh",
					constants.O11YK8SIDLabel: "o11y-prod-regional-" + region,
					"component":              "observability-regional-server",
					"namespace":              ns,
					"pod":                    "prometheus-" + id + "-0",
					"severity":               "critical",
					"container":              "prometheus",
				},
				Annotations: map[string]string{},
			},
		},
	}
}

func mockMemoryAlertReq(id string, region string, ns string) message.GlobalHandleAlertReq {
	return message.GlobalHandleAlertReq{
		Status: "firing",
		Alerts: []structs.Alert{
			{
				Status: "firing",
				Labels: map[string]string{
					"alertname":              "O11yPodMemoryFillingUp",
					constants.O11YK8SIDLabel: "o11y-prod-regional-" + region,
					"component":              "observability-regional-server",
					"namespace":              ns,
					"pod":                    "prometheus-" + id + "-0",
					"severity":               "critical",
					"container":              "prometheus",
				},
				Annotations: map[string]string{},
			},
		},
	}
}

func mockStorageAlertReq(ns string, region string, pvcName string) message.GlobalHandleAlertReq {
	return message.GlobalHandleAlertReq{
		Status: "firing",
		Alerts: []structs.Alert{
			{
				Status: "firing",
				Labels: map[string]string{
					"alertname":              "KubePersistentVolumeFillingUp",
					constants.O11YK8SIDLabel: "o11y-prod-regional-" + region,
					"component":              "observability-regional-server",
					"endpoint":               "https-metrics",
					"instance":               "10.0.56.142:10250",
					"job":                    "kubelet",
					"metrics_path":           "/metrics",
					"namespace":              ns,
					"node":                   "ip-10-0-56-142.ec2.internal",
					"persistentvolumeclaim":  pvcName,
					"severity":               "critical",
				},
				Annotations: map[string]string{},
			},
		},
	}
}

func mockOtherMemoryAlertReq() message.GlobalHandleAlertReq {
	return message.GlobalHandleAlertReq{
		Status: "firing",
		Alerts: []structs.Alert{
			{
				Status: "firing",
				Labels: map[string]string{
					"alertname":              "O11yPodMemoryFillingUp",
					constants.O11YK8SIDLabel: "o11y-staging-regional-us-west-2",
					"component":              "observability-regional-server",
					"namespace":              "monitoring-system",
					"pod":                    "vm-operator-tttttt",
					"severity":               "critical",
					"prometheus":             "self-monitoring/self-monitoring-kube-prome-prometheus",
				},
				Annotations: map[string]string{},
			},
		},
	}
}
