package handlers

import (
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"veza-backend-api/internal/errors"
	"veza-backend-api/internal/metrics"
)

func TestAggregatedMetricsHandler_GetAggregated_AllWindows(t *testing.T) {
	gin.SetMode(gin.TestMode)
	errorMetrics := metrics.NewErrorMetrics()

	// Enregistrer quelques erreurs
	errorMetrics.RecordError(errors.ErrCodeValidation, 400)
	errorMetrics.RecordError(errors.ErrCodeNotFound, 404)

	router := gin.New()
	router.GET("/metrics/aggregated", AggregatedMetrics(errorMetrics))

	w := httptest.NewRecorder()
	req := httptest.NewRequest("GET", "/metrics/aggregated", 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)

	// Vérifier que toutes les fenêtres sont présentes
	windows, ok := response["windows"].(map[string]interface{})
	require.True(t, ok)
	assert.Contains(t, windows, "1m")
	assert.Contains(t, windows, "5m")
	assert.Contains(t, windows, "1h")
}

func TestAggregatedMetricsHandler_GetAggregated_SingleWindow(t *testing.T) {
	gin.SetMode(gin.TestMode)
	errorMetrics := metrics.NewErrorMetrics()

	// Enregistrer quelques erreurs
	errorMetrics.RecordError(errors.ErrCodeValidation, 400)
	errorMetrics.RecordError(errors.ErrCodeNotFound, 404)

	router := gin.New()
	router.GET("/metrics/aggregated", AggregatedMetrics(errorMetrics))

	w := httptest.NewRecorder()
	req := httptest.NewRequest("GET", "/metrics/aggregated?window=1m", 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 la structure de la réponse
	assert.Equal(t, "1m", response["window"])
	windows, ok := response["windows"].([]interface{})
	require.True(t, ok)
	assert.Greater(t, len(windows), 0)
}

func TestAggregatedMetricsHandler_GetAggregated_InvalidWindow(t *testing.T) {
	gin.SetMode(gin.TestMode)
	errorMetrics := metrics.NewErrorMetrics()

	router := gin.New()
	router.GET("/metrics/aggregated", AggregatedMetrics(errorMetrics))

	w := httptest.NewRecorder()
	req := httptest.NewRequest("GET", "/metrics/aggregated?window=invalid", nil)
	router.ServeHTTP(w, req)

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

	var response map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &response)
	require.NoError(t, err)
	assert.Contains(t, response["error"], "Invalid window type")
}

func TestAggregatedMetricsHandler_GetAggregated_ValidWindows(t *testing.T) {
	gin.SetMode(gin.TestMode)
	errorMetrics := metrics.NewErrorMetrics()

	router := gin.New()
	router.GET("/metrics/aggregated", AggregatedMetrics(errorMetrics))

	validWindows := []string{"1m", "5m", "1h"}
	for _, window := range validWindows {
		t.Run(window, func(t *testing.T) {
			w := httptest.NewRecorder()
			req := httptest.NewRequest("GET", "/metrics/aggregated?window="+window, 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)
			assert.Equal(t, window, response["window"])
		})
	}
}

func TestAggregatedMetricsHandler_GetAggregated_NoErrorMetrics(t *testing.T) {
	gin.SetMode(gin.TestMode)

	router := gin.New()
	router.GET("/metrics/aggregated", AggregatedMetrics(nil))

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

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

	var response map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &response)
	require.NoError(t, err)
	assert.Contains(t, response["error"], "Metrics not available")
}

func TestAggregatedMetricsHandler_WindowDataStructure(t *testing.T) {
	gin.SetMode(gin.TestMode)
	errorMetrics := metrics.NewErrorMetrics()

	// Enregistrer des erreurs
	errorMetrics.RecordError(errors.ErrCodeValidation, 400)
	errorMetrics.RecordError(errors.ErrCodeNotFound, 404)

	router := gin.New()
	router.GET("/metrics/aggregated", AggregatedMetrics(errorMetrics))

	w := httptest.NewRecorder()
	req := httptest.NewRequest("GET", "/metrics/aggregated?window=1m", 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)

	windows, ok := response["windows"].([]interface{})
	require.True(t, ok)
	require.Greater(t, len(windows), 0)

	// Vérifier la structure d'une fenêtre
	window := windows[0].(map[string]interface{})
	assert.Contains(t, window, "start")
	assert.Contains(t, window, "end")
	assert.Contains(t, window, "errors")
	assert.Contains(t, window, "requests")
	assert.Contains(t, window, "errors_by_code")
	assert.Contains(t, window, "errors_by_http_status")
}
