package handlers

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

	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
	"go.uber.org/zap/zaptest"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"

	"veza-backend-api/internal/models"
	"veza-backend-api/internal/services"
)

// T0389: Create Playback Analytics Rate Limiting - Tests de rate limiting
func TestPlaybackAnalyticsHandler_RecordAnalytics_WithRateLimiting(t *testing.T) {
	db, _ := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
	db.Exec("PRAGMA foreign_keys = ON")
	db.AutoMigrate(&models.User{}, &models.Track{}, &models.PlaybackAnalytics{})

	user := &models.User{ID: 1, Username: "testuser", Email: "test@example.com", IsActive: true}
	db.Create(user)
	track := &models.Track{
		ID:       1,
		UserID:   1,
		Title:    "Test Track",
		FilePath: "/test.mp3",
		FileSize: 1024,
		Format:   "MP3",
		Duration: 180,
		IsPublic: true,
		Status:   models.TrackStatusCompleted,
	}
	db.Create(track)

	logger := zaptest.NewLogger(t)
	analyticsService := services.NewPlaybackAnalyticsService(db, logger)
	
	// Créer un rate limiter avec des limites basses pour le test
	rateLimitConfig := services.RateLimitConfig{
		RequestsPerMinute:  2, // Seulement 2 requêtes par minute
		RequestsWindow:     1 * time.Minute,
		MinRequestInterval: 10 * time.Millisecond,
		DailyQuota:         1000,
		WeeklyQuota:        5000,
	}
	rateLimiter := services.NewPlaybackAnalyticsRateLimiter(db, logger, rateLimitConfig)
	handler := NewPlaybackAnalyticsHandlerWithRateLimiter(analyticsService, rateLimiter)

	gin.SetMode(gin.TestMode)
	router := gin.New()
	protected := router.Group("/api/v1/tracks")
	protected.Use(func(c *gin.Context) {
		c.Set("user_id", int64(1))
		c.Next()
	})
	protected.POST("/:id/playback/analytics", handler.RecordAnalytics)

	now := time.Now()
	reqBody := RecordAnalyticsRequest{
		PlayTime:  120,
		StartedAt: now,
	}

	// Première requête - devrait être autorisée
	jsonBody1, _ := json.Marshal(reqBody)
	req1, _ := http.NewRequest("POST", "/api/v1/tracks/1/playback/analytics", bytes.NewBuffer(jsonBody1))
	req1.Header.Set("Content-Type", "application/json")
	w1 := httptest.NewRecorder()
	router.ServeHTTP(w1, req1)
	assert.Equal(t, http.StatusOK, w1.Code)
	assert.Contains(t, w1.Header().Get("X-RateLimit-Limit"), "2")
	assert.Contains(t, w1.Header().Get("X-RateLimit-Remaining"), "1")

	time.Sleep(20 * time.Millisecond) // Attendre pour éviter le throttling

	// Deuxième requête - devrait être autorisée
	jsonBody2, _ := json.Marshal(reqBody)
	req2, _ := http.NewRequest("POST", "/api/v1/tracks/1/playback/analytics", bytes.NewBuffer(jsonBody2))
	req2.Header.Set("Content-Type", "application/json")
	w2 := httptest.NewRecorder()
	router.ServeHTTP(w2, req2)
	assert.Equal(t, http.StatusOK, w2.Code)
	assert.Contains(t, w2.Header().Get("X-RateLimit-Remaining"), "0")

	time.Sleep(20 * time.Millisecond)

	// Troisième requête - devrait être bloquée par rate limit
	jsonBody3, _ := json.Marshal(reqBody)
	req3, _ := http.NewRequest("POST", "/api/v1/tracks/1/playback/analytics", bytes.NewBuffer(jsonBody3))
	req3.Header.Set("Content-Type", "application/json")
	w3 := httptest.NewRecorder()
	router.ServeHTTP(w3, req3)
	assert.Equal(t, http.StatusTooManyRequests, w3.Code)

	var response3 map[string]interface{}
	json.Unmarshal(w3.Body.Bytes(), &response3)
	assert.Equal(t, "Rate limit exceeded", response3["error"])
	assert.Contains(t, response3["reason"], "rate limit exceeded")
}

func TestPlaybackAnalyticsHandler_RecordAnalytics_Throttling(t *testing.T) {
	db, _ := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
	db.Exec("PRAGMA foreign_keys = ON")
	db.AutoMigrate(&models.User{}, &models.Track{}, &models.PlaybackAnalytics{})

	user := &models.User{ID: 1, Username: "testuser", Email: "test@example.com", IsActive: true}
	db.Create(user)
	track := &models.Track{
		ID:       1,
		UserID:   1,
		Title:    "Test Track",
		FilePath: "/test.mp3",
		FileSize: 1024,
		Format:   "MP3",
		Duration: 180,
		IsPublic: true,
		Status:   models.TrackStatusCompleted,
	}
	db.Create(track)

	logger := zaptest.NewLogger(t)
	analyticsService := services.NewPlaybackAnalyticsService(db, logger)
	
	// Créer un rate limiter avec un intervalle minimum élevé
	rateLimitConfig := services.RateLimitConfig{
		RequestsPerMinute:  100,
		RequestsWindow:     1 * time.Minute,
		MinRequestInterval: 200 * time.Millisecond, // 200ms minimum entre requêtes
		DailyQuota:         10000,
		WeeklyQuota:        50000,
	}
	rateLimiter := services.NewPlaybackAnalyticsRateLimiter(db, logger, rateLimitConfig)
	handler := NewPlaybackAnalyticsHandlerWithRateLimiter(analyticsService, rateLimiter)

	gin.SetMode(gin.TestMode)
	router := gin.New()
	protected := router.Group("/api/v1/tracks")
	protected.Use(func(c *gin.Context) {
		c.Set("user_id", int64(1))
		c.Next()
	})
	protected.POST("/:id/playback/analytics", handler.RecordAnalytics)

	now := time.Now()
	reqBody := RecordAnalyticsRequest{
		PlayTime:  120,
		StartedAt: now,
	}

	// Première requête - devrait être autorisée
	jsonBody1, _ := json.Marshal(reqBody)
	req1, _ := http.NewRequest("POST", "/api/v1/tracks/1/playback/analytics", bytes.NewBuffer(jsonBody1))
	req1.Header.Set("Content-Type", "application/json")
	w1 := httptest.NewRecorder()
	router.ServeHTTP(w1, req1)
	assert.Equal(t, http.StatusOK, w1.Code)

	// Deuxième requête immédiatement - devrait être bloquée par throttling
	jsonBody2, _ := json.Marshal(reqBody)
	req2, _ := http.NewRequest("POST", "/api/v1/tracks/1/playback/analytics", bytes.NewBuffer(jsonBody2))
	req2.Header.Set("Content-Type", "application/json")
	w2 := httptest.NewRecorder()
	router.ServeHTTP(w2, req2)
	assert.Equal(t, http.StatusTooManyRequests, w2.Code)

	var response2 map[string]interface{}
	json.Unmarshal(w2.Body.Bytes(), &response2)
	assert.Equal(t, "Rate limit exceeded", response2["error"])
	assert.Contains(t, response2["reason"], "throttling")
}

func TestPlaybackAnalyticsHandler_GetQuotaInfo(t *testing.T) {
	db, _ := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
	logger := zaptest.NewLogger(t)
	analyticsService := services.NewPlaybackAnalyticsService(db, logger)
	rateLimiter := services.NewPlaybackAnalyticsRateLimiter(db, logger, services.DefaultRateLimitConfig())
	handler := NewPlaybackAnalyticsHandlerWithRateLimiter(analyticsService, rateLimiter)

	gin.SetMode(gin.TestMode)
	router := gin.New()
	protected := router.Group("/api/v1/playback/analytics")
	protected.Use(func(c *gin.Context) {
		c.Set("user_id", int64(1))
		c.Next()
	})
	protected.GET("/quota", handler.GetQuotaInfo)

	req, _ := http.NewRequest("GET", "/api/v1/playback/analytics/quota", nil)
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

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

	var response map[string]interface{}
	json.Unmarshal(w.Body.Bytes(), &response)
	assert.NotNil(t, response["quota"])

	quota := response["quota"].(map[string]interface{})
	assert.NotNil(t, quota["rate_limit"])
	assert.NotNil(t, quota["throttling"])
	assert.NotNil(t, quota["quotas"])
}

func TestPlaybackAnalyticsHandler_GetQuotaInfo_NotEnabled(t *testing.T) {
	db, _ := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
	logger := zaptest.NewLogger(t)
	analyticsService := services.NewPlaybackAnalyticsService(db, logger)
	handler := NewPlaybackAnalyticsHandler(analyticsService) // Sans rate limiter

	gin.SetMode(gin.TestMode)
	router := gin.New()
	protected := router.Group("/api/v1/playback/analytics")
	protected.Use(func(c *gin.Context) {
		c.Set("user_id", int64(1))
		c.Next()
	})
	protected.GET("/quota", handler.GetQuotaInfo)

	req, _ := http.NewRequest("GET", "/api/v1/playback/analytics/quota", nil)
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

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

	var response map[string]interface{}
	json.Unmarshal(w.Body.Bytes(), &response)
	assert.Equal(t, "rate limiting not enabled", response["error"])
}

