package services

import (
	"testing"

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

func TestNewBitrateStrategyService(t *testing.T) {
	logger := zaptest.NewLogger(t)
	service := NewBitrateStrategyService(logger)

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

func TestNewBitrateStrategyService_NilLogger(t *testing.T) {
	service := NewBitrateStrategyService(nil)

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

func TestBitrateStrategyService_GetThresholds_Conservative(t *testing.T) {
	logger := zaptest.NewLogger(t)
	service := NewBitrateStrategyService(logger)

	thresholds := service.GetThresholds(StrategyConservative)

	assert.Equal(t, 0.3, thresholds.BufferLevelThreshold)
	assert.Equal(t, 0.7, thresholds.BandwidthRatioThreshold)
	assert.False(t, thresholds.UseOrCondition)
}

func TestBitrateStrategyService_GetThresholds_Aggressive(t *testing.T) {
	logger := zaptest.NewLogger(t)
	service := NewBitrateStrategyService(logger)

	thresholds := service.GetThresholds(StrategyAggressive)

	assert.Equal(t, 0.15, thresholds.BufferLevelThreshold)
	assert.Equal(t, 0.5, thresholds.BandwidthRatioThreshold)
	assert.True(t, thresholds.UseOrCondition)
}

func TestBitrateStrategyService_GetThresholds_Balanced(t *testing.T) {
	logger := zaptest.NewLogger(t)
	service := NewBitrateStrategyService(logger)

	thresholds := service.GetThresholds(StrategyBalanced)

	assert.Equal(t, 0.2, thresholds.BufferLevelThreshold)
	assert.Equal(t, 0.6, thresholds.BandwidthRatioThreshold)
	assert.False(t, thresholds.UseOrCondition)
}

func TestBitrateStrategyService_GetThresholds_Default(t *testing.T) {
	logger := zaptest.NewLogger(t)
	service := NewBitrateStrategyService(logger)

	// Tester avec une stratégie invalide (devrait retourner Balanced par défaut)
	thresholds := service.GetThresholds(BitrateStrategy("invalid"))

	assert.Equal(t, 0.2, thresholds.BufferLevelThreshold)
	assert.Equal(t, 0.6, thresholds.BandwidthRatioThreshold)
	assert.False(t, thresholds.UseOrCondition)
}

func TestBitrateStrategyService_ShouldAdapt_Conservative(t *testing.T) {
	logger := zaptest.NewLogger(t)
	service := NewBitrateStrategyService(logger)

	tests := []struct {
		name           string
		bufferLevel    float64
		bandwidthRatio float64
		expected       bool
	}{
		{
			name:           "both low - should adapt",
			bufferLevel:    0.25, // < 0.3
			bandwidthRatio: 0.6,  // < 0.7
			expected:       true,
		},
		{
			name:           "buffer low but bandwidth ok - should not adapt",
			bufferLevel:    0.25, // < 0.3
			bandwidthRatio: 0.8,  // >= 0.7
			expected:       false,
		},
		{
			name:           "bandwidth low but buffer ok - should not adapt",
			bufferLevel:    0.4, // >= 0.3
			bandwidthRatio: 0.6, // < 0.7
			expected:       false,
		},
		{
			name:           "both ok - should not adapt",
			bufferLevel:    0.4, // >= 0.3
			bandwidthRatio: 0.8, // >= 0.7
			expected:       false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := service.ShouldAdapt(StrategyConservative, tt.bufferLevel, tt.bandwidthRatio)
			assert.Equal(t, tt.expected, result, "ShouldAdapt failed for %s", tt.name)
		})
	}
}

func TestBitrateStrategyService_ShouldAdapt_Aggressive(t *testing.T) {
	logger := zaptest.NewLogger(t)
	service := NewBitrateStrategyService(logger)

	tests := []struct {
		name           string
		bufferLevel    float64
		bandwidthRatio float64
		expected       bool
	}{
		{
			name:           "buffer low - should adapt",
			bufferLevel:    0.1, // < 0.15
			bandwidthRatio: 0.8, // >= 0.5
			expected:       true,
		},
		{
			name:           "bandwidth low - should adapt",
			bufferLevel:    0.3, // >= 0.15
			bandwidthRatio: 0.4, // < 0.5
			expected:       true,
		},
		{
			name:           "both low - should adapt",
			bufferLevel:    0.1, // < 0.15
			bandwidthRatio: 0.4, // < 0.5
			expected:       true,
		},
		{
			name:           "both ok - should not adapt",
			bufferLevel:    0.2, // >= 0.15
			bandwidthRatio: 0.6, // >= 0.5
			expected:       false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := service.ShouldAdapt(StrategyAggressive, tt.bufferLevel, tt.bandwidthRatio)
			assert.Equal(t, tt.expected, result, "ShouldAdapt failed for %s", tt.name)
		})
	}
}

func TestBitrateStrategyService_ShouldAdapt_Balanced(t *testing.T) {
	logger := zaptest.NewLogger(t)
	service := NewBitrateStrategyService(logger)

	tests := []struct {
		name           string
		bufferLevel    float64
		bandwidthRatio float64
		expected       bool
	}{
		{
			name:           "both low - should adapt",
			bufferLevel:    0.15, // < 0.2
			bandwidthRatio: 0.5,  // < 0.6
			expected:       true,
		},
		{
			name:           "buffer low but bandwidth ok - should not adapt",
			bufferLevel:    0.15, // < 0.2
			bandwidthRatio: 0.7,  // >= 0.6
			expected:       false,
		},
		{
			name:           "bandwidth low but buffer ok - should not adapt",
			bufferLevel:    0.3, // >= 0.2
			bandwidthRatio: 0.5, // < 0.6
			expected:       false,
		},
		{
			name:           "both ok - should not adapt",
			bufferLevel:    0.3, // >= 0.2
			bandwidthRatio: 0.7, // >= 0.6
			expected:       false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := service.ShouldAdapt(StrategyBalanced, tt.bufferLevel, tt.bandwidthRatio)
			assert.Equal(t, tt.expected, result, "ShouldAdapt failed for %s", tt.name)
		})
	}
}

func TestBitrateStrategyService_ShouldAdapt_InvalidBufferLevel(t *testing.T) {
	logger := zaptest.NewLogger(t)
	service := NewBitrateStrategyService(logger)

	// Buffer level négatif
	result := service.ShouldAdapt(StrategyBalanced, -0.1, 0.5)
	assert.False(t, result)

	// Buffer level > 1.0
	result = service.ShouldAdapt(StrategyBalanced, 1.5, 0.5)
	assert.False(t, result)
}

func TestBitrateStrategyService_ShouldAdapt_InvalidBandwidthRatio(t *testing.T) {
	logger := zaptest.NewLogger(t)
	service := NewBitrateStrategyService(logger)

	// Bandwidth ratio négatif
	result := service.ShouldAdapt(StrategyBalanced, 0.5, -0.1)
	assert.False(t, result)
}

func TestBitrateStrategyService_ShouldAdapt_EdgeCases(t *testing.T) {
	logger := zaptest.NewLogger(t)
	service := NewBitrateStrategyService(logger)

	// Buffer level exactement au seuil
	result := service.ShouldAdapt(StrategyBalanced, 0.2, 0.5)
	assert.False(t, result) // 0.2 n'est pas < 0.2

	// Buffer level juste en dessous du seuil
	result = service.ShouldAdapt(StrategyBalanced, 0.199, 0.5)
	assert.True(t, result)

	// Bandwidth ratio exactement au seuil
	result = service.ShouldAdapt(StrategyBalanced, 0.15, 0.6)
	assert.False(t, result) // 0.6 n'est pas < 0.6

	// Bandwidth ratio juste en dessous du seuil
	result = service.ShouldAdapt(StrategyBalanced, 0.15, 0.599)
	assert.True(t, result)
}

func TestBitrateStrategyService_SelectStrategy_WithUserPreference(t *testing.T) {
	logger := zaptest.NewLogger(t)
	service := NewBitrateStrategyService(logger)

	preference := StrategyAggressive
	result := service.SelectStrategy(0.5, &preference)

	assert.Equal(t, StrategyAggressive, result)
}

func TestBitrateStrategyService_SelectStrategy_UnstableNetwork(t *testing.T) {
	logger := zaptest.NewLogger(t)
	service := NewBitrateStrategyService(logger)

	// Réseau instable (< 0.3)
	result := service.SelectStrategy(0.2, nil)
	assert.Equal(t, StrategyConservative, result)

	result = service.SelectStrategy(0.0, nil)
	assert.Equal(t, StrategyConservative, result)
}

func TestBitrateStrategyService_SelectStrategy_StableNetwork(t *testing.T) {
	logger := zaptest.NewLogger(t)
	service := NewBitrateStrategyService(logger)

	// Réseau stable (> 0.7)
	result := service.SelectStrategy(0.8, nil)
	assert.Equal(t, StrategyAggressive, result)

	result = service.SelectStrategy(1.0, nil)
	assert.Equal(t, StrategyAggressive, result)
}

func TestBitrateStrategyService_SelectStrategy_ModerateNetwork(t *testing.T) {
	logger := zaptest.NewLogger(t)
	service := NewBitrateStrategyService(logger)

	// Réseau modéré (0.3 à 0.7)
	result := service.SelectStrategy(0.5, nil)
	assert.Equal(t, StrategyBalanced, result)

	result = service.SelectStrategy(0.3, nil)
	assert.Equal(t, StrategyBalanced, result)

	result = service.SelectStrategy(0.7, nil)
	assert.Equal(t, StrategyBalanced, result)
}

func TestBitrateStrategyService_IsValidStrategy(t *testing.T) {
	logger := zaptest.NewLogger(t)
	service := NewBitrateStrategyService(logger)

	assert.True(t, service.IsValidStrategy(StrategyConservative))
	assert.True(t, service.IsValidStrategy(StrategyAggressive))
	assert.True(t, service.IsValidStrategy(StrategyBalanced))
	assert.False(t, service.IsValidStrategy(BitrateStrategy("invalid")))
	assert.False(t, service.IsValidStrategy(BitrateStrategy("")))
}

func TestBitrateStrategyService_RealWorldScenarios(t *testing.T) {
	logger := zaptest.NewLogger(t)
	service := NewBitrateStrategyService(logger)

	tests := []struct {
		name           string
		strategy       BitrateStrategy
		bufferLevel    float64
		bandwidthRatio float64
		expected       bool
		description    string
	}{
		{
			name:           "conservative - good conditions",
			strategy:       StrategyConservative,
			bufferLevel:    0.5,
			bandwidthRatio: 0.9,
			expected:       false,
			description:    "Should not adapt with good buffer and bandwidth",
		},
		{
			name:           "aggressive - buffer dropping",
			strategy:       StrategyAggressive,
			bufferLevel:    0.1,
			bandwidthRatio: 0.8,
			expected:       true,
			description:    "Should adapt when buffer is dropping even with good bandwidth",
		},
		{
			name:           "balanced - moderate conditions",
			strategy:       StrategyBalanced,
			bufferLevel:    0.18,
			bandwidthRatio: 0.55,
			expected:       true,
			description:    "Should adapt when both are moderately low",
		},
		{
			name:           "conservative - critical buffer",
			strategy:       StrategyConservative,
			bufferLevel:    0.25,
			bandwidthRatio: 0.65,
			expected:       true,
			description:    "Should adapt when both are below conservative thresholds",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := service.ShouldAdapt(tt.strategy, tt.bufferLevel, tt.bandwidthRatio)
			assert.Equal(t, tt.expected, result, tt.description)
		})
	}
}
