package config

import (
	"strings"
	"testing"

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

func TestConfigValidator_ValidatePort(t *testing.T) {
	validator := NewConfigValidator()

	tests := []struct {
		name    string
		port    int
		wantErr bool
	}{
		{"valid port", 8080, false},
		{"min port", 1, false},
		{"max port", 65535, false},
		{"invalid negative", -1, true},
		{"invalid too high", 65536, true},
		{"invalid zero", 0, true},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := validator.ValidatePort(tt.port)
			if tt.wantErr {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

func TestConfigValidator_ValidateURL(t *testing.T) {
	validator := NewConfigValidator()

	tests := []struct {
		name           string
		url            string
		expectedScheme string
		wantErr        bool
	}{
		{"valid postgres URL", "postgres://user:pass@localhost:5432/db", "postgres", false},
		{"valid postgresql URL", "postgresql://user:pass@localhost:5432/db", "postgresql", false},
		{"valid redis URL", "redis://localhost:6379", "redis", false},
		{"valid rediss URL", "rediss://localhost:6380", "rediss", false},
		{"invalid scheme", "http://localhost", "postgres", true},
		{"empty URL", "", "postgres", true},
		{"malformed URL", "://invalid", "postgres", true},
		{"missing scheme", "localhost:5432/db", "postgres", true},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := validator.ValidateURL(tt.url, tt.expectedScheme)
			if tt.wantErr {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

func TestConfigValidator_ValidateEnum(t *testing.T) {
	validator := NewConfigValidator()

	tests := []struct {
		name    string
		value   string
		allowed []string
		wantErr bool
	}{
		{
			name:    "valid value in enum",
			value:   "INFO",
			allowed: []string{"DEBUG", "INFO", "WARN", "ERROR"},
			wantErr: false,
		},
		{
			name:    "case sensitive match",
			value:   "info",
			allowed: []string{"INFO", "WARN"},
			wantErr: true,
		},
		{
			name:    "value not in enum",
			value:   "TRACE",
			allowed: []string{"DEBUG", "INFO", "WARN", "ERROR"},
			wantErr: true,
		},
		{
			name:    "empty value with empty allowed",
			value:   "",
			allowed: []string{},
			wantErr: true,
		},
		{
			name:    "empty value in allowed",
			value:   "",
			allowed: []string{"", "value1"},
			wantErr: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := validator.ValidateEnum(tt.value, tt.allowed)
			if tt.wantErr {
				assert.Error(t, err)
				assert.Contains(t, err.Error(), "not allowed")
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

func TestConfigValidator_ValidateSecretLength(t *testing.T) {
	validator := NewConfigValidator()

	tests := []struct {
		name      string
		secret    string
		minLength int
		wantErr   bool
	}{
		{"valid secret", "my-super-secret-key-that-is-long-enough", 32, false},
		{"exact length", strings.Repeat("a", 32), 32, false},
		{"too short", "short", 32, true},
		{"empty secret", "", 1, true},
		{"empty secret with min 0", "", 0, false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := validator.ValidateSecretLength(tt.secret, tt.minLength)
			if tt.wantErr {
				assert.Error(t, err)
				assert.Contains(t, err.Error(), "at least")
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

func TestConfigValidator_ValidatePositiveInt(t *testing.T) {
	validator := NewConfigValidator()

	tests := []struct {
		name      string
		value     int
		fieldName string
		wantErr   bool
	}{
		{"valid positive", 42, "test_field", false},
		{"valid one", 1, "test_field", false},
		{"invalid zero", 0, "test_field", true},
		{"invalid negative", -1, "test_field", true},
		{"invalid large negative", -1000, "test_field", true},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := validator.ValidatePositiveInt(tt.value, tt.fieldName)
			if tt.wantErr {
				assert.Error(t, err)
				assert.Contains(t, err.Error(), "must be positive")
				assert.Contains(t, err.Error(), tt.fieldName)
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

func TestNewConfigValidator(t *testing.T) {
	validator := NewConfigValidator()
	assert.NotNil(t, validator)
}

func TestConfigValidator_ValidateURL_MultipleSchemes(t *testing.T) {
	validator := NewConfigValidator()

	// Test avec différents schémas PostgreSQL
	err1 := validator.ValidateURL("postgres://localhost/db", "postgres")
	assert.NoError(t, err1)

	err2 := validator.ValidateURL("postgresql://localhost/db", "postgresql")
	assert.NoError(t, err2)

	// Test avec schéma Redis
	err3 := validator.ValidateURL("redis://localhost:6379", "redis")
	assert.NoError(t, err3)

	err4 := validator.ValidateURL("rediss://localhost:6380", "rediss")
	assert.NoError(t, err4)
}

func TestConfigValidator_ValidateEnum_ErrorMessages(t *testing.T) {
	validator := NewConfigValidator()

	err := validator.ValidateEnum("invalid", []string{"valid1", "valid2", "valid3"})
	require.Error(t, err)
	assert.Contains(t, err.Error(), "not allowed")
	assert.Contains(t, err.Error(), "valid1, valid2, valid3")
}

func TestConfigValidator_ValidateSecretLength_ErrorMessages(t *testing.T) {
	validator := NewConfigValidator()

	err := validator.ValidateSecretLength("short", 32)
	require.Error(t, err)
	assert.Contains(t, err.Error(), "at least 32")
	assert.Contains(t, err.Error(), "got 5")
}

func TestConfigValidator_ValidatePositiveInt_ErrorMessages(t *testing.T) {
	validator := NewConfigValidator()

	err := validator.ValidatePositiveInt(-5, "rate_limit")
	require.Error(t, err)
	assert.Contains(t, err.Error(), "rate_limit")
	assert.Contains(t, err.Error(), "must be positive")
	assert.Contains(t, err.Error(), "got -5")
}
