package config

import (
	"fmt"
	"os"
	"time"

	"gopkg.in/yaml.v3"
)

// Config represents the application configuration
type Config struct {
	Server       ServerConfig       `yaml:"server"`
	Database     DatabaseConfig     `yaml:"database"`
	Auth         AuthConfig         `yaml:"auth"`
	Logging      LoggingConfig      `yaml:"logging"`
	Security     SecurityConfig     `yaml:"security"`
	ObjectStorage ObjectStorageConfig `yaml:"object_storage"`
}

// ServerConfig holds HTTP server configuration
type ServerConfig struct {
	Port         int           `yaml:"port"`
	Host         string        `yaml:"host"`
	ReadTimeout  time.Duration `yaml:"read_timeout"`
	WriteTimeout time.Duration `yaml:"write_timeout"`
	IdleTimeout  time.Duration `yaml:"idle_timeout"`
	Cache        CacheConfig   `yaml:"cache"`
}

// CacheConfig holds response caching configuration
type CacheConfig struct {
	Enabled    bool          `yaml:"enabled"`
	DefaultTTL time.Duration `yaml:"default_ttl"`
	MaxAge     int           `yaml:"max_age"` // seconds for Cache-Control header
}

// DatabaseConfig holds PostgreSQL connection configuration
type DatabaseConfig struct {
	Host            string `yaml:"host"`
	Port            int    `yaml:"port"`
	User            string `yaml:"user"`
	Password        string `yaml:"password"`
	Database        string `yaml:"database"`
	SSLMode         string `yaml:"ssl_mode"`
	MaxConnections  int    `yaml:"max_connections"`
	MaxIdleConns    int    `yaml:"max_idle_conns"`
	ConnMaxLifetime time.Duration `yaml:"conn_max_lifetime"`
}

// AuthConfig holds authentication configuration
type AuthConfig struct {
	JWTSecret     string        `yaml:"jwt_secret"`
	TokenLifetime time.Duration `yaml:"token_lifetime"`
	Argon2Params  Argon2Params  `yaml:"argon2"`
}

// Argon2Params holds Argon2id password hashing parameters
type Argon2Params struct {
	Memory      uint32 `yaml:"memory"`
	Iterations  uint32 `yaml:"iterations"`
	Parallelism uint8  `yaml:"parallelism"`
	SaltLength  uint32 `yaml:"salt_length"`
	KeyLength   uint32 `yaml:"key_length"`
}

// LoggingConfig holds logging configuration
type LoggingConfig struct {
	Level  string `yaml:"level"`
	Format string `yaml:"format"` // json or text
}

// SecurityConfig holds security-related configuration
type SecurityConfig struct {
	CORS          CORSConfig          `yaml:"cors"`
	RateLimit     RateLimitConfig     `yaml:"rate_limit"`
	SecurityHeaders SecurityHeadersConfig `yaml:"security_headers"`
}

// CORSConfig holds CORS configuration
type CORSConfig struct {
	AllowedOrigins []string `yaml:"allowed_origins"`
	AllowedMethods []string `yaml:"allowed_methods"`
	AllowedHeaders []string `yaml:"allowed_headers"`
	AllowCredentials bool   `yaml:"allow_credentials"`
}

// RateLimitConfig holds rate limiting configuration
type RateLimitConfig struct {
	Enabled          bool    `yaml:"enabled"`
	RequestsPerSecond float64 `yaml:"requests_per_second"`
	BurstSize        int     `yaml:"burst_size"`
}

// SecurityHeadersConfig holds security headers configuration
type SecurityHeadersConfig struct {
	Enabled bool `yaml:"enabled"`
}

// ObjectStorageConfig holds MinIO configuration
type ObjectStorageConfig struct {
	Endpoint  string `yaml:"endpoint"`
	AccessKey string `yaml:"access_key"`
	SecretKey string `yaml:"secret_key"`
	UseSSL    bool   `yaml:"use_ssl"`
}

// Load reads configuration from file and environment variables
func Load(path string) (*Config, error) {
	cfg := DefaultConfig()

	// Read from file if it exists
	if _, err := os.Stat(path); err == nil {
		data, err := os.ReadFile(path)
		if err != nil {
			return nil, fmt.Errorf("failed to read config file: %w", err)
		}

		if err := yaml.Unmarshal(data, cfg); err != nil {
			return nil, fmt.Errorf("failed to parse config file: %w", err)
		}
	}

	// Override with environment variables
	overrideFromEnv(cfg)

	return cfg, nil
}

// DefaultConfig returns a configuration with sensible defaults
func DefaultConfig() *Config {
	return &Config{
		Server: ServerConfig{
			Port:         8080,
			Host:         "0.0.0.0",
			ReadTimeout:  15 * time.Second,
			WriteTimeout: 15 * time.Second,
			IdleTimeout:  60 * time.Second,
		},
		Database: DatabaseConfig{
			Host:            getEnv("CALYPSO_DB_HOST", "localhost"),
			Port:            getEnvInt("CALYPSO_DB_PORT", 5432),
			User:            getEnv("CALYPSO_DB_USER", "calypso"),
			Password:        getEnv("CALYPSO_DB_PASSWORD", ""),
			Database:        getEnv("CALYPSO_DB_NAME", "calypso"),
			SSLMode:         getEnv("CALYPSO_DB_SSLMODE", "disable"),
			MaxConnections:  25,
			MaxIdleConns:    5,
			ConnMaxLifetime: 5 * time.Minute,
		},
		Auth: AuthConfig{
			JWTSecret:     getEnv("CALYPSO_JWT_SECRET", "change-me-in-production"),
			TokenLifetime: 24 * time.Hour,
			Argon2Params: Argon2Params{
				Memory:      64 * 1024, // 64 MB
				Iterations:  3,
				Parallelism: 4,
				SaltLength:  16,
				KeyLength:   32,
			},
		},
		Logging: LoggingConfig{
			Level:  getEnv("CALYPSO_LOG_LEVEL", "info"),
			Format: getEnv("CALYPSO_LOG_FORMAT", "json"),
		},
		Security: SecurityConfig{
			CORS: CORSConfig{
				AllowedOrigins:   []string{"*"}, // Default: allow all (should be restricted in production)
				AllowedMethods:   []string{"GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"},
				AllowedHeaders:   []string{"Content-Type", "Authorization", "Accept", "Origin"},
				AllowCredentials: true,
			},
			RateLimit: RateLimitConfig{
				Enabled:          true,
				RequestsPerSecond: 100.0,
				BurstSize:        50,
			},
			SecurityHeaders: SecurityHeadersConfig{
				Enabled: true,
			},
		},
	}
}

// overrideFromEnv applies environment variable overrides
func overrideFromEnv(cfg *Config) {
	if v := os.Getenv("CALYPSO_SERVER_PORT"); v != "" {
		cfg.Server.Port = getEnvInt("CALYPSO_SERVER_PORT", cfg.Server.Port)
	}
	if v := os.Getenv("CALYPSO_DB_HOST"); v != "" {
		cfg.Database.Host = v
	}
	if v := os.Getenv("CALYPSO_DB_PASSWORD"); v != "" {
		cfg.Database.Password = v
	}
	if v := os.Getenv("CALYPSO_JWT_SECRET"); v != "" {
		cfg.Auth.JWTSecret = v
	}
}

// Helper functions
func getEnv(key, defaultValue string) string {
	if v := os.Getenv(key); v != "" {
		return v
	}
	return defaultValue
}

func getEnvInt(key string, defaultValue int) int {
	if v := os.Getenv(key); v != "" {
		var result int
		if _, err := fmt.Sscanf(v, "%d", &result); err == nil {
			return result
		}
	}
	return defaultValue
}

