package config

import (
	"testing"

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

func TestConfigDefaults_Build(t *testing.T) {
	defaults := NewConfigDefaults().
		WithPort(9000).
		WithEnv("test").
		WithJWTSecret("test-secret").
		WithDatabaseURL("postgres://test").
		WithLogLevel("DEBUG")

	config := defaults.Build()

	assert.Equal(t, 9000, config.AppPort)
	assert.Equal(t, "test-secret", config.JWTSecret)
	assert.Equal(t, "postgres://test", config.DatabaseURL)
	assert.Equal(t, "DEBUG", config.LogLevel)
}

func TestConfigDefaults_Merge(t *testing.T) {
	existingConfig := &Config{
		AppPort:  8080,
		LogLevel: "INFO",
	}

	defaults := NewConfigDefaults().
		WithPort(9000).
		WithLogLevel("DEBUG")

	merged := defaults.Merge(existingConfig)

	assert.Equal(t, 9000, merged.AppPort)     // Override
	assert.Equal(t, "DEBUG", merged.LogLevel) // Override
	assert.Same(t, existingConfig, merged)    // Même instance
}

func TestConfigDefaults_WithPort(t *testing.T) {
	defaults := NewConfigDefaults().WithPort(3000)
	config := defaults.Build()
	assert.Equal(t, 3000, config.AppPort)
}

func TestConfigDefaults_WithJWTSecret(t *testing.T) {
	defaults := NewConfigDefaults().WithJWTSecret("my-secret-key")
	config := defaults.Build()
	assert.Equal(t, "my-secret-key", config.JWTSecret)
}

func TestConfigDefaults_WithDatabaseURL(t *testing.T) {
	defaults := NewConfigDefaults().WithDatabaseURL("postgresql://localhost/db")
	config := defaults.Build()
	assert.Equal(t, "postgresql://localhost/db", config.DatabaseURL)
}

func TestConfigDefaults_WithRedisURL(t *testing.T) {
	defaults := NewConfigDefaults().WithRedisURL("redis://localhost:6379")
	config := defaults.Build()
	assert.Equal(t, "redis://localhost:6379", config.RedisURL)
}

func TestConfigDefaults_WithCORSOrigins(t *testing.T) {
	origins := []string{"http://localhost:3000", "https://example.com"}
	defaults := NewConfigDefaults().WithCORSOrigins(origins)
	config := defaults.Build()
	assert.Equal(t, origins, config.CORSOrigins)
}

func TestConfigDefaults_WithRateLimit(t *testing.T) {
	defaults := NewConfigDefaults().WithRateLimit(200, 120)
	config := defaults.Build()
	assert.Equal(t, 200, config.RateLimitLimit)
	assert.Equal(t, 120, config.RateLimitWindow)
}

func TestConfigDefaults_WithLogLevel(t *testing.T) {
	defaults := NewConfigDefaults().WithLogLevel("ERROR")
	config := defaults.Build()
	assert.Equal(t, "ERROR", config.LogLevel)
}

func TestConfigDefaults_WithLogger(t *testing.T) {
	logger, _ := zap.NewDevelopment()
	defaults := NewConfigDefaults().WithLogger(logger)
	config := defaults.Build()
	assert.Same(t, logger, config.Logger)
}

func TestConfigDefaults_Build_Empty(t *testing.T) {
	defaults := NewConfigDefaults()
	config := defaults.Build()

	assert.NotNil(t, config)
	assert.Equal(t, 0, config.AppPort)
	assert.Empty(t, config.JWTSecret)
	assert.Empty(t, config.DatabaseURL)
	assert.Empty(t, config.RedisURL)
	assert.Nil(t, config.CORSOrigins)
	assert.Equal(t, 0, config.RateLimitLimit)
	assert.Equal(t, 0, config.RateLimitWindow)
	assert.Empty(t, config.LogLevel)
	assert.Nil(t, config.Logger)
}

func TestConfigDefaults_FluentChaining(t *testing.T) {
	config := NewConfigDefaults().
		WithPort(8080).
		WithJWTSecret("secret").
		WithDatabaseURL("postgres://db").
		WithRedisURL("redis://redis").
		WithCORSOrigins([]string{"*"}).
		WithRateLimit(100, 60).
		WithLogLevel("INFO").
		Build()

	assert.Equal(t, 8080, config.AppPort)
	assert.Equal(t, "secret", config.JWTSecret)
	assert.Equal(t, "postgres://db", config.DatabaseURL)
	assert.Equal(t, "redis://redis", config.RedisURL)
	assert.Equal(t, []string{"*"}, config.CORSOrigins)
	assert.Equal(t, 100, config.RateLimitLimit)
	assert.Equal(t, 60, config.RateLimitWindow)
	assert.Equal(t, "INFO", config.LogLevel)
}

func TestConfigDefaults_Merge_Partial(t *testing.T) {
	existingConfig := &Config{
		AppPort:     8080,
		JWTSecret:   "original-secret",
		DatabaseURL: "postgres://original",
		LogLevel:    "INFO",
	}

	defaults := NewConfigDefaults().
		WithPort(9000).
		WithDatabaseURL("postgres://new")

	merged := defaults.Merge(existingConfig)

	assert.Equal(t, 9000, merged.AppPort)                 // Override
	assert.Equal(t, "original-secret", merged.JWTSecret)  // Pas override
	assert.Equal(t, "postgres://new", merged.DatabaseURL) // Override
	assert.Equal(t, "INFO", merged.LogLevel)              // Pas override
}

func TestConfigDefaults_Merge_AllFields(t *testing.T) {
	existingConfig := &Config{
		AppPort:         8080,
		JWTSecret:       "old-secret",
		DatabaseURL:     "postgres://old",
		RedisURL:        "redis://old",
		CORSOrigins:     []string{"old-origin"},
		RateLimitLimit:  50,
		RateLimitWindow: 30,
		LogLevel:        "WARN",
	}

	logger, _ := zap.NewDevelopment()
	newOrigins := []string{"new-origin1", "new-origin2"}

	defaults := NewConfigDefaults().
		WithPort(9000).
		WithJWTSecret("new-secret").
		WithDatabaseURL("postgres://new").
		WithRedisURL("redis://new").
		WithCORSOrigins(newOrigins).
		WithRateLimit(200, 120).
		WithLogLevel("DEBUG").
		WithLogger(logger)

	merged := defaults.Merge(existingConfig)

	assert.Equal(t, 9000, merged.AppPort)
	assert.Equal(t, "new-secret", merged.JWTSecret)
	assert.Equal(t, "postgres://new", merged.DatabaseURL)
	assert.Equal(t, "redis://new", merged.RedisURL)
	assert.Equal(t, newOrigins, merged.CORSOrigins)
	assert.Equal(t, 200, merged.RateLimitLimit)
	assert.Equal(t, 120, merged.RateLimitWindow)
	assert.Equal(t, "DEBUG", merged.LogLevel)
	assert.Same(t, logger, merged.Logger)
}

func TestConfigDefaults_WithEnv(t *testing.T) {
	defaults := NewConfigDefaults().WithEnv("production")
	// Env n'est pas stocké dans Config, mais le builder l'accepte
	// Ceci permet d'utiliser l'env pour d'autres choses si nécessaire
	config := defaults.Build()
	assert.NotNil(t, config)
}

func TestConfigDefaults_MultipleCalls(t *testing.T) {
	defaults := NewConfigDefaults().
		WithPort(1000).
		WithPort(2000). // Override
		WithLogLevel("INFO").
		WithLogLevel("DEBUG") // Override

	config := defaults.Build()
	assert.Equal(t, 2000, config.AppPort)     // Dernière valeur
	assert.Equal(t, "DEBUG", config.LogLevel) // Dernière valeur
}

func TestNewConfigDefaults(t *testing.T) {
	defaults := NewConfigDefaults()
	assert.NotNil(t, defaults)
	config := defaults.Build()
	assert.NotNil(t, config)
}
