package config

import (
	"testing"
	"time"
)

// TestConfig_Validate tests configuration validation rules
func TestConfig_Validate(t *testing.T) {
	tests := []struct {
		name    string
		config  *Config
		wantErr bool
		errMsg  string
	}{
		{
			name: "valid config",
			config: &Config{
				Server: ServerConfig{
					Address:         ":3000",
					ReadTimeout:     10 * time.Second,
					WriteTimeout:    10 * time.Second,
					ShutdownTimeout: 30 * time.Second,
				},
				Redis: RedisConfig{
					Address:      "localhost",
					Port:         6379,
					DB:           0,
					PoolSize:     10,
					MinIdleConns: 5,
				},
				Logging: LoggingConfig{
					Level: "info",
					AppLog: LogRotationConfig{
						Filename:   "logs/app.log",
						MaxSize:    100,
						MaxBackups: 30,
						MaxAge:     30,
					},
					AccessLog: LogRotationConfig{
						Filename:   "logs/access.log",
						MaxSize:    500,
						MaxBackups: 90,
						MaxAge:     90,
					},
				},
				Middleware: MiddlewareConfig{
					RateLimiter: RateLimiterConfig{
						Max:        100,
						Expiration: 1 * time.Minute,
						Storage:    "memory",
					},
				},
				JWT: JWTConfig{
					TokenDuration:   24 * time.Hour,
					AccessTokenTTL:  24 * time.Hour,
					RefreshTokenTTL: 168 * time.Hour,
				},
			},
			wantErr: false,
		},
		{
			name: "empty server address",
			config: &Config{
				Server: ServerConfig{
					Address:         "",
					ReadTimeout:     10 * time.Second,
					WriteTimeout:    10 * time.Second,
					ShutdownTimeout: 30 * time.Second,
				},
				Redis: RedisConfig{
					Address:  "localhost",
					Port:     6379,
					PoolSize: 10,
				},
				Logging: LoggingConfig{
					Level: "info",
					AppLog: LogRotationConfig{
						Filename: "logs/app.log",
						MaxSize:  100,
					},
					AccessLog: LogRotationConfig{
						Filename: "logs/access.log",
						MaxSize:  500,
					},
				},
			},
			wantErr: true,
			errMsg:  "server.address",
		},
		{
			name: "read timeout too short",
			config: &Config{
				Server: ServerConfig{
					Address:         ":3000",
					ReadTimeout:     1 * time.Second,
					WriteTimeout:    10 * time.Second,
					ShutdownTimeout: 30 * time.Second,
				},
				Redis: RedisConfig{
					Address:  "localhost",
					Port:     6379,
					PoolSize: 10,
				},
				Logging: LoggingConfig{
					Level: "info",
					AppLog: LogRotationConfig{
						Filename: "logs/app.log",
						MaxSize:  100,
					},
					AccessLog: LogRotationConfig{
						Filename: "logs/access.log",
						MaxSize:  500,
					},
				},
			},
			wantErr: true,
			errMsg:  "read_timeout",
		},
		{
			name: "read timeout too long",
			config: &Config{
				Server: ServerConfig{
					Address:         ":3000",
					ReadTimeout:     400 * time.Second,
					WriteTimeout:    10 * time.Second,
					ShutdownTimeout: 30 * time.Second,
				},
				Redis: RedisConfig{
					Address:  "localhost",
					Port:     6379,
					PoolSize: 10,
				},
				Logging: LoggingConfig{
					Level: "info",
					AppLog: LogRotationConfig{
						Filename: "logs/app.log",
						MaxSize:  100,
					},
					AccessLog: LogRotationConfig{
						Filename: "logs/access.log",
						MaxSize:  500,
					},
				},
			},
			wantErr: true,
			errMsg:  "read_timeout",
		},
		{
			name: "write timeout out of range",
			config: &Config{
				Server: ServerConfig{
					Address:         ":3000",
					ReadTimeout:     10 * time.Second,
					WriteTimeout:    1 * time.Second,
					ShutdownTimeout: 30 * time.Second,
				},
				Redis: RedisConfig{
					Address:  "localhost",
					Port:     6379,
					PoolSize: 10,
				},
				Logging: LoggingConfig{
					Level: "info",
					AppLog: LogRotationConfig{
						Filename: "logs/app.log",
						MaxSize:  100,
					},
					AccessLog: LogRotationConfig{
						Filename: "logs/access.log",
						MaxSize:  500,
					},
				},
			},
			wantErr: true,
			errMsg:  "write_timeout",
		},
		{
			name: "shutdown timeout too short",
			config: &Config{
				Server: ServerConfig{
					Address:         ":3000",
					ReadTimeout:     10 * time.Second,
					WriteTimeout:    10 * time.Second,
					ShutdownTimeout: 5 * time.Second,
				},
				Redis: RedisConfig{
					Address:  "localhost",
					Port:     6379,
					PoolSize: 10,
				},
				Logging: LoggingConfig{
					Level: "info",
					AppLog: LogRotationConfig{
						Filename: "logs/app.log",
						MaxSize:  100,
					},
					AccessLog: LogRotationConfig{
						Filename: "logs/access.log",
						MaxSize:  500,
					},
				},
			},
			wantErr: true,
			errMsg:  "shutdown_timeout",
		},
		{
			name: "empty redis address",
			config: &Config{
				Server: ServerConfig{
					Address:         ":3000",
					ReadTimeout:     10 * time.Second,
					WriteTimeout:    10 * time.Second,
					ShutdownTimeout: 30 * time.Second,
				},
				Redis: RedisConfig{
					Address:  "",
					Port:     6379,
					PoolSize: 10,
				},
				Logging: LoggingConfig{
					Level: "info",
					AppLog: LogRotationConfig{
						Filename: "logs/app.log",
						MaxSize:  100,
					},
					AccessLog: LogRotationConfig{
						Filename: "logs/access.log",
						MaxSize:  500,
					},
				},
			},
			wantErr: true,
			errMsg:  "redis.address",
		},
		{
			name: "invalid redis port - too high",
			config: &Config{
				Server: ServerConfig{
					Address:         ":3000",
					ReadTimeout:     10 * time.Second,
					WriteTimeout:    10 * time.Second,
					ShutdownTimeout: 30 * time.Second,
				},
				Redis: RedisConfig{
					Address:  "localhost",
					Port:     99999,
					PoolSize: 10,
				},
				Logging: LoggingConfig{
					Level: "info",
					AppLog: LogRotationConfig{
						Filename: "logs/app.log",
						MaxSize:  100,
					},
					AccessLog: LogRotationConfig{
						Filename: "logs/access.log",
						MaxSize:  500,
					},
				},
			},
			wantErr: true,
			errMsg:  "redis.port",
		},
		{
			name: "invalid redis port - zero",
			config: &Config{
				Server: ServerConfig{
					Address:         ":3000",
					ReadTimeout:     10 * time.Second,
					WriteTimeout:    10 * time.Second,
					ShutdownTimeout: 30 * time.Second,
				},
				Redis: RedisConfig{
					Address:  "localhost",
					Port:     0,
					PoolSize: 10,
				},
				Logging: LoggingConfig{
					Level: "info",
					AppLog: LogRotationConfig{
						Filename: "logs/app.log",
						MaxSize:  100,
					},
					AccessLog: LogRotationConfig{
						Filename: "logs/access.log",
						MaxSize:  500,
					},
				},
			},
			wantErr: true,
			errMsg:  "redis.port",
		},
		{
			name: "redis db out of range",
			config: &Config{
				Server: ServerConfig{
					Address:         ":3000",
					ReadTimeout:     10 * time.Second,
					WriteTimeout:    10 * time.Second,
					ShutdownTimeout: 30 * time.Second,
				},
				Redis: RedisConfig{
					Address:  "localhost",
					Port:     6379,
					DB:       20,
					PoolSize: 10,
				},
				Logging: LoggingConfig{
					Level: "info",
					AppLog: LogRotationConfig{
						Filename: "logs/app.log",
						MaxSize:  100,
					},
					AccessLog: LogRotationConfig{
						Filename: "logs/access.log",
						MaxSize:  500,
					},
				},
			},
			wantErr: true,
			errMsg:  "redis.db",
		},
		{
			name: "redis pool size too large",
			config: &Config{
				Server: ServerConfig{
					Address:         ":3000",
					ReadTimeout:     10 * time.Second,
					WriteTimeout:    10 * time.Second,
					ShutdownTimeout: 30 * time.Second,
				},
				Redis: RedisConfig{
					Address:  "localhost",
					Port:     6379,
					PoolSize: 2000,
				},
				Logging: LoggingConfig{
					Level: "info",
					AppLog: LogRotationConfig{
						Filename: "logs/app.log",
						MaxSize:  100,
					},
					AccessLog: LogRotationConfig{
						Filename: "logs/access.log",
						MaxSize:  500,
					},
				},
			},
			wantErr: true,
			errMsg:  "pool_size",
		},
		{
			name: "min idle conns exceeds pool size",
			config: &Config{
				Server: ServerConfig{
					Address:         ":3000",
					ReadTimeout:     10 * time.Second,
					WriteTimeout:    10 * time.Second,
					ShutdownTimeout: 30 * time.Second,
				},
				Redis: RedisConfig{
					Address:      "localhost",
					Port:         6379,
					PoolSize:     10,
					MinIdleConns: 20,
				},
				Logging: LoggingConfig{
					Level: "info",
					AppLog: LogRotationConfig{
						Filename: "logs/app.log",
						MaxSize:  100,
					},
					AccessLog: LogRotationConfig{
						Filename: "logs/access.log",
						MaxSize:  500,
					},
				},
			},
			wantErr: true,
			errMsg:  "min_idle_conns",
		},
		{
			name: "invalid log level",
			config: &Config{
				Server: ServerConfig{
					Address:         ":3000",
					ReadTimeout:     10 * time.Second,
					WriteTimeout:    10 * time.Second,
					ShutdownTimeout: 30 * time.Second,
				},
				Redis: RedisConfig{
					Address:  "localhost",
					Port:     6379,
					PoolSize: 10,
				},
				Logging: LoggingConfig{
					Level: "invalid",
					AppLog: LogRotationConfig{
						Filename: "logs/app.log",
						MaxSize:  100,
					},
					AccessLog: LogRotationConfig{
						Filename: "logs/access.log",
						MaxSize:  500,
					},
				},
			},
			wantErr: true,
			errMsg:  "logging.level",
		},
		{
			name: "empty app log filename",
			config: &Config{
				Server: ServerConfig{
					Address:         ":3000",
					ReadTimeout:     10 * time.Second,
					WriteTimeout:    10 * time.Second,
					ShutdownTimeout: 30 * time.Second,
				},
				Redis: RedisConfig{
					Address:  "localhost",
					Port:     6379,
					PoolSize: 10,
				},
				Logging: LoggingConfig{
					Level: "info",
					AppLog: LogRotationConfig{
						Filename: "",
						MaxSize:  100,
					},
					AccessLog: LogRotationConfig{
						Filename: "logs/access.log",
						MaxSize:  500,
					},
				},
			},
			wantErr: true,
			errMsg:  "app_log.filename",
		},
		{
			name: "app log max size out of range",
			config: &Config{
				Server: ServerConfig{
					Address:         ":3000",
					ReadTimeout:     10 * time.Second,
					WriteTimeout:    10 * time.Second,
					ShutdownTimeout: 30 * time.Second,
				},
				Redis: RedisConfig{
					Address:  "localhost",
					Port:     6379,
					PoolSize: 10,
				},
				Logging: LoggingConfig{
					Level: "info",
					AppLog: LogRotationConfig{
						Filename: "logs/app.log",
						MaxSize:  2000,
					},
					AccessLog: LogRotationConfig{
						Filename: "logs/access.log",
						MaxSize:  500,
					},
				},
			},
			wantErr: true,
			errMsg:  "app_log.max_size",
		},
		{
			name: "invalid rate limiter storage",
			config: &Config{
				Server: ServerConfig{
					Address:         ":3000",
					ReadTimeout:     10 * time.Second,
					WriteTimeout:    10 * time.Second,
					ShutdownTimeout: 30 * time.Second,
				},
				Redis: RedisConfig{
					Address:  "localhost",
					Port:     6379,
					PoolSize: 10,
				},
				Logging: LoggingConfig{
					Level: "info",
					AppLog: LogRotationConfig{
						Filename: "logs/app.log",
						MaxSize:  100,
					},
					AccessLog: LogRotationConfig{
						Filename: "logs/access.log",
						MaxSize:  500,
					},
				},
				Middleware: MiddlewareConfig{
					RateLimiter: RateLimiterConfig{
						Max:     100,
						Storage: "invalid",
					},
				},
			},
			wantErr: true,
			errMsg:  "rate_limiter.storage",
		},
		{
			name: "rate limiter max too high",
			config: &Config{
				Server: ServerConfig{
					Address:         ":3000",
					ReadTimeout:     10 * time.Second,
					WriteTimeout:    10 * time.Second,
					ShutdownTimeout: 30 * time.Second,
				},
				Redis: RedisConfig{
					Address:  "localhost",
					Port:     6379,
					PoolSize: 10,
				},
				Logging: LoggingConfig{
					Level: "info",
					AppLog: LogRotationConfig{
						Filename: "logs/app.log",
						MaxSize:  100,
					},
					AccessLog: LogRotationConfig{
						Filename: "logs/access.log",
						MaxSize:  500,
					},
				},
				Middleware: MiddlewareConfig{
					RateLimiter: RateLimiterConfig{
						Max:     20000,
						Storage: "memory",
					},
				},
			},
			wantErr: true,
			errMsg:  "rate_limiter.max",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := tt.config.Validate()

			if (err != nil) != tt.wantErr {
				t.Errorf("Config.Validate() error = %v, wantErr %v", err, tt.wantErr)
				return
			}

			if tt.wantErr && tt.errMsg != "" {
				if err == nil {
					t.Errorf("expected error containing %q, got nil", tt.errMsg)
				} else if err.Error() == "" {
					t.Errorf("expected error containing %q, got empty error", tt.errMsg)
				}
				// Note: We check that error message exists, not exact match
				// This is because error messages might change slightly
			}
		})
	}
}

// TestSet tests the Set function
func TestSet(t *testing.T) {
	// Valid config
	validCfg := &Config{
		Server: ServerConfig{
			Address:         ":3000",
			ReadTimeout:     10 * time.Second,
			WriteTimeout:    10 * time.Second,
			ShutdownTimeout: 30 * time.Second,
		},
		Redis: RedisConfig{
			Address:  "localhost",
			Port:     6379,
			PoolSize: 10,
		},
		Logging: LoggingConfig{
			Level: "info",
			AppLog: LogRotationConfig{
				Filename: "logs/app.log",
				MaxSize:  100,
			},
			AccessLog: LogRotationConfig{
				Filename: "logs/access.log",
				MaxSize:  500,
			},
		},
		JWT: JWTConfig{
			TokenDuration:   24 * time.Hour,
			AccessTokenTTL:  24 * time.Hour,
			RefreshTokenTTL: 168 * time.Hour,
		},
	}

	err := Set(validCfg)
	if err != nil {
		t.Errorf("Set() with valid config failed: %v", err)
	}

	// Verify it was set
	got := Get()
	if got.Server.Address != ":3000" {
		t.Errorf("Get() after Set() returned wrong address: got %s, want :3000", got.Server.Address)
	}

	// Test with nil config
	err = Set(nil)
	if err == nil {
		t.Error("Set(nil) should return error")
	}

	// Test with invalid config
	invalidCfg := &Config{
		Server: ServerConfig{
			Address: "", // Empty address is invalid
		},
	}

	err = Set(invalidCfg)
	if err == nil {
		t.Error("Set() with invalid config should return error")
	}
}
