package config

import (
	"os"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestNewTestConfig(t *testing.T) {
	config := NewTestConfig(t)

	// Vérifier les valeurs par défaut
	assert.Equal(t, 8080, config.AppPort)
	assert.Equal(t, "test-jwt-secret-key-minimum-32-characters-long", config.JWTSecret)
	assert.Equal(t, "postgres://test:test@localhost:5432/test_db", config.DatabaseURL)
	assert.Equal(t, "redis://localhost:6379/0", config.RedisURL)
	assert.Equal(t, []string{"*"}, config.CORSOrigins)
	assert.Equal(t, 100, config.RateLimitLimit)
	assert.Equal(t, 60, config.RateLimitWindow)
	assert.Equal(t, "DEBUG", config.LogLevel)
	assert.NotNil(t, config.Logger)

	// Vérifier que la config est valide (selon les règles de validation)
	// Note: Pour un test complet, on devrait tester que Validate() passe
	// mais NewTestConfig ne crée pas une config complète avec DB/Redis
}

func TestWithEnv(t *testing.T) {
	// Sauvegarder la valeur originale si elle existe
	originalValue := os.Getenv("TEST_VAR")
	defer func() {
		if originalValue != "" {
			os.Setenv("TEST_VAR", originalValue)
		} else {
			os.Unsetenv("TEST_VAR")
		}
	}()

	// Tester avec une variable qui n'existe pas
	os.Unsetenv("TEST_VAR")
	reset := WithEnv("TEST_VAR", "test_value")

	// Vérifier que la valeur est définie
	assert.Equal(t, "test_value", os.Getenv("TEST_VAR"))

	// Nettoyer
	reset()
	assert.Empty(t, os.Getenv("TEST_VAR"))

	// Tester avec une variable qui existe déjà
	os.Setenv("TEST_VAR", "original_value")
	reset2 := WithEnv("TEST_VAR", "new_value")
	defer reset2()

	// Vérifier que la nouvelle valeur est définie
	assert.Equal(t, "new_value", os.Getenv("TEST_VAR"))

	// Nettoyer et vérifier que l'ancienne valeur est restaurée
	reset2()
	assert.Equal(t, "original_value", os.Getenv("TEST_VAR"))
}

func TestWithEnv_MultipleCalls(t *testing.T) {
	// Tester plusieurs appels consécutifs
	os.Unsetenv("TEST_VAR")
	defer os.Unsetenv("TEST_VAR")

	reset1 := WithEnv("TEST_VAR", "value1")
	assert.Equal(t, "value1", os.Getenv("TEST_VAR"))

	reset2 := WithEnv("TEST_VAR", "value2")
	assert.Equal(t, "value2", os.Getenv("TEST_VAR"))

	reset2()
	assert.Equal(t, "value1", os.Getenv("TEST_VAR"))

	reset1()
	assert.Empty(t, os.Getenv("TEST_VAR"))
}

func TestResetEnv(t *testing.T) {
	// Définir quelques variables de test
	testVars := map[string]string{
		"APP_ENV":              "test",
		"APP_PORT":             "9000",
		"JWT_SECRET":           "test-secret",
		"DATABASE_URL":         "postgres://test",
		"REDIS_URL":            "redis://test",
		"CORS_ALLOWED_ORIGINS": "http://test",
		"RATE_LIMIT_LIMIT":     "200",
		"RATE_LIMIT_WINDOW":    "120",
		"LOG_LEVEL":            "ERROR",
	}

	// Sauvegarder les valeurs originales
	originalValues := make(map[string]string)
	for key := range testVars {
		originalValues[key] = os.Getenv(key)
	}
	defer func() {
		// Restaurer les valeurs originales
		for key, value := range originalValues {
			if value != "" {
				os.Setenv(key, value)
			} else {
				os.Unsetenv(key)
			}
		}
	}()

	// Définir les variables de test
	for key, value := range testVars {
		os.Setenv(key, value)
	}

	// Vérifier qu'elles sont définies
	for key, expectedValue := range testVars {
		assert.Equal(t, expectedValue, os.Getenv(key), "Variable %s should be set", key)
	}

	// Réinitialiser
	ResetEnv()

	// Vérifier qu'elles sont toutes unset
	for key := range testVars {
		assert.Empty(t, os.Getenv(key), "Variable %s should be unset", key)
	}
}

func TestWithMultipleEnv(t *testing.T) {
	// Sauvegarder les valeurs originales
	originalValues := make(map[string]string)
	testKeys := []string{"TEST_VAR1", "TEST_VAR2", "TEST_VAR3"}
	for _, key := range testKeys {
		originalValues[key] = os.Getenv(key)
	}
	defer func() {
		// Restaurer les valeurs originales
		for key, value := range originalValues {
			if value != "" {
				os.Setenv(key, value)
			} else {
				os.Unsetenv(key)
			}
		}
	}()

	// Définir quelques variables avec des valeurs existantes
	os.Setenv("TEST_VAR1", "original1")
	os.Unsetenv("TEST_VAR2")
	os.Unsetenv("TEST_VAR3")

	// Utiliser WithMultipleEnv
	reset := WithMultipleEnv(map[string]string{
		"TEST_VAR1": "new1",
		"TEST_VAR2": "new2",
		"TEST_VAR3": "new3",
	})
	defer reset()

	// Vérifier que les nouvelles valeurs sont définies
	assert.Equal(t, "new1", os.Getenv("TEST_VAR1"))
	assert.Equal(t, "new2", os.Getenv("TEST_VAR2"))
	assert.Equal(t, "new3", os.Getenv("TEST_VAR3"))

	// Nettoyer
	reset()

	// Vérifier que les valeurs originales sont restaurées
	assert.Equal(t, "original1", os.Getenv("TEST_VAR1"))
	assert.Empty(t, os.Getenv("TEST_VAR2"))
	assert.Empty(t, os.Getenv("TEST_VAR3"))
}

func TestWithMultipleEnv_Empty(t *testing.T) {
	// Tester avec une map vide
	reset := WithMultipleEnv(map[string]string{})
	require.NotNil(t, reset)

	// La fonction de cleanup devrait fonctionner sans erreur
	reset()
}

func TestNewTestConfig_Logger(t *testing.T) {
	config := NewTestConfig(t)
	require.NotNil(t, config.Logger)

	// Vérifier que le logger fonctionne
	config.Logger.Info("test log message")
}

func TestNewTestConfig_Isolation(t *testing.T) {
	// Tester que chaque appel crée une nouvelle instance
	config1 := NewTestConfig(t)
	config2 := NewTestConfig(t)

	// Modifier config1
	config1.AppPort = 9000
	config1.LogLevel = "ERROR"

	// Vérifier que config2 n'est pas affecté
	assert.Equal(t, 8080, config2.AppPort)
	assert.Equal(t, "DEBUG", config2.LogLevel)
}
