package logging

import (
	"os"
	"path/filepath"
	"testing"
	"time"

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

func TestNewLoggerWithRotation_Production(t *testing.T) {
	tmpDir := t.TempDir()
	logFile := filepath.Join(tmpDir, "test.log")

	logger, err := NewLoggerWithRotation("production", logFile, "INFO")
	require.NoError(t, err)
	require.NotNil(t, logger)

	// Écrire quelques logs
	for i := 0; i < 100; i++ {
		logger.Info("test log", zap.Int("iteration", i))
	}

	// Vérifier que le fichier de log existe
	_, err = os.Stat(logFile)
	assert.NoError(t, err, "Log file should exist")

	// Sync pour s'assurer que tout est écrit
	_ = logger.Sync()

	// Vérifier que le fichier n'est pas vide
	fileInfo, err := os.Stat(logFile)
	require.NoError(t, err)
	assert.Greater(t, fileInfo.Size(), int64(0), "Log file should not be empty")
}

func TestNewLoggerWithRotation_Development(t *testing.T) {
	tmpDir := t.TempDir()
	logFile := filepath.Join(tmpDir, "test-dev.log")

	logger, err := NewLoggerWithRotation("development", logFile, "DEBUG")
	require.NoError(t, err)
	require.NotNil(t, logger)

	// Écrire quelques logs
	logger.Debug("debug message", zap.String("key", "value"))
	logger.Info("info message", zap.Int("count", 42))
	logger.Warn("warn message", zap.Bool("flag", true))
	logger.Error("error message", zap.String("error", "test error"))

	// Sync pour s'assurer que tout est écrit
	_ = logger.Sync()

	// Vérifier que le fichier existe
	_, err = os.Stat(logFile)
	assert.NoError(t, err, "Log file should exist")
}

func TestNewLoggerWithRotation_ManyLogs(t *testing.T) {
	tmpDir := t.TempDir()
	logFile := filepath.Join(tmpDir, "test-many.log")

	logger, err := NewLoggerWithRotation("production", logFile, "INFO")
	require.NoError(t, err)
	require.NotNil(t, logger)

	// Écrire beaucoup de logs pour tester la rotation
	// Note: On ne peut pas facilement déclencher la rotation dans un test
	// car elle nécessite 100MB de logs, mais on peut vérifier que ça fonctionne
	for i := 0; i < 10000; i++ {
		logger.Info("test log", zap.Int("iteration", i))
	}

	// Sync pour s'assurer que tout est écrit
	_ = logger.Sync()

	// Vérifier que le fichier existe
	_, err = os.Stat(logFile)
	assert.NoError(t, err, "Log file should exist")

	// Vérifier que le fichier contient des données
	fileInfo, err := os.Stat(logFile)
	require.NoError(t, err)
	assert.Greater(t, fileInfo.Size(), int64(0), "Log file should contain logs")
}

func TestNewLoggerWithRotation_AllLogLevels(t *testing.T) {
	tmpDir := t.TempDir()
	logFile := filepath.Join(tmpDir, "test-levels.log")

	logger, err := NewLoggerWithRotation("production", logFile, "DEBUG")
	require.NoError(t, err)
	require.NotNil(t, logger)

	// Tester tous les niveaux de log
	logger.Debug("debug message")
	logger.Info("info message")
	logger.Warn("warn message")
	logger.Error("error message")

	// Sync pour s'assurer que tout est écrit
	_ = logger.Sync()

	// Vérifier que le fichier existe
	_, err = os.Stat(logFile)
	assert.NoError(t, err, "Log file should exist")
}

func TestNewLoggerWithRotation_WithFields(t *testing.T) {
	tmpDir := t.TempDir()
	logFile := filepath.Join(tmpDir, "test-fields.log")

	logger, err := NewLoggerWithRotation("production", logFile, "INFO")
	require.NoError(t, err)
	require.NotNil(t, logger)

	// Créer un logger avec des champs contextuels
	contextLogger := logger.With(
		zap.String("request_id", "req-123"),
		zap.String("user_id", "user-456"),
	)

	// Écrire des logs avec le logger contextuel
	contextLogger.Info("request processed", zap.String("action", "login"))
	contextLogger.Error("request failed", zap.String("action", "update"))

	// Sync pour s'assurer que tout est écrit
	_ = logger.Sync()

	// Vérifier que le fichier existe
	_, err = os.Stat(logFile)
	assert.NoError(t, err, "Log file should exist")
}

func TestNewLoggerWithRotation_NoDataLoss(t *testing.T) {
	tmpDir := t.TempDir()
	logFile := filepath.Join(tmpDir, "test-noloss.log")

	logger, err := NewLoggerWithRotation("production", logFile, "INFO")
	require.NoError(t, err)
	require.NotNil(t, logger)

	// Écrire des logs avec différents patterns
	messages := []string{
		"First message",
		"Second message",
		"Third message",
		"Fourth message",
		"Fifth message",
	}

	for _, msg := range messages {
		logger.Info(msg, zap.String("timestamp", time.Now().Format(time.RFC3339)))
	}

	// Sync pour s'assurer que tout est écrit
	err = logger.Sync()
	require.NoError(t, err, "Sync should not fail")

	// Vérifier que le fichier existe et contient des données
	fileInfo, err := os.Stat(logFile)
	require.NoError(t, err)
	assert.Greater(t, fileInfo.Size(), int64(0), "Log file should contain all messages")
}

func TestNewLoggerWithRotation_ConcurrentWrites(t *testing.T) {
	tmpDir := t.TempDir()
	logFile := filepath.Join(tmpDir, "test-concurrent.log")

	logger, err := NewLoggerWithRotation("production", logFile, "INFO")
	require.NoError(t, err)
	require.NotNil(t, logger)

	// Écrire des logs de manière concurrente
	done := make(chan bool, 10)
	for i := 0; i < 10; i++ {
		go func(id int) {
			for j := 0; j < 100; j++ {
				logger.Info("concurrent log", zap.Int("goroutine", id), zap.Int("iteration", j))
			}
			done <- true
		}(i)
	}

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

	// Sync pour s'assurer que tout est écrit
	_ = logger.Sync()

	// Vérifier que le fichier existe
	_, err = os.Stat(logFile)
	assert.NoError(t, err, "Log file should exist")

	// Vérifier que le fichier contient des données
	fileInfo, err := os.Stat(logFile)
	require.NoError(t, err)
	assert.Greater(t, fileInfo.Size(), int64(0), "Log file should contain logs from all goroutines")
}
