package config

import (
	"os"
	"testing"

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

func TestEnvSecretsProvider_GetSecret(t *testing.T) {
	os.Setenv("TEST_SECRET", "my-secret-value")
	defer os.Unsetenv("TEST_SECRET")

	provider := NewEnvSecretsProvider([]string{"TEST_SECRET"})

	secret, err := provider.GetSecret("TEST_SECRET")
	require.NoError(t, err)
	assert.Equal(t, "my-secret-value", secret)

	_, err = provider.GetSecret("NONEXISTENT")
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "not found")
}

func TestEnvSecretsProvider_IsSecret(t *testing.T) {
	provider := NewEnvSecretsProvider([]string{"SECRET_KEY", "ANOTHER_SECRET"})

	assert.True(t, provider.IsSecret("SECRET_KEY"))
	assert.True(t, provider.IsSecret("ANOTHER_SECRET"))
	assert.False(t, provider.IsSecret("NOT_A_SECRET"))
	assert.False(t, provider.IsSecret(""))
}

func TestEnvSecretsProvider_GetSecret_Empty(t *testing.T) {
	// S'assurer que la variable n'existe pas
	os.Unsetenv("MISSING_SECRET")
	defer os.Unsetenv("MISSING_SECRET")

	provider := NewEnvSecretsProvider([]string{"MISSING_SECRET"})

	_, err := provider.GetSecret("MISSING_SECRET")
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "not found")
}

func TestMaskSecret(t *testing.T) {
	tests := []struct {
		name     string
		secret   string
		expected string
	}{
		{"long secret", "my-super-secret-key-12345", "my-s****2345"},                              // length 23, 4 prefix, 4 suffix
		{"short secret", "short", "****"},                                                         // length 5, <= 8
		{"empty secret", "", ""},                                                                  // length 0, empty
		{"very short", "ab", "****"},                                                              // length 2, <= 8
		{"exactly 8 chars", "12345678", "****"},                                                   // length 8, <= 8
		{"9 chars", "123456789", "1234****6789"},                                                  // length 9, 4 prefix, 4 suffix
		{"exactly 10 chars", "1234567890", "1234****7890"},                                        // length 10, 4 prefix, 4 suffix
		{"very long secret", "this-is-a-very-long-secret-key-that-needs-masking", "this****king"}, // length 45, 4 prefix, 4 suffix
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := MaskSecret(tt.secret)
			assert.Equal(t, tt.expected, result)
		})
	}
}

func TestMaskConfigValue(t *testing.T) {
	provider := NewEnvSecretsProvider([]string{"JWT_SECRET", "DB_PASSWORD"})

	tests := []struct {
		name           string
		key            string
		value          string
		provider       SecretsProvider
		expectedMasked bool
	}{
		{
			name:           "secret key should be masked",
			key:            "JWT_SECRET",
			value:          "my-secret-key-12345",
			provider:       provider,
			expectedMasked: true,
		},
		{
			name:           "non-secret key should not be masked",
			key:            "APP_PORT",
			value:          "8080",
			provider:       provider,
			expectedMasked: false,
		},
		{
			name:           "nil provider should not mask",
			key:            "JWT_SECRET",
			value:          "my-secret-key-12345",
			provider:       nil,
			expectedMasked: false,
		},
		{
			name:           "empty value should remain empty",
			key:            "JWT_SECRET",
			value:          "",
			provider:       provider,
			expectedMasked: false, // MaskSecret retourne "" pour empty, donc pas de changement visible
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := MaskConfigValue(tt.key, tt.value, tt.provider)
			if tt.expectedMasked {
				assert.NotEqual(t, tt.value, result, "Value should be masked")
				if tt.value != "" {
					assert.Contains(t, result, "****")
				}
			} else {
				assert.Equal(t, tt.value, result, "Value should not be masked")
			}
		})
	}
}

func TestDefaultSecretKeys(t *testing.T) {
	keys := DefaultSecretKeys()
	assert.NotEmpty(t, keys)

	// Vérifier que les clés communes sont présentes
	expectedKeys := []string{
		"JWT_SECRET",
		"DB_PASSWORD",
		"REDIS_PASSWORD",
		"AWS_SECRET_ACCESS_KEY",
		"STRIPE_SECRET_KEY",
	}

	for _, expectedKey := range expectedKeys {
		assert.Contains(t, keys, expectedKey, "DefaultSecretKeys should contain %s", expectedKey)
	}

	// Vérifier qu'il n'y a pas de doublons
	seen := make(map[string]bool)
	for _, key := range keys {
		assert.False(t, seen[key], "Duplicate key found: %s", key)
		seen[key] = true
	}
}

func TestNewEnvSecretsProvider(t *testing.T) {
	keys := []string{"KEY1", "KEY2", "KEY3"}
	provider := NewEnvSecretsProvider(keys)

	assert.NotNil(t, provider)
	assert.True(t, provider.IsSecret("KEY1"))
	assert.True(t, provider.IsSecret("KEY2"))
	assert.True(t, provider.IsSecret("KEY3"))
	assert.False(t, provider.IsSecret("KEY4"))
}

func TestEnvSecretsProvider_EmptyKeys(t *testing.T) {
	provider := NewEnvSecretsProvider([]string{})

	assert.NotNil(t, provider)
	assert.False(t, provider.IsSecret("ANY_KEY"))

	_, err := provider.GetSecret("ANY_KEY")
	assert.Error(t, err)
}

func TestMaskSecret_BoundaryCases(t *testing.T) {
	// Test cas limites
	tests := []struct {
		name     string
		secret   string
		expected string
	}{
		{"nil equivalent (empty)", "", ""},
		{"1 char", "a", "****"},
		{"4 chars", "abcd", "****"},
		{"5 chars", "abcde", "****"},
		{"8 chars", "12345678", "****"},
		{"9 chars (threshold)", "123456789", "1234****6789"}, // Adjusted expected
		{"exactly 10 chars", "1234567890", "1234****7890"},   // Adjusted expected
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := MaskSecret(tt.secret)
			assert.Equal(t, tt.expected, result)
		})
	}
}

func TestEnvSecretsProvider_MultipleSecrets(t *testing.T) {
	os.Setenv("SECRET1", "value1")
	os.Setenv("SECRET2", "value2")
	os.Setenv("SECRET3", "value3")
	defer func() {
		os.Unsetenv("SECRET1")
		os.Unsetenv("SECRET2")
		os.Unsetenv("SECRET3")
	}()

	provider := NewEnvSecretsProvider([]string{"SECRET1", "SECRET2", "SECRET3"})

	secret1, err := provider.GetSecret("SECRET1")
	require.NoError(t, err)
	assert.Equal(t, "value1", secret1)

	secret2, err := provider.GetSecret("SECRET2")
	require.NoError(t, err)
	assert.Equal(t, "value2", secret2)

	secret3, err := provider.GetSecret("SECRET3")
	require.NoError(t, err)
	assert.Equal(t, "value3", secret3)
}

func TestMaskConfigValue_AllCases(t *testing.T) {
	provider := NewEnvSecretsProvider([]string{"SECRET_KEY"})

	// Test avec différents types de valeurs
	testCases := []struct {
		key      string
		value    string
		expected string
	}{
		{"SECRET_KEY", "long-secret-value-12345", "long****2345"}, // Adjusted expected
		{"SECRET_KEY", "short", "****"},
		{"SECRET_KEY", "", ""},
		{"PUBLIC_KEY", "public-value", "public-value"}, // Ne devrait pas être masqué
	}

	for _, tc := range testCases {
		t.Run(tc.key+"_"+tc.value, func(t *testing.T) {
			result := MaskConfigValue(tc.key, tc.value, provider)
			assert.Equal(t, tc.expected, result)
		})
	}
}
