package logging

import (
	"testing"
	"time"

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

// BenchmarkLogging_Optimized benchmark le logger optimisé
func BenchmarkLogging_Optimized(b *testing.B) {
	logger, err := NewOptimizedLogger("production", "INFO")
	require.NoError(b, err)
	defer logger.Sync()

	b.ResetTimer()
	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			logger.Info("test message",
				zap.String("key", "value"),
				zap.Int("count", 42),
			)
		}
	})
}

// BenchmarkLogging_Standard benchmark le logger standard (pour comparaison)
func BenchmarkLogging_Standard(b *testing.B) {
	logger, err := NewLogger("production", "INFO")
	require.NoError(b, err)
	defer logger.Sync()

	b.ResetTimer()
	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			logger.Info("test message",
				zap.String("key", "value"),
				zap.Int("count", 42),
			)
		}
	})
}

// TestOptimizedLogger_Performance teste que le logger optimisé atteint < 1ms par log
func TestOptimizedLogger_Performance(t *testing.T) {
	logger, err := NewOptimizedLogger("production", "INFO")
	require.NoError(t, err)
	defer logger.Sync()

	iterations := 1000
	start := time.Now()

	for i := 0; i < iterations; i++ {
		logger.Info("test message",
			zap.String("key", "value"),
			zap.Int("iteration", i),
		)
	}

	// Sync pour s'assurer que tous les logs sont écrits
	// Note: Sync() peut retourner une erreur sur stdout/stderr sur certains systèmes, c'est OK
	_ = logger.Sync()

	duration := time.Since(start)
	avgDuration := duration / time.Duration(iterations)

	// Vérifier que la moyenne est < 1ms par log
	assert.Less(t, avgDuration, 1*time.Millisecond,
		"Average log time should be < 1ms, got %v", avgDuration)
}

// TestOptimizedLogger_HighLoad teste la performance avec 10K logs/seconde
func TestOptimizedLogger_HighLoad(t *testing.T) {
	logger, err := NewOptimizedLogger("production", "INFO")
	require.NoError(t, err)
	defer logger.Sync()

	duration := 1 * time.Second
	iteration := 0
	done := make(chan struct{})

	// Goroutine qui envoie des logs rapidement
	go func() {
		endTime := time.Now().Add(duration)
		for time.Now().Before(endTime) {
			logger.Info("high load test",
				zap.String("test", "high_load"),
				zap.Int("iteration", iteration),
			)
			iteration++
		}
		close(done)
	}()

	// Attendre la fin
	<-done

	// Sync pour s'assurer que tous les logs sont écrits
	// Note: Sync() peut retourner une erreur sur stdout/stderr sur certains systèmes, c'est OK
	_ = logger.Sync()

	// Le système devrait pouvoir gérer cette charge sans bloquer
	// Avec sampling activé, certains logs peuvent être filtrés, c'est normal
	// On vérifie juste qu'il n'y a pas eu de panique et que le système répond
}

// TestOptimizedLogger_Sampling teste que le sampling fonctionne correctement
func TestOptimizedLogger_Sampling(t *testing.T) {
	logger, err := NewOptimizedLogger("production", "INFO")
	require.NoError(t, err)
	defer logger.Sync()

	// Envoyer beaucoup de logs rapidement
	// Avec sampling Initial:100, Thereafter:100, on devrait voir une réduction après 100 logs
	for i := 0; i < 500; i++ {
		logger.Info("sampling test",
			zap.Int("iteration", i),
		)
	}

	// Sync pour s'assurer que tous les logs sont écrits
	// Note: Sync() peut retourner une erreur sur stdout/stderr sur certains systèmes, c'est OK
	_ = logger.Sync()

	// Le sampling devrait être actif sans erreur
	// On vérifie juste que ça ne panique pas et que le logger fonctionne
}

// TestOptimizedLogger_Concurrent teste que le logger peut gérer des logs concurrents
func TestOptimizedLogger_Concurrent(t *testing.T) {
	logger, err := NewOptimizedLogger("production", "INFO")
	require.NoError(t, err)
	defer logger.Sync()

	goroutines := 10
	logsPerGoroutine := 100

	done := make(chan struct{}, goroutines)

	for i := 0; i < goroutines; i++ {
		go func(id int) {
			for j := 0; j < logsPerGoroutine; j++ {
				logger.Info("concurrent test",
					zap.Int("goroutine", id),
					zap.Int("iteration", j),
				)
			}
			done <- struct{}{}
		}(i)
	}

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

	// Sync pour s'assurer que tous les logs sont écrits
	// Note: Sync() peut retourner une erreur sur stdout/stderr sur certains systèmes, c'est OK
	_ = logger.Sync()

	// Le logger devrait gérer les logs concurrents sans problème
	// On vérifie juste qu'il n'y a pas eu de panique
}

// TestOptimizedLogger_WithRotation teste le logger optimisé avec rotation
func TestOptimizedLogger_WithRotation(t *testing.T) {
	tmpDir := t.TempDir()
	logFile := tmpDir + "/optimized.log"

	logger, err := NewOptimizedLoggerWithRotation("production", logFile, "INFO")
	require.NoError(t, err)
	defer logger.Sync()

	// Envoyer des logs
	for i := 0; i < 100; i++ {
		logger.Info("optimized rotation test",
			zap.Int("iteration", i),
		)
	}

	// Sync pour s'assurer que tous les logs sont écrits
	err = logger.Sync()
	require.NoError(t, err)

	// Vérifier que le fichier existe et contient des données
	// (le buffering async peut prendre un peu de temps)
	time.Sleep(200 * time.Millisecond)

	// Le logger devrait fonctionner avec rotation
	assert.NoError(t, err)
}

// BenchmarkLogging_OptimizedWithRotation benchmark le logger optimisé avec rotation
func BenchmarkLogging_OptimizedWithRotation(b *testing.B) {
	tmpDir := b.TempDir()
	logFile := tmpDir + "/bench.log"

	logger, err := NewOptimizedLoggerWithRotation("production", logFile, "INFO")
	require.NoError(b, err)
	defer logger.Sync()

	b.ResetTimer()
	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			logger.Info("benchmark message",
				zap.String("key", "value"),
				zap.Int("count", 42),
			)
		}
	})
}
