package services

import (
	"context"
	"testing"
	"time"

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

func TestNewBandwidthDetectionService(t *testing.T) {
	logger := zaptest.NewLogger(t)
	service := NewBandwidthDetectionService(logger)

	assert.NotNil(t, service)
	assert.NotNil(t, service.samples)
	assert.Equal(t, 10, service.maxSamples)
	assert.Equal(t, 0, len(service.samples))
}

func TestNewBandwidthDetectionService_NilLogger(t *testing.T) {
	service := NewBandwidthDetectionService(nil)

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

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

	// Test mesure de bande passante: 1 MB en 1 seconde = 8 Mbps = 8000000 bps
	bytesTransferred := int64(1024 * 1024) // 1 MB
	duration := time.Second

	bandwidth := service.MeasureBandwidth(ctx, bytesTransferred, duration)

	assert.Equal(t, int64(8388608), bandwidth) // 1 MB * 8 bits / 1 second = 8388608 bps
	assert.Equal(t, 1, service.GetSampleCount())
}

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

	// Ajouter plusieurs échantillons
	service.MeasureBandwidth(ctx, 1024*1024, time.Second)   // ~8 Mbps
	service.MeasureBandwidth(ctx, 2*1024*1024, time.Second) // ~16 Mbps
	service.MeasureBandwidth(ctx, 3*1024*1024, time.Second) // ~24 Mbps

	assert.Equal(t, 3, service.GetSampleCount())

	// La moyenne devrait être environ (8 + 16 + 24) / 3 = 16 Mbps
	avgBandwidth := service.GetAverageBandwidth()
	assert.Greater(t, avgBandwidth, int64(15000000)) // ~15 Mbps
	assert.Less(t, avgBandwidth, int64(17000000))    // ~17 Mbps
}

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

	// Ajouter plus de 10 échantillons (maxSamples = 10)
	for i := 0; i < 15; i++ {
		service.MeasureBandwidth(ctx, int64(1024*1024*(i+1)), time.Second)
	}

	// Le nombre d'échantillons ne devrait pas dépasser maxSamples
	assert.Equal(t, 10, service.GetSampleCount())
}

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

	// Test avec durée nulle
	bandwidth := service.MeasureBandwidth(ctx, 1024*1024, 0)
	assert.Equal(t, int64(0), bandwidth)

	// Test avec durée négative
	bandwidth = service.MeasureBandwidth(ctx, 1024*1024, -time.Second)
	assert.Equal(t, int64(0), bandwidth)
}

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

	// Test avec bytes négatifs
	bandwidth := service.MeasureBandwidth(ctx, -1024, time.Second)
	assert.Equal(t, int64(0), bandwidth)
}

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

	// Test avec une durée très courte (1 milliseconde)
	bytesTransferred := int64(1024) // 1 KB
	duration := time.Millisecond

	bandwidth := service.MeasureBandwidth(ctx, bytesTransferred, duration)

	// 1 KB * 8 bits / 0.001 second = 8 Mbps = 8000000 bps
	assert.Greater(t, bandwidth, int64(7000000))
	assert.Less(t, bandwidth, int64(9000000))
}

func TestBandwidthDetectionService_CalculateAverage_EmptySamples(t *testing.T) {
	logger := zaptest.NewLogger(t)
	service := NewBandwidthDetectionService(logger)

	avg := service.GetAverageBandwidth()
	assert.Equal(t, int64(0), avg)
}

func TestBandwidthDetectionService_RecommendBitrate(t *testing.T) {
	logger := zaptest.NewLogger(t)
	service := NewBandwidthDetectionService(logger)

	// Test avec bande passante élevée (>= 400 kbps avec buffer)
	// 400 kbps * 1.25 (pour compenser le buffer 20%) = 500 kbps = 500000 bps
	bitrate := service.RecommendBitrate(500000)
	assert.Equal(t, 320, bitrate)

	// Test avec bande passante moyenne (>= 240 kbps avec buffer)
	// 240 kbps * 1.25 = 300 kbps = 300000 bps
	bitrate = service.RecommendBitrate(300000)
	assert.Equal(t, 192, bitrate)

	// Test avec bande passante faible (>= 160 kbps avec buffer)
	// 160 kbps * 1.25 = 200 kbps = 200000 bps
	bitrate = service.RecommendBitrate(200000)
	assert.Equal(t, 128, bitrate)

	// Test avec bande passante très faible (< 160 kbps avec buffer)
	bitrate = service.RecommendBitrate(100000)
	assert.Equal(t, 128, bitrate)
}

func TestBandwidthDetectionService_RecommendBitrate_EdgeCases(t *testing.T) {
	logger := zaptest.NewLogger(t)
	service := NewBandwidthDetectionService(logger)

	// Test avec bande passante nulle
	bitrate := service.RecommendBitrate(0)
	assert.Equal(t, 128, bitrate)

	// Test avec bande passante négative
	bitrate = service.RecommendBitrate(-1000)
	assert.Equal(t, 128, bitrate)

	// Test avec bande passante exactement à la limite (320 kbps)
	// 320 kbps * 1.25 = 400 kbps = 400000 bps
	bitrate = service.RecommendBitrate(400000)
	assert.Equal(t, 320, bitrate)

	// Test avec bande passante juste en dessous de 320 kbps
	bitrate = service.RecommendBitrate(399999)
	assert.Equal(t, 192, bitrate)

	// Test avec bande passante exactement à la limite (192 kbps)
	// 192 kbps * 1.25 = 240 kbps = 240000 bps
	bitrate = service.RecommendBitrate(240000)
	assert.Equal(t, 192, bitrate)

	// Test avec bande passante juste en dessous de 192 kbps
	bitrate = service.RecommendBitrate(239999)
	assert.Equal(t, 128, bitrate)

	// Test avec bande passante exactement à la limite (128 kbps)
	// 128 kbps * 1.25 = 160 kbps = 160000 bps
	bitrate = service.RecommendBitrate(160000)
	assert.Equal(t, 128, bitrate)
}

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

	// Ajouter quelques échantillons
	service.MeasureBandwidth(ctx, 1024*1024, time.Second)
	service.MeasureBandwidth(ctx, 2*1024*1024, time.Second)

	assert.Equal(t, 2, service.GetSampleCount())

	// Effacer les échantillons
	service.ClearSamples()

	assert.Equal(t, 0, service.GetSampleCount())
	assert.Equal(t, int64(0), service.GetAverageBandwidth())
}

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

	assert.Equal(t, 0, service.GetSampleCount())

	service.MeasureBandwidth(ctx, 1024*1024, time.Second)
	assert.Equal(t, 1, service.GetSampleCount())

	service.MeasureBandwidth(ctx, 2*1024*1024, time.Second)
	assert.Equal(t, 2, service.GetSampleCount())
}

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

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

	for i := 0; i < 10; i++ {
		go func(index int) {
			service.MeasureBandwidth(ctx, int64(1024*1024*(index+1)), time.Second)
			service.GetAverageBandwidth()
			service.GetSampleCount()
			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
	assert.LessOrEqual(t, service.GetSampleCount(), 10)
	assert.Greater(t, service.GetAverageBandwidth(), int64(0))
}

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

	// Scénario 1: Connexion rapide (10 Mbps)
	// 10 Mbps = 10 * 1024 * 1024 / 8 = 1310720 bytes/s
	// En 1 seconde: 1310720 bytes
	// Bande passante mesurée: 1310720 * 8 = 10485760 bps = 10 Mbps
	// Avec buffer 20%: 10485760 * 0.8 = 8388608 bps = 8388 kbps > 320 kbps
	service.MeasureBandwidth(ctx, 1310720, time.Second)
	bitrate := service.RecommendBitrate(service.GetAverageBandwidth())
	assert.Equal(t, 320, bitrate)

	// Scénario 2: Connexion moyenne (2 Mbps)
	// 2 Mbps = 2 * 1024 * 1024 / 8 = 262144 bytes/s
	// Bande passante mesurée: 262144 * 8 = 2097152 bps = 2 Mbps
	// Avec buffer 20%: 2097152 * 0.8 = 1677721 bps = 1677 kbps > 320 kbps
	// Donc on recommande 320 kbps (pas 192)
	service.ClearSamples()
	service.MeasureBandwidth(ctx, 262144, time.Second)
	bitrate = service.RecommendBitrate(service.GetAverageBandwidth())
	assert.Equal(t, 320, bitrate)

	// Scénario 3: Connexion lente (300 kbps)
	// 300 kbps = 300 * 1024 / 8 = 38400 bytes/s
	// Bande passante mesurée: 38400 * 8 = 307200 bps = 300 kbps
	// Avec buffer 20%: 307200 * 0.8 = 245760 bps = 245 kbps
	// 245 kbps >= 192 kbps, donc on recommande 192 kbps
	service.ClearSamples()
	service.MeasureBandwidth(ctx, 38400, time.Second)
	bitrate = service.RecommendBitrate(service.GetAverageBandwidth())
	assert.Equal(t, 192, bitrate)

	// Scénario 4: Connexion très lente (150 kbps)
	// 150 kbps = 150 * 1024 / 8 = 19200 bytes/s
	// Bande passante mesurée: 19200 * 8 = 153600 bps = 150 kbps
	// Avec buffer 20%: 153600 * 0.8 = 122880 bps = 122 kbps < 128 kbps
	// Donc on recommande 128 kbps
	service.ClearSamples()
	service.MeasureBandwidth(ctx, 19200, time.Second)
	bitrate = service.RecommendBitrate(service.GetAverageBandwidth())
	assert.Equal(t, 128, bitrate)
}
