package mcp

import (
	"net/http"
	"regexp"
	"testing"

	"github.com/containers/kubernetes-mcp-server/internal/test"
	"github.com/mark3labs/mcp-go/mcp"

	"github.com/containers/kubernetes-mcp-server/pkg/config"
)

func TestPodsTopMetricsUnavailable(t *testing.T) {
	testCase(t, func(c *mcpContext) {
		mockServer := test.NewMockServer()
		defer mockServer.Close()
		c.withKubeConfig(mockServer.Config())
		mockServer.Handle(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			w.Header().Set("Content-Type", "application/json")
			// Request Performed by DiscoveryClient to Kube API (Get API Groups legacy -core-)
			if req.URL.Path == "/api" {
				_, _ = w.Write([]byte(`{"kind":"APIVersions","versions":[],"serverAddressByClientCIDRs":[{"clientCIDR":"0.0.0.0/0"}]}`))
				return
			}
			// Request Performed by DiscoveryClient to Kube API (Get API Groups)
			if req.URL.Path == "/apis" {
				_, _ = w.Write([]byte(`{"kind":"APIGroupList","apiVersion":"v1","groups":[]}`))
				return
			}
		}))
		podsTopMetricsApiUnavailable, err := c.callTool("pods_top", map[string]interface{}{})
		t.Run("pods_top with metrics API not available", func(t *testing.T) {
			if err != nil {
				t.Fatalf("call tool failed %v", err)
			}
			if !podsTopMetricsApiUnavailable.IsError {
				t.Errorf("call tool should have returned an error")
			}
			if podsTopMetricsApiUnavailable.Content[0].(mcp.TextContent).Text != "failed to get pods top: metrics API is not available" {
				t.Errorf("call tool returned unexpected content: %s", podsTopMetricsApiUnavailable.Content[0].(mcp.TextContent).Text)
			}
		})
	})
}

func TestPodsTopMetricsAvailable(t *testing.T) {
	testCase(t, func(c *mcpContext) {
		mockServer := test.NewMockServer()
		defer mockServer.Close()
		c.withKubeConfig(mockServer.Config())
		mockServer.Handle(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			println("Request received:", req.Method, req.URL.Path) // TODO: REMOVE LINE
			w.Header().Set("Content-Type", "application/json")
			// Request Performed by DiscoveryClient to Kube API (Get API Groups legacy -core-)
			if req.URL.Path == "/api" {
				_, _ = w.Write([]byte(`{"kind":"APIVersions","versions":["metrics.k8s.io/v1beta1"],"serverAddressByClientCIDRs":[{"clientCIDR":"0.0.0.0/0"}]}`))
				return
			}
			// Request Performed by DiscoveryClient to Kube API (Get API Groups)
			if req.URL.Path == "/apis" {
				_, _ = w.Write([]byte(`{"kind":"APIGroupList","apiVersion":"v1","groups":[]}`))
				return
			}
			// Request Performed by DiscoveryClient to Kube API (Get API Resources)
			if req.URL.Path == "/apis/metrics.k8s.io/v1beta1" {
				_, _ = w.Write([]byte(`{"kind":"APIResourceList","apiVersion":"v1","groupVersion":"metrics.k8s.io/v1beta1","resources":[{"name":"pods","singularName":"","namespaced":true,"kind":"PodMetrics","verbs":["get","list"]}]}`))
				return
			}
			// Pod Metrics from all namespaces
			if req.URL.Path == "/apis/metrics.k8s.io/v1beta1/pods" {
				if req.URL.Query().Get("labelSelector") == "app=pod-ns-5-42" {
					_, _ = w.Write([]byte(`{"kind":"PodMetricsList","apiVersion":"metrics.k8s.io/v1beta1","items":[` +
						`{"metadata":{"name":"pod-ns-5-42","namespace":"ns-5"},"containers":[{"name":"container-1","usage":{"cpu":"42m","memory":"42Mi","swap":"42Mi"}}]}` +
						`]}`))
				} else {
					_, _ = w.Write([]byte(`{"kind":"PodMetricsList","apiVersion":"metrics.k8s.io/v1beta1","items":[` +
						`{"metadata":{"name":"pod-1","namespace":"default"},"containers":[{"name":"container-1","usage":{"cpu":"100m","memory":"200Mi","swap":"13Mi"}},{"name":"container-2","usage":{"cpu":"200m","memory":"300Mi","swap":"37Mi"}}]},` +
						`{"metadata":{"name":"pod-2","namespace":"ns-1"},"containers":[{"name":"container-1-ns-1","usage":{"cpu":"300m","memory":"400Mi","swap":"42Mi"}}]}` +
						`]}`))

				}
				return
			}
			// Pod Metrics from configured namespace
			if req.URL.Path == "/apis/metrics.k8s.io/v1beta1/namespaces/default/pods" {
				_, _ = w.Write([]byte(`{"kind":"PodMetricsList","apiVersion":"metrics.k8s.io/v1beta1","items":[` +
					`{"metadata":{"name":"pod-1","namespace":"default"},"containers":[{"name":"container-1","usage":{"cpu":"10m","memory":"20Mi","swap":"13Mi"}},{"name":"container-2","usage":{"cpu":"30m","memory":"40Mi","swap":"37Mi"}}]}` +
					`]}`))
				return
			}
			// Pod Metrics from ns-5 namespace
			if req.URL.Path == "/apis/metrics.k8s.io/v1beta1/namespaces/ns-5/pods" {
				_, _ = w.Write([]byte(`{"kind":"PodMetricsList","apiVersion":"metrics.k8s.io/v1beta1","items":[` +
					`{"metadata":{"name":"pod-ns-5-1","namespace":"ns-5"},"containers":[{"name":"container-1","usage":{"cpu":"10m","memory":"20Mi","swap":"42Mi"}}]}` +
					`]}`))
				return
			}
			// Pod Metrics from ns-5 namespace with pod-ns-5-5 pod name
			if req.URL.Path == "/apis/metrics.k8s.io/v1beta1/namespaces/ns-5/pods/pod-ns-5-5" {
				_, _ = w.Write([]byte(`{"kind":"PodMetrics","apiVersion":"metrics.k8s.io/v1beta1",` +
					`"metadata":{"name":"pod-ns-5-5","namespace":"ns-5"},` +
					`"containers":[{"name":"container-1","usage":{"cpu":"13m","memory":"37Mi","swap":"42Mi"}}]` +
					`}`))
			}
		}))
		podsTopDefaults, err := c.callTool("pods_top", map[string]interface{}{})
		t.Run("pods_top defaults returns pod metrics from all namespaces", func(t *testing.T) {
			if err != nil {
				t.Fatalf("call tool failed %v", err)
			}
			textContent := podsTopDefaults.Content[0].(mcp.TextContent).Text
			if podsTopDefaults.IsError {
				t.Fatalf("call tool failed %s", textContent)
			}
			expectedHeaders := regexp.MustCompile(`(?m)^\s*NAMESPACE\s+POD\s+NAME\s+CPU\(cores\)\s+MEMORY\(bytes\)\s+SWAP\(bytes\)\s*$`)
			if !expectedHeaders.MatchString(textContent) {
				t.Errorf("Expected headers '%s' not found in output:\n%s", expectedHeaders.String(), textContent)
			}
			expectedRows := []string{
				"default\\s+pod-1\\s+container-1\\s+100m\\s+200Mi\\s+13Mi",
				"default\\s+pod-1\\s+container-2\\s+200m\\s+300Mi\\s+37Mi",
				"ns-1\\s+pod-2\\s+container-1-ns-1\\s+300m\\s+400Mi\\s+42Mi",
			}
			for _, row := range expectedRows {
				if !regexp.MustCompile(row).MatchString(textContent) {
					t.Errorf("Expected row '%s' not found in output:\n%s", row, textContent)
				}
			}
			expectedTotal := regexp.MustCompile(`(?m)^\s+600m\s+900Mi\s+92Mi\s*$`)
			if !expectedTotal.MatchString(textContent) {
				t.Errorf("Expected total row '%s' not found in output:\n%s", expectedTotal.String(), textContent)
			}
		})
		podsTopConfiguredNamespace, err := c.callTool("pods_top", map[string]interface{}{
			"all_namespaces": false,
		})
		t.Run("pods_top[allNamespaces=false] returns pod metrics from configured namespace", func(t *testing.T) {
			if err != nil {
				t.Fatalf("call tool failed %v", err)
			}
			textContent := podsTopConfiguredNamespace.Content[0].(mcp.TextContent).Text
			expectedRows := []string{
				"default\\s+pod-1\\s+container-1\\s+10m\\s+20Mi\\s+13Mi",
				"default\\s+pod-1\\s+container-2\\s+30m\\s+40Mi\\s+37Mi",
			}
			for _, row := range expectedRows {
				if !regexp.MustCompile(row).MatchString(textContent) {
					t.Errorf("Expected row '%s' not found in output:\n%s", row, textContent)
				}
			}
			expectedTotal := regexp.MustCompile(`(?m)^\s+40m\s+60Mi\s+50Mi\s*$`)
			if !expectedTotal.MatchString(textContent) {
				t.Errorf("Expected total row '%s' not found in output:\n%s", expectedTotal.String(), textContent)
			}
		})
		podsTopNamespace, err := c.callTool("pods_top", map[string]interface{}{
			"namespace": "ns-5",
		})
		t.Run("pods_top[namespace=ns-5] returns pod metrics from provided namespace", func(t *testing.T) {
			if err != nil {
				t.Fatalf("call tool failed %v", err)
			}
			textContent := podsTopNamespace.Content[0].(mcp.TextContent).Text
			expectedRow := regexp.MustCompile(`ns-5\s+pod-ns-5-1\s+container-1\s+10m\s+20Mi\s+42Mi`)
			if !expectedRow.MatchString(textContent) {
				t.Errorf("Expected row '%s' not found in output:\n%s", expectedRow.String(), textContent)
			}
			expectedTotal := regexp.MustCompile(`(?m)^\s+10m\s+20Mi\s+42Mi\s*$`)
			if !expectedTotal.MatchString(textContent) {
				t.Errorf("Expected total row '%s' not found in output:\n%s", expectedTotal.String(), textContent)
			}
		})
		podsTopNamespaceName, err := c.callTool("pods_top", map[string]interface{}{
			"namespace": "ns-5",
			"name":      "pod-ns-5-5",
		})
		t.Run("pods_top[namespace=ns-5,name=pod-ns-5-5] returns pod metrics from provided namespace and name", func(t *testing.T) {
			if err != nil {
				t.Fatalf("call tool failed %v", err)
			}
			textContent := podsTopNamespaceName.Content[0].(mcp.TextContent).Text
			expectedRow := regexp.MustCompile(`ns-5\s+pod-ns-5-5\s+container-1\s+13m\s+37Mi\s+42Mi`)
			if !expectedRow.MatchString(textContent) {
				t.Errorf("Expected row '%s' not found in output:\n%s", expectedRow.String(), textContent)
			}
			expectedTotal := regexp.MustCompile(`(?m)^\s+13m\s+37Mi\s+42Mi\s*$`)
			if !expectedTotal.MatchString(textContent) {
				t.Errorf("Expected total row '%s' not found in output:\n%s", expectedTotal.String(), textContent)
			}
		})
		podsTopNamespaceLabelSelector, err := c.callTool("pods_top", map[string]interface{}{
			"label_selector": "app=pod-ns-5-42",
		})
		t.Run("pods_top[label_selector=app=pod-ns-5-42] returns pod metrics from pods matching selector", func(t *testing.T) {
			if err != nil {
				t.Fatalf("call tool failed %v", err)
			}
			textContent := podsTopNamespaceLabelSelector.Content[0].(mcp.TextContent).Text
			expectedRow := regexp.MustCompile(`ns-5\s+pod-ns-5-42\s+container-1\s+42m\s+42Mi`)
			if !expectedRow.MatchString(textContent) {
				t.Errorf("Expected row '%s' not found in output:\n%s", expectedRow.String(), textContent)
			}
			expectedTotal := regexp.MustCompile(`(?m)^\s+42m\s+42Mi\s+42Mi\s*$`)
			if !expectedTotal.MatchString(textContent) {
				t.Errorf("Expected total row '%s' not found in output:\n%s", expectedTotal.String(), textContent)
			}
		})
	})
}

func TestPodsTopDenied(t *testing.T) {
	deniedResourcesServer := test.Must(config.ReadToml([]byte(`
		denied_resources = [ { group = "metrics.k8s.io", version = "v1beta1" } ]
	`)))
	testCaseWithContext(t, &mcpContext{staticConfig: deniedResourcesServer}, func(c *mcpContext) {
		mockServer := test.NewMockServer()
		defer mockServer.Close()
		c.withKubeConfig(mockServer.Config())
		mockServer.Handle(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			w.Header().Set("Content-Type", "application/json")
			// Request Performed by DiscoveryClient to Kube API (Get API Groups legacy -core-)
			if req.URL.Path == "/api" {
				_, _ = w.Write([]byte(`{"kind":"APIVersions","versions":["metrics.k8s.io/v1beta1"],"serverAddressByClientCIDRs":[{"clientCIDR":"0.0.0.0/0"}]}`))
				return
			}
			// Request Performed by DiscoveryClient to Kube API (Get API Groups)
			if req.URL.Path == "/apis" {
				_, _ = w.Write([]byte(`{"kind":"APIGroupList","apiVersion":"v1","groups":[]}`))
				return
			}
			// Request Performed by DiscoveryClient to Kube API (Get API Resources)
			if req.URL.Path == "/apis/metrics.k8s.io/v1beta1" {
				_, _ = w.Write([]byte(`{"kind":"APIResourceList","apiVersion":"v1","groupVersion":"metrics.k8s.io/v1beta1","resources":[{"name":"pods","singularName":"","namespaced":true,"kind":"PodMetrics","verbs":["get","list"]}]}`))
				return
			}
		}))
		podsTop, _ := c.callTool("pods_top", map[string]interface{}{})
		t.Run("pods_run has error", func(t *testing.T) {
			if !podsTop.IsError {
				t.Fatalf("call tool should fail")
			}
		})
		t.Run("pods_run describes denial", func(t *testing.T) {
			expectedMessage := "failed to get pods top: resource not allowed: metrics.k8s.io/v1beta1, Kind=PodMetrics"
			if podsTop.Content[0].(mcp.TextContent).Text != expectedMessage {
				t.Fatalf("expected descriptive error '%s', got %v", expectedMessage, podsTop.Content[0].(mcp.TextContent).Text)
			}
		})
	})
}
