package slowquery

import (
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/require"
)

func TestRecordHttpMetric(t *testing.T) {
	gin.SetMode(gin.TestMode)
	router := gin.New()

	router.GET("/test", func(c *gin.Context) {
		f := recordHttpMetric(c, "/test")
		defer f()

		c.JSON(http.StatusOK, gin.H{"message": "success"})
	})

	req := httptest.NewRequest("GET", "/test", nil)
	w := httptest.NewRecorder()

	router.ServeHTTP(w, req)

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

func TestRecordHttpMetric_Duration(t *testing.T) {
	gin.SetMode(gin.TestMode)
	router := gin.New()

	router.GET("/slow", func(c *gin.Context) {
		f := recordHttpMetric(c, "/slow")
		defer f()

		// Simulate some work
		time.Sleep(10 * time.Millisecond)

		c.JSON(http.StatusOK, gin.H{"message": "done"})
	})

	req := httptest.NewRequest("GET", "/slow", nil)
	w := httptest.NewRecorder()

	start := time.Now()
	router.ServeHTTP(w, req)
	duration := time.Since(start)

	require.Equal(t, http.StatusOK, w.Code)
	require.GreaterOrEqual(t, duration, 10*time.Millisecond)
}

func TestRecordHttpMetric_ErrorStatus(t *testing.T) {
	gin.SetMode(gin.TestMode)
	router := gin.New()

	router.GET("/error", func(c *gin.Context) {
		f := recordHttpMetric(c, "/error")
		defer f()

		c.JSON(http.StatusInternalServerError, gin.H{"error": "internal error"})
	})

	req := httptest.NewRequest("GET", "/error", nil)
	w := httptest.NewRecorder()

	router.ServeHTTP(w, req)

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

func TestRecordHttpMetric_DifferentMethods(t *testing.T) {
	gin.SetMode(gin.TestMode)
	router := gin.New()

	router.GET("/method", func(c *gin.Context) {
		f := recordHttpMetric(c, "/method")
		defer f()
		c.JSON(http.StatusOK, gin.H{})
	})

	router.POST("/method", func(c *gin.Context) {
		f := recordHttpMetric(c, "/method")
		defer f()
		c.JSON(http.StatusOK, gin.H{})
	})

	router.PUT("/method", func(c *gin.Context) {
		f := recordHttpMetric(c, "/method")
		defer f()
		c.JSON(http.StatusOK, gin.H{})
	})

	router.DELETE("/method", func(c *gin.Context) {
		f := recordHttpMetric(c, "/method")
		defer f()
		c.JSON(http.StatusOK, gin.H{})
	})

	methods := []string{"GET", "POST", "PUT", "DELETE"}
	for _, method := range methods {
		req := httptest.NewRequest(method, "/method", nil)
		w := httptest.NewRecorder()
		router.ServeHTTP(w, req)
		require.Equal(t, http.StatusOK, w.Code)
	}
}

func TestRecordHttpMetric_Concurrent(t *testing.T) {
	gin.SetMode(gin.TestMode)
	router := gin.New()

	router.GET("/concurrent", func(c *gin.Context) {
		f := recordHttpMetric(c, "/concurrent")
		defer f()
		c.JSON(http.StatusOK, gin.H{})
	})

	// Test concurrent requests
	done := make(chan bool, 10)
	for i := 0; i < 10; i++ {
		go func() {
			req := httptest.NewRequest("GET", "/concurrent", nil)
			w := httptest.NewRecorder()
			router.ServeHTTP(w, req)
			require.Equal(t, http.StatusOK, w.Code)
			done <- true
		}()
	}

	// Wait for all requests to complete
	for i := 0; i < 10; i++ {
		<-done
	}
}

func TestRecordHttpMetric_DifferentPaths(t *testing.T) {
	gin.SetMode(gin.TestMode)
	router := gin.New()

	paths := []string{"/list", "/detail", "/databases", "/available_fields", "/stats", "/stats/metric", "/stats/databases", "/stats/time_windows"}

	for _, path := range paths {
		router.GET(path, func(c *gin.Context) {
			f := recordHttpMetric(c, path)
			defer f()
			c.JSON(http.StatusOK, gin.H{"path": path})
		})
	}

	for _, path := range paths {
		req := httptest.NewRequest("GET", path, nil)
		w := httptest.NewRecorder()
		router.ServeHTTP(w, req)
		require.Equal(t, http.StatusOK, w.Code)
	}
}

func TestRecordHttpMetric_DifferentStatusCodes(t *testing.T) {
	gin.SetMode(gin.TestMode)
	router := gin.New()

	router.GET("/200", func(c *gin.Context) {
		f := recordHttpMetric(c, "/200")
		defer f()
		c.JSON(http.StatusOK, gin.H{})
	})

	router.GET("/400", func(c *gin.Context) {
		f := recordHttpMetric(c, "/400")
		defer f()
		c.JSON(http.StatusBadRequest, gin.H{})
	})

	router.GET("/404", func(c *gin.Context) {
		f := recordHttpMetric(c, "/404")
		defer f()
		c.JSON(http.StatusNotFound, gin.H{})
	})

	router.GET("/500", func(c *gin.Context) {
		f := recordHttpMetric(c, "/500")
		defer f()
		c.JSON(http.StatusInternalServerError, gin.H{})
	})

	tests := []struct {
		path       string
		statusCode int
	}{
		{"/200", http.StatusOK},
		{"/400", http.StatusBadRequest},
		{"/404", http.StatusNotFound},
		{"/500", http.StatusInternalServerError},
	}

	for _, tt := range tests {
		req := httptest.NewRequest("GET", tt.path, nil)
		w := httptest.NewRecorder()
		router.ServeHTTP(w, req)
		require.Equal(t, tt.statusCode, w.Code)
	}
}
