package handlers

import (
	"encoding/json"
	"github.com/google/uuid"
	"net/http"
	"net/http/httptest"
	"testing"

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

func TestSystemMetrics(t *testing.T) {
	gin.SetMode(gin.TestMode)
	router := gin.New()
	router.GET("/system/metrics", SystemMetrics)

	w := httptest.NewRecorder()
	req := httptest.NewRequest("GET", "/system/metrics", nil)
	router.ServeHTTP(w, req)

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

	body := w.Body.String()
	assert.Contains(t, body, "memory")
	assert.Contains(t, body, "goroutines")
	assert.Contains(t, body, "cpu_count")
	assert.Contains(t, body, "timestamp")
}

func TestSystemMetrics_JSONFormat(t *testing.T) {
	gin.SetMode(gin.TestMode)
	router := gin.New()
	router.GET("/system/metrics", SystemMetrics)

	w := httptest.NewRecorder()
	req := httptest.NewRequest("GET", "/system/metrics", nil)
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)
	assert.Contains(t, w.Header().Get("Content-Type"), "application/json")

	var response map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &response)
	require.NoError(t, err, "Response should be valid JSON")

	// Vérifier la structure
	assert.Contains(t, response, "timestamp")
	assert.Contains(t, response, "memory")
	assert.Contains(t, response, "goroutines")
	assert.Contains(t, response, "cpu_count")
}

func TestSystemMetrics_MemoryMetrics(t *testing.T) {
	gin.SetMode(gin.TestMode)
	router := gin.New()
	router.GET("/system/metrics", SystemMetrics)

	w := httptest.NewRecorder()
	req := httptest.NewRequest("GET", "/system/metrics", nil)
	router.ServeHTTP(w, req)

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

	var response map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &response)
	require.NoError(t, err)

	// Vérifier les métriques mémoire
	memory, ok := response["memory"].(map[string]interface{})
	require.True(t, ok, "Memory should be an object")

	assert.Contains(t, memory, "alloc_mb")
	assert.Contains(t, memory, "total_alloc_mb")
	assert.Contains(t, memory, "sys_mb")
	assert.Contains(t, memory, "num_gc")

	// Vérifier que les valeurs sont des nombres
	assert.NotNil(t, memory["alloc_mb"])
	assert.NotNil(t, memory["total_alloc_mb"])
	assert.NotNil(t, memory["sys_mb"])
	assert.NotNil(t, memory["num_gc"])
}

func TestSystemMetrics_Goroutines(t *testing.T) {
	gin.SetMode(gin.TestMode)
	router := gin.New()
	router.GET("/system/metrics", SystemMetrics)

	w := httptest.NewRecorder()
	req := httptest.NewRequest("GET", "/system/metrics", nil)
	router.ServeHTTP(w, req)

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

	var response map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &response)
	require.NoError(t, err)

	// Vérifier que goroutines est présent et est un nombre
	goroutines, ok := response["goroutines"]
	require.True(t, ok, "Goroutines should be present")

	goroutinesNum, ok := goroutines.(float64)
	require.True(t, ok, "Goroutines should be a number")
	assert.Greater(t, goroutinesNum, float64(0), "Should have at least one goroutine")
}

func TestSystemMetrics_CPUCount(t *testing.T) {
	gin.SetMode(gin.TestMode)
	router := gin.New()
	router.GET("/system/metrics", SystemMetrics)

	w := httptest.NewRecorder()
	req := httptest.NewRequest("GET", "/system/metrics", nil)
	router.ServeHTTP(w, req)

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

	var response map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &response)
	require.NoError(t, err)

	// Vérifier que cpu_count est présent et est un nombre
	cpuCount, ok := response["cpu_count"]
	require.True(t, ok, "CPU count should be present")

	cpuCountNum, ok := cpuCount.(float64)
	require.True(t, ok, "CPU count should be a number")
	assert.Greater(t, cpuCountNum, float64(0), "Should have at least one CPU")
}

func TestSystemMetrics_Timestamp(t *testing.T) {
	gin.SetMode(gin.TestMode)
	router := gin.New()
	router.GET("/system/metrics", SystemMetrics)

	w := httptest.NewRecorder()
	req := httptest.NewRequest("GET", "/system/metrics", nil)
	router.ServeHTTP(w, req)

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

	var response map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &response)
	require.NoError(t, err)

	// Vérifier que timestamp est présent et est un nombre
	timestamp, ok := response["timestamp"]
	require.True(t, ok, "Timestamp should be present")

	timestampNum, ok := timestamp.(float64)
	require.True(t, ok, "Timestamp should be a number")
	assert.Greater(t, timestampNum, float64(0), "Timestamp should be positive")
}

func TestSystemMetrics_MultipleRequests(t *testing.T) {
	gin.SetMode(gin.TestMode)
	router := gin.New()
	router.GET("/system/metrics", SystemMetrics)

	// Faire plusieurs requêtes et vérifier que les métriques changent
	var timestamps []float64
	for i := 0; i < 3; i++ {
		w := httptest.NewRecorder()
		req := httptest.NewRequest("GET", "/system/metrics", nil)
		router.ServeHTTP(w, req)

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

		var response map[string]interface{}
		err := json.Unmarshal(w.Body.Bytes(), &response)
		require.NoError(t, err)

		timestamp := response["timestamp"].(float64)
		timestamps = append(timestamps, timestamp)
	}

	// Les timestamps devraient être différents (ou au moins l'un devrait être différent)
	// Mais ils pourraient être identiques si les requêtes sont très rapides
	// On vérifie juste qu'ils sont tous valides
	for _, ts := range timestamps {
		assert.Greater(t, ts, float64(0))
	}
}

func TestBToMb(t *testing.T) {
	// Tester la conversion bytes vers megabytes
	assert.Equal(t, uint64(0), bToMb(0))
	assert.Equal(t, uint64(0), bToMb(1024*1024-1))
	assert.Equal(t, uint64(1), bToMb(1024*1024))
	assert.Equal(t, uint64(2), bToMb(2*1024*1024))
	assert.Equal(t, uint64(100), bToMb(100*1024*1024))
}
