package services

import (
	"context"
	"testing"

	"github.com/stretchr/testify/assert"
	"go.uber.org/zap/zaptest"
)

func TestNewBufferMonitorService(t *testing.T) {
	logger := zaptest.NewLogger(t)
	service := NewBufferMonitorService(logger)

	assert.NotNil(t, service)
	assert.Equal(t, 0.2, service.lowThreshold)
	assert.Equal(t, 0.8, service.highThreshold)
	assert.NotNil(t, service.logger)
}

func TestNewBufferMonitorService_NilLogger(t *testing.T) {
	service := NewBufferMonitorService(nil)

	assert.NotNil(t, service)
	assert.NotNil(t, service.logger)
}

func TestBufferMonitorService_CalculateBufferLevel(t *testing.T) {
	logger := zaptest.NewLogger(t)
	service := NewBufferMonitorService(logger)

	// Test normal: 10 secondes buffered sur 100 secondes = 0.1 (10%)
	level := service.CalculateBufferLevel(10.0, 100.0)
	assert.Equal(t, 0.1, level)

	// Test buffer plein: 100 secondes buffered sur 100 secondes = 1.0 (100%)
	level = service.CalculateBufferLevel(100.0, 100.0)
	assert.Equal(t, 1.0, level)

	// Test buffer vide: 0 secondes buffered sur 100 secondes = 0.0 (0%)
	level = service.CalculateBufferLevel(0.0, 100.0)
	assert.Equal(t, 0.0, level)

	// Test buffer partiel: 50 secondes buffered sur 100 secondes = 0.5 (50%)
	level = service.CalculateBufferLevel(50.0, 100.0)
	assert.Equal(t, 0.5, level)
}

func TestBufferMonitorService_CalculateBufferLevel_EdgeCases(t *testing.T) {
	logger := zaptest.NewLogger(t)
	service := NewBufferMonitorService(logger)

	// Test avec duration = 0
	level := service.CalculateBufferLevel(10.0, 0.0)
	assert.Equal(t, 0.0, level)

	// Test avec duration négative
	level = service.CalculateBufferLevel(10.0, -10.0)
	assert.Equal(t, 0.0, level)

	// Test avec buffered négatif
	level = service.CalculateBufferLevel(-10.0, 100.0)
	assert.Equal(t, 0.0, level)

	// Test avec buffered > duration (devrait être limité à 1.0)
	level = service.CalculateBufferLevel(150.0, 100.0)
	assert.Equal(t, 1.0, level)

	// Test avec très petites valeurs
	level = service.CalculateBufferLevel(0.1, 1.0)
	assert.Equal(t, 0.1, level)
}

func TestBufferMonitorService_IsBufferLow(t *testing.T) {
	logger := zaptest.NewLogger(t)
	service := NewBufferMonitorService(logger)

	// Test buffer faible (< 0.2)
	assert.True(t, service.IsBufferLow(0.1))
	assert.True(t, service.IsBufferLow(0.15))
	assert.True(t, service.IsBufferLow(0.0))

	// Test buffer normal (>= 0.2)
	assert.False(t, service.IsBufferLow(0.2))
	assert.False(t, service.IsBufferLow(0.5))
	assert.False(t, service.IsBufferLow(0.8))
}

func TestBufferMonitorService_IsBufferHigh(t *testing.T) {
	logger := zaptest.NewLogger(t)
	service := NewBufferMonitorService(logger)

	// Test buffer élevé (> 0.8)
	assert.True(t, service.IsBufferHigh(0.9))
	assert.True(t, service.IsBufferHigh(0.85))
	assert.True(t, service.IsBufferHigh(1.0))

	// Test buffer normal (<= 0.8)
	assert.False(t, service.IsBufferHigh(0.8))
	assert.False(t, service.IsBufferHigh(0.5))
	assert.False(t, service.IsBufferHigh(0.2))
}

func TestBufferMonitorService_ShouldAdaptBuffer(t *testing.T) {
	logger := zaptest.NewLogger(t)
	service := NewBufferMonitorService(logger)

	// Test buffer faible - devrait adapter
	assert.True(t, service.ShouldAdaptBuffer(0.1))
	assert.True(t, service.ShouldAdaptBuffer(0.0))
	assert.True(t, service.ShouldAdaptBuffer(0.15))

	// Test buffer élevé - devrait adapter
	assert.True(t, service.ShouldAdaptBuffer(0.9))
	assert.True(t, service.ShouldAdaptBuffer(1.0))
	assert.True(t, service.ShouldAdaptBuffer(0.85))

	// Test buffer normal - ne devrait pas adapter
	assert.False(t, service.ShouldAdaptBuffer(0.3))
	assert.False(t, service.ShouldAdaptBuffer(0.5))
	assert.False(t, service.ShouldAdaptBuffer(0.7))

	// Test aux limites
	assert.False(t, service.ShouldAdaptBuffer(0.2)) // Exactement au seuil bas
	assert.False(t, service.ShouldAdaptBuffer(0.8)) // Exactement au seuil haut
}

func TestBufferMonitorService_GetBufferStatus(t *testing.T) {
	logger := zaptest.NewLogger(t)
	service := NewBufferMonitorService(logger)

	// Test buffer faible
	assert.Equal(t, "low", service.GetBufferStatus(0.1))
	assert.Equal(t, "low", service.GetBufferStatus(0.0))
	assert.Equal(t, "low", service.GetBufferStatus(0.15))

	// Test buffer élevé
	assert.Equal(t, "high", service.GetBufferStatus(0.9))
	assert.Equal(t, "high", service.GetBufferStatus(1.0))
	assert.Equal(t, "high", service.GetBufferStatus(0.85))

	// Test buffer normal
	assert.Equal(t, "normal", service.GetBufferStatus(0.3))
	assert.Equal(t, "normal", service.GetBufferStatus(0.5))
	assert.Equal(t, "normal", service.GetBufferStatus(0.7))
	assert.Equal(t, "normal", service.GetBufferStatus(0.2)) // Limite basse
	assert.Equal(t, "normal", service.GetBufferStatus(0.8)) // Limite haute
}

func TestBufferMonitorService_MonitorBuffer(t *testing.T) {
	logger := zaptest.NewLogger(t)
	service := NewBufferMonitorService(logger)
	ctx := context.Background()

	// Test avec buffer faible
	bufferLevel, shouldAdapt, status := service.MonitorBuffer(ctx, 10.0, 100.0)
	assert.Equal(t, 0.1, bufferLevel)
	assert.True(t, shouldAdapt)
	assert.Equal(t, "low", status)

	// Test avec buffer normal
	bufferLevel, shouldAdapt, status = service.MonitorBuffer(ctx, 50.0, 100.0)
	assert.Equal(t, 0.5, bufferLevel)
	assert.False(t, shouldAdapt)
	assert.Equal(t, "normal", status)

	// Test avec buffer élevé
	bufferLevel, shouldAdapt, status = service.MonitorBuffer(ctx, 90.0, 100.0)
	assert.Equal(t, 0.9, bufferLevel)
	assert.True(t, shouldAdapt)
	assert.Equal(t, "high", status)
}

func TestBufferMonitorService_SetThresholds(t *testing.T) {
	logger := zaptest.NewLogger(t)
	service := NewBufferMonitorService(logger)

	// Vérifier les valeurs par défaut
	low, high := service.GetThresholds()
	assert.Equal(t, 0.2, low)
	assert.Equal(t, 0.8, high)

	// Définir de nouveaux seuils
	service.SetThresholds(0.15, 0.85)
	low, high = service.GetThresholds()
	assert.Equal(t, 0.15, low)
	assert.Equal(t, 0.85, high)

	// Test avec valeurs invalides (devrait ignorer)
	service.SetThresholds(-0.1, 1.5)
	low, high = service.GetThresholds()
	// Les valeurs précédentes devraient être conservées
	assert.Equal(t, 0.15, low)
	assert.Equal(t, 0.85, high)

	// Test avec high <= low (devrait ignorer high)
	service.SetThresholds(0.3, 0.2)
	low, high = service.GetThresholds()
	assert.Equal(t, 0.3, low)
	// high devrait rester à 0.85 car 0.2 <= 0.3
	assert.Equal(t, 0.85, high)
}

func TestBufferMonitorService_GetThresholds(t *testing.T) {
	logger := zaptest.NewLogger(t)
	service := NewBufferMonitorService(logger)

	low, high := service.GetThresholds()
	assert.Equal(t, 0.2, low)
	assert.Equal(t, 0.8, high)

	// Modifier les seuils
	service.SetThresholds(0.1, 0.9)
	low, high = service.GetThresholds()
	assert.Equal(t, 0.1, low)
	assert.Equal(t, 0.9, high)
}

func TestBufferMonitorService_ConcurrentAccess(t *testing.T) {
	logger := zaptest.NewLogger(t)
	service := NewBufferMonitorService(logger)

	// Test d'accès concurrent
	done := make(chan bool, 10)

	for i := 0; i < 10; i++ {
		go func(index int) {
			bufferLevel := float64(index) / 10.0
			service.IsBufferLow(bufferLevel)
			service.IsBufferHigh(bufferLevel)
			service.ShouldAdaptBuffer(bufferLevel)
			service.GetBufferStatus(bufferLevel)
			service.SetThresholds(0.2+float64(index)/100.0, 0.8-float64(index)/100.0)
			service.GetThresholds()
			done <- true
		}(i)
	}

	// Attendre que toutes les goroutines se terminent
	for i := 0; i < 10; i++ {
		<-done
	}

	// Le service devrait toujours être dans un état cohérent
	low, high := service.GetThresholds()
	assert.GreaterOrEqual(t, low, 0.0)
	assert.LessOrEqual(t, high, 1.0)
	assert.Less(t, low, high)
}

func TestBufferMonitorService_RealWorldScenarios(t *testing.T) {
	logger := zaptest.NewLogger(t)
	service := NewBufferMonitorService(logger)
	ctx := context.Background()

	// Scénario 1: Buffer très faible (5 secondes sur 180 secondes)
	bufferLevel, shouldAdapt, status := service.MonitorBuffer(ctx, 5.0, 180.0)
	assert.InDelta(t, 0.027, bufferLevel, 0.001)
	assert.True(t, shouldAdapt)
	assert.Equal(t, "low", status)

	// Scénario 2: Buffer normal (60 secondes sur 180 secondes)
	bufferLevel, shouldAdapt, status = service.MonitorBuffer(ctx, 60.0, 180.0)
	assert.InDelta(t, 0.333, bufferLevel, 0.001)
	assert.False(t, shouldAdapt)
	assert.Equal(t, "normal", status)

	// Scénario 3: Buffer élevé (160 secondes sur 180 secondes)
	bufferLevel, shouldAdapt, status = service.MonitorBuffer(ctx, 160.0, 180.0)
	assert.InDelta(t, 0.888, bufferLevel, 0.001)
	assert.True(t, shouldAdapt)
	assert.Equal(t, "high", status)
}

func TestBufferMonitorService_CalculateBufferLevel_Precision(t *testing.T) {
	logger := zaptest.NewLogger(t)
	service := NewBufferMonitorService(logger)

	// Test avec des valeurs précises
	level := service.CalculateBufferLevel(33.333, 100.0)
	assert.InDelta(t, 0.33333, level, 0.0001)

	// Test avec des valeurs très petites
	level = service.CalculateBufferLevel(0.001, 1.0)
	assert.Equal(t, 0.001, level)

	// Test avec des valeurs très grandes
	level = service.CalculateBufferLevel(1000.0, 100.0)
	assert.Equal(t, 1.0, level) // Devrait être limité à 1.0
}
