package config

import (
	"fmt"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/joho/godotenv"
)

// ConfigProvider defines the interface for configuration operations
type ConfigProvider interface {
	// GetString retrieves a string configuration value
	GetString(key string) string
	
	// GetInt retrieves an integer configuration value
	GetInt(key string) int
	
	// GetBool retrieves a boolean configuration value
	GetBool(key string) bool
	
	// GetDuration retrieves a duration configuration value
	GetDuration(key string) time.Duration
	
	// GetStringSlice retrieves a string slice configuration value
	GetStringSlice(key string) []string
	
	// IsSet checks if a configuration key is set
	IsSet(key string) bool
	
	// Validate validates all required configuration values
	Validate() error
	
	// GetDatabaseDSN constructs and returns the database connection string
	GetDatabaseDSN() string
	
	// GetServerAddress returns the server address in host:port format
	GetServerAddress() string
	
	// GetMetricsAddress returns the metrics server address in host:port format
	GetMetricsAddress() string
	
	// GetJWTSecret returns the JWT signing secret
	GetJWTSecret() string
	
	// IsDevelopment returns true if the environment is development
	IsDevelopment() bool
	
	// IsProduction returns true if the environment is production
	IsProduction() bool
}

// Config implements the ConfigProvider interface
type Config struct {
	values map[string]string
}

// NewConfig creates a new configuration provider
func NewConfig() ConfigProvider {
	// Load .env file if it exists
	_ = godotenv.Load()

	c := &Config{
		values: make(map[string]string),
	}

	// Load environment variables
	for _, env := range os.Environ() {
		pair := strings.SplitN(env, "=", 2)
		if len(pair) == 2 {
			c.values[pair[0]] = pair[1]
		}
	}

	// Set defaults
	c.setDefaults()

	return c
}

func (c *Config) setDefaults() {
	defaults := map[string]string{
		"APP_NAME":                    "api-key-service",
		"APP_VERSION":                 "1.0.0",
		"SERVER_HOST":                 "0.0.0.0",
		"SERVER_PORT":                 "8080",
		"SERVER_READ_TIMEOUT":         "30s",
		"SERVER_WRITE_TIMEOUT":        "30s",
		"SERVER_IDLE_TIMEOUT":         "120s",
		"DB_HOST":                     "localhost",
		"DB_PORT":                     "5432",
		"DB_NAME":                     "kms",
		"DB_USER":                     "postgres",
		"DB_PASSWORD":                 "postgres",
		"DB_SSLMODE":                  "disable",
		"DB_MAX_OPEN_CONNS":           "25",
		"DB_MAX_IDLE_CONNS":           "25",
		"DB_CONN_MAX_LIFETIME":        "5m",
		"MIGRATION_PATH":              "./migrations",
		"LOG_LEVEL":                   "info",
		"LOG_FORMAT":                  "json",
		"RATE_LIMIT_ENABLED":          "true",
		"RATE_LIMIT_RPS":              "100",
		"RATE_LIMIT_BURST":            "200",
		"CACHE_ENABLED":               "false",
		"CACHE_TTL":                   "1h",
		"JWT_ISSUER":                  "api-key-service",
		"JWT_SECRET":                  "bootstrap-jwt-secret-change-in-production",
		"AUTH_PROVIDER":               "header", // header or sso
		"AUTH_HEADER_USER_EMAIL":      "X-User-Email",
		"SSO_PROVIDER_URL":            "",
		"SSO_CLIENT_ID":               "",
		"SSO_CLIENT_SECRET":           "",
		"INTERNAL_APP_ID":             "internal.api-key-service",
		"INTERNAL_HMAC_KEY":           "bootstrap-hmac-key-change-in-production",
		"METRICS_ENABLED":             "false",
		"METRICS_PORT":                "9090",
		"REDIS_ENABLED":               "false",
		"REDIS_ADDR":                  "localhost:6379",
		"REDIS_PASSWORD":              "",
		"REDIS_DB":                    "0",
		"REDIS_POOL_SIZE":             "10",
		"REDIS_MIN_IDLE_CONNS":        "5",
		"REDIS_MAX_RETRIES":           "3",
		"REDIS_DIAL_TIMEOUT":          "5s",
		"REDIS_READ_TIMEOUT":          "3s",
		"REDIS_WRITE_TIMEOUT":         "3s",
		"MAX_AUTH_FAILURES":           "5",
		"AUTH_FAILURE_WINDOW":         "15m",
		"IP_BLOCK_DURATION":           "1h",
		"REQUEST_MAX_AGE":             "5m",
		"IP_WHITELIST":                "",
		"SAML_ENABLED":                "false",
		"SAML_IDP_METADATA_URL":       "",
		"SAML_SP_ENTITY_ID":           "",
		"SAML_SP_ACS_URL":             "",
		"SAML_SP_PRIVATE_KEY":         "",
		"SAML_SP_CERTIFICATE":         "",
	}

	for key, value := range defaults {
		if _, exists := c.values[key]; !exists {
			c.values[key] = value
		}
	}
}

// GetString retrieves a string configuration value
func (c *Config) GetString(key string) string {
	return c.values[key]
}

// GetInt retrieves an integer configuration value
func (c *Config) GetInt(key string) int {
	if value, exists := c.values[key]; exists {
		if intVal, err := strconv.Atoi(value); err == nil {
			return intVal
		}
	}
	return 0
}

// GetBool retrieves a boolean configuration value
func (c *Config) GetBool(key string) bool {
	if value, exists := c.values[key]; exists {
		if boolVal, err := strconv.ParseBool(value); err == nil {
			return boolVal
		}
	}
	return false
}

// GetDuration retrieves a duration configuration value
func (c *Config) GetDuration(key string) time.Duration {
	if value, exists := c.values[key]; exists {
		if duration, err := time.ParseDuration(value); err == nil {
			return duration
		}
	}
	return 0
}

// GetStringSlice retrieves a string slice configuration value
func (c *Config) GetStringSlice(key string) []string {
	if value, exists := c.values[key]; exists {
		if value == "" {
			return []string{}
		}
		return strings.Split(value, ",")
	}
	return []string{}
}

// IsSet checks if a configuration key is set
func (c *Config) IsSet(key string) bool {
	_, exists := c.values[key]
	return exists
}

// Validate validates all required configuration values
func (c *Config) Validate() error {
	required := []string{
		"DB_HOST",
		"DB_PORT",
		"DB_NAME",
		"DB_USER",
		"DB_PASSWORD",
		"SERVER_HOST",
		"SERVER_PORT",
		"INTERNAL_APP_ID",
		"INTERNAL_HMAC_KEY",
		"JWT_SECRET",
	}

	var missing []string
	for _, key := range required {
		if !c.IsSet(key) || c.GetString(key) == "" {
			missing = append(missing, key)
		}
	}

	if len(missing) > 0 {
		return fmt.Errorf("missing required configuration keys: %s", strings.Join(missing, ", "))
	}

	// Validate specific values
	if c.GetInt("DB_PORT") <= 0 || c.GetInt("DB_PORT") > 65535 {
		return fmt.Errorf("DB_PORT must be a valid port number")
	}

	if c.GetInt("SERVER_PORT") <= 0 || c.GetInt("SERVER_PORT") > 65535 {
		return fmt.Errorf("SERVER_PORT must be a valid port number")
	}

	if c.GetDuration("SERVER_READ_TIMEOUT") <= 0 {
		return fmt.Errorf("SERVER_READ_TIMEOUT must be a positive duration")
	}

	if c.GetDuration("SERVER_WRITE_TIMEOUT") <= 0 {
		return fmt.Errorf("SERVER_WRITE_TIMEOUT must be a positive duration")
	}

	if c.GetDuration("DB_CONN_MAX_LIFETIME") <= 0 {
		return fmt.Errorf("DB_CONN_MAX_LIFETIME must be a positive duration")
	}

	authProvider := c.GetString("AUTH_PROVIDER")
	if authProvider != "header" && authProvider != "sso" {
		return fmt.Errorf("AUTH_PROVIDER must be either 'header' or 'sso'")
	}

	if authProvider == "sso" {
		if c.GetString("SSO_PROVIDER_URL") == "" {
			return fmt.Errorf("SSO_PROVIDER_URL is required when AUTH_PROVIDER is 'sso'")
		}
		if c.GetString("SSO_CLIENT_ID") == "" {
			return fmt.Errorf("SSO_CLIENT_ID is required when AUTH_PROVIDER is 'sso'")
		}
		if c.GetString("SSO_CLIENT_SECRET") == "" {
			return fmt.Errorf("SSO_CLIENT_SECRET is required when AUTH_PROVIDER is 'sso'")
		}
	}

	return nil
}

// GetDatabaseDSN constructs and returns the database connection string
func (c *Config) GetDatabaseDSN() string {
	return fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=%s",
		c.GetString("DB_HOST"),
		c.GetInt("DB_PORT"),
		c.GetString("DB_USER"),
		c.GetString("DB_PASSWORD"),
		c.GetString("DB_NAME"),
		c.GetString("DB_SSLMODE"),
	)
}

// GetServerAddress returns the server address in host:port format
func (c *Config) GetServerAddress() string {
	return fmt.Sprintf("%s:%d", c.GetString("SERVER_HOST"), c.GetInt("SERVER_PORT"))
}

// GetMetricsAddress returns the metrics server address in host:port format
func (c *Config) GetMetricsAddress() string {
	return fmt.Sprintf("%s:%d", c.GetString("SERVER_HOST"), c.GetInt("METRICS_PORT"))
}

// GetJWTSecret returns the JWT signing secret
func (c *Config) GetJWTSecret() string {
	return c.GetString("JWT_SECRET")
}

// IsDevelopment returns true if the environment is development
func (c *Config) IsDevelopment() bool {
	env := c.GetString("APP_ENV")
	return env == "development" || env == "dev" || env == ""
}

// IsProduction returns true if the environment is production
func (c *Config) IsProduction() bool {
	env := c.GetString("APP_ENV")
	return env == "production" || env == "prod"
}
