package sqls

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

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

func TestInitMetrics(t *testing.T) {
	// Reset the once variable by creating a new instance
	once = sync.Once{}

	// Test that InitMetrics can be called multiple times safely
	InitMetrics()
	InitMetrics()
	InitMetrics()

	// Verify metrics are registered by checking they can be collected
	// Note: We can't easily test registration without affecting global state,
	// so we just verify the function doesn't panic
	require.NotPanics(t, func() {
		InitMetrics()
	})
}

func TestRecordHttpMetric(t *testing.T) {
	// Reset the once variable
	once = sync.Once{}

	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)

	// Verify metrics were recorded
	// Note: In a real scenario, you would collect metrics and verify them
	// For now, we just verify the function doesn't panic
}

func TestRecordHttpMetric_Duration(t *testing.T) {
	// Reset the once variable
	once = sync.Once{}

	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) {
	// Reset the once variable
	once = sync.Once{}

	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) {
	// Reset the once variable
	once = sync.Once{}

	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) {
	// Reset the once variable
	once = sync.Once{}

	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
	}
}
