// codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config/config.go
package config

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

type Config struct {
	App            AppConfig
	Server         ServerConfig
	Database       DatabaseConfig
	Cache          CacheConfig
	S3             S3Config
	JWT            JWTConfig
	Mailgun        MailgunConfig
	Observability  ObservabilityConfig
	Logging        LoggingConfig
	Security       SecurityConfig
	LeaderElection LeaderElectionConfig
	InviteEmail    InviteEmailConfig
	LoginRateLimit LoginRateLimitConfig
}

// Configuration is an alias for Config for backward compatibility
type Configuration = Config

type AppConfig struct {
	Environment string
	Version     string
	DataDir     string
}

type ServerConfig struct {
	Host            string
	Port            int
	ReadTimeout     time.Duration
	WriteTimeout    time.Duration
	IdleTimeout     time.Duration
	ShutdownTimeout time.Duration
}

type DatabaseConfig struct {
	Hosts             []string
	Keyspace          string
	Consistency       string
	Username          string
	Password          string
	MigrationsPath    string
	AutoMigrate       bool // Run migrations automatically on startup
	ConnectTimeout    time.Duration
	RequestTimeout    time.Duration
	ReplicationFactor int
	MaxRetryAttempts  int
	RetryDelay        time.Duration
}

type CacheConfig struct {
	Host     string
	Port     int
	Password string
	DB       int
}

type S3Config struct {
	Endpoint       string
	PublicEndpoint string // Public-facing endpoint for presigned URLs (e.g., http://localhost:8334)
	AccessKey      string
	SecretKey      string
	BucketName     string
	Region         string
	UseSSL         bool
	UsePathStyle   bool // Use path-style URLs (true for MinIO/SeaweedFS, false for AWS S3/DigitalOcean Spaces)
}

type JWTConfig struct {
	Secret                 string
	AccessTokenDuration    time.Duration
	RefreshTokenDuration   time.Duration
	SessionDuration        time.Duration
	SessionCleanupInterval time.Duration
}

type MailgunConfig struct {
	APIKey      string
	Domain      string
	APIBase     string
	SenderEmail string
	SenderName  string
	FrontendURL string
}

type ObservabilityConfig struct {
	Enabled              bool
	Port                 int
	HealthCheckTimeout   time.Duration
	MetricsEnabled       bool
	HealthChecksEnabled  bool
	DetailedHealthChecks bool
}

type LoggingConfig struct {
	Level            string
	Format           string
	EnableStacktrace bool
	EnableCaller     bool
}

type SecurityConfig struct {
	GeoLiteDBPath                string
	BannedCountries              []string
	RateLimitEnabled             bool
	IPBlockEnabled               bool
	AllowedOrigins               []string // CORS allowed origins
	TrustedProxies               []string
	IPAnonymizationEnabled       bool
	IPAnonymizationRetentionDays int
	IPAnonymizationSchedule      string
}

type LeaderElectionConfig struct {
	Enabled           bool
	LockTTL           time.Duration
	HeartbeatInterval time.Duration
	RetryInterval     time.Duration
	InstanceID        string
	Hostname          string
}

// InviteEmailConfig holds configuration for invitation emails to non-registered users
type InviteEmailConfig struct {
	MaxEmailsPerDay int // Maximum invitation emails a user can send per day
}

// LoginRateLimitConfig holds configuration for login rate limiting
type LoginRateLimitConfig struct {
	MaxAttemptsPerIP            int           // Maximum login attempts per IP in the window
	IPWindow                    time.Duration // Time window for IP-based rate limiting
	MaxFailedAttemptsPerAccount int           // Maximum failed attempts before account lockout
	AccountLockoutDuration      time.Duration // How long to lock an account after too many failures
}

func Load() (*Config, error) {
	cfg := &Config{
		// App
		App: AppConfig{
			Environment: getEnvString("APP_ENVIRONMENT", "development"),
			Version:     getEnvString("APP_VERSION", "0.1.0"),
			DataDir:     getEnvString("APP_DATA_DIRECTORY", "./data"),
		},

		// Server
		Server: ServerConfig{
			Host:            getEnvString("SERVER_HOST", "0.0.0.0"),
			Port:            getEnvInt("SERVER_PORT", 8000),
			ReadTimeout:     getEnvDuration("SERVER_READ_TIMEOUT", 30*time.Second),
			WriteTimeout:    getEnvDuration("SERVER_WRITE_TIMEOUT", 30*time.Second),
			IdleTimeout:     getEnvDuration("SERVER_IDLE_TIMEOUT", 60*time.Second),
			ShutdownTimeout: getEnvDuration("SERVER_SHUTDOWN_TIMEOUT", 10*time.Second),
		},

		// Database
		Database: DatabaseConfig{
			Hosts:             strings.Split(getEnvString("DATABASE_HOSTS", "localhost:9042"), ","),
			Keyspace:          getEnvString("DATABASE_KEYSPACE", "maplefile"),
			Consistency:       getEnvString("DATABASE_CONSISTENCY", "QUORUM"),
			Username:          getEnvString("DATABASE_USERNAME", ""),
			Password:          getEnvString("DATABASE_PASSWORD", ""),
			MigrationsPath:    getEnvString("DATABASE_MIGRATIONS_PATH", "./migrations"),
			AutoMigrate:       getEnvBool("DATABASE_AUTO_MIGRATE", true),
			ConnectTimeout:    getEnvDuration("DATABASE_CONNECT_TIMEOUT", 10*time.Second),
			RequestTimeout:    getEnvDuration("DATABASE_REQUEST_TIMEOUT", 5*time.Second),
			ReplicationFactor: getEnvInt("DATABASE_REPLICATION", 3),
			MaxRetryAttempts:  getEnvInt("DATABASE_MAX_RETRIES", 3),
			RetryDelay:        getEnvDuration("DATABASE_RETRY_DELAY", 1*time.Second),
		},

		// Cache
		Cache: CacheConfig{
			Host:     getEnvString("CACHE_HOST", "localhost"),
			Port:     getEnvInt("CACHE_PORT", 6379),
			Password: getEnvString("CACHE_PASSWORD", ""),
			DB:       getEnvInt("CACHE_DB", 0),
		},

		// S3
		S3: S3Config{
			Endpoint:       getEnvString("S3_ENDPOINT", "http://localhost:9000"),
			PublicEndpoint: getEnvString("S3_PUBLIC_ENDPOINT", ""), // Falls back to Endpoint if not set
			// CWE-798: Remove default credentials - require explicit configuration
			// SECURITY: Default 'minioadmin' credentials removed for production safety
			AccessKey:    getEnvString("S3_ACCESS_KEY", ""),
			SecretKey:    getEnvString("S3_SECRET_KEY", ""),
			BucketName:   getEnvString("S3_BUCKET", "maplefile"),
			Region:       getEnvString("S3_REGION", "us-east-1"),
			UseSSL:       getEnvBool("S3_USE_SSL", false),
			UsePathStyle: getEnvBool("S3_USE_PATH_STYLE", true), // Default true for dev (SeaweedFS), false for prod (DO Spaces)
		},

		// JWT
		JWT: JWTConfig{
			// CWE-798: Remove default weak secret - require explicit configuration
			// SECURITY: Default 'change-me-in-production' removed to force proper JWT secret setup
			Secret:                 getEnvString("JWT_SECRET", ""),
			AccessTokenDuration:    getEnvDuration("JWT_ACCESS_TOKEN_DURATION", 15*time.Minute),
			RefreshTokenDuration:   getEnvDuration("JWT_REFRESH_TOKEN_DURATION", 7*24*time.Hour),
			SessionDuration:        getEnvDuration("JWT_SESSION_DURATION", 24*time.Hour),
			SessionCleanupInterval: getEnvDuration("JWT_SESSION_CLEANUP_INTERVAL", 1*time.Hour),
		},

		// Mailgun
		Mailgun: MailgunConfig{
			APIKey:      getEnvString("MAILGUN_API_KEY", ""),
			Domain:      getEnvString("MAILGUN_DOMAIN", ""),
			APIBase:     getEnvString("MAILGUN_API_BASE", "https://api.mailgun.net/v3"),
			SenderEmail: getEnvString("MAILGUN_FROM_EMAIL", "noreply@maplefile.app"),
			SenderName:  getEnvString("MAILGUN_FROM_NAME", "MapleFile"),
			FrontendURL: getEnvString("MAILGUN_FRONTEND_URL", "http://localhost:3000"),
		},

		// Observability
		Observability: ObservabilityConfig{
			Enabled:              getEnvBool("OBSERVABILITY_ENABLED", true),
			Port:                 getEnvInt("OBSERVABILITY_PORT", 9090),
			HealthCheckTimeout:   getEnvDuration("OBSERVABILITY_HEALTH_TIMEOUT", 5*time.Second),
			MetricsEnabled:       getEnvBool("OBSERVABILITY_METRICS_ENABLED", true),
			HealthChecksEnabled:  getEnvBool("OBSERVABILITY_HEALTH_ENABLED", true),
			DetailedHealthChecks: getEnvBool("OBSERVABILITY_DETAILED_HEALTH", false),
		},

		// Logging
		Logging: LoggingConfig{
			Level:            getEnvString("LOG_LEVEL", "info"),
			Format:           getEnvString("LOG_FORMAT", "json"),
			EnableStacktrace: getEnvBool("LOG_STACKTRACE", false),
			EnableCaller:     getEnvBool("LOG_CALLER", true),
		},

		// Security
		Security: SecurityConfig{
			GeoLiteDBPath:                getEnvString("SECURITY_GEOLITE_DB_PATH", "./data/GeoLite2-Country.mmdb"),
			BannedCountries:              strings.Split(getEnvString("SECURITY_BANNED_COUNTRIES", ""), ","),
			RateLimitEnabled:             getEnvBool("SECURITY_RATE_LIMIT_ENABLED", true),
			IPBlockEnabled:               getEnvBool("SECURITY_IP_BLOCK_ENABLED", true),
			AllowedOrigins:               strings.Split(getEnvString("SECURITY_ALLOWED_ORIGINS", ""), ","),
			TrustedProxies:               strings.Split(getEnvString("SECURITY_TRUSTED_PROXIES", ""), ","),
			IPAnonymizationEnabled:       getEnvBool("SECURITY_IP_ANONYMIZATION_ENABLED", true),
			IPAnonymizationRetentionDays: getEnvInt("SECURITY_IP_ANONYMIZATION_RETENTION_DAYS", 90),
			IPAnonymizationSchedule:      getEnvString("SECURITY_IP_ANONYMIZATION_SCHEDULE", "0 2 * * *"), // Daily at 2 AM
		},

		// Leader Election
		LeaderElection: LeaderElectionConfig{
			Enabled:           getEnvBool("LEADER_ELECTION_ENABLED", true),
			LockTTL:           getEnvDuration("LEADER_ELECTION_LOCK_TTL", 10*time.Second),
			HeartbeatInterval: getEnvDuration("LEADER_ELECTION_HEARTBEAT_INTERVAL", 3*time.Second),
			RetryInterval:     getEnvDuration("LEADER_ELECTION_RETRY_INTERVAL", 2*time.Second),
			InstanceID:        getEnvString("LEADER_ELECTION_INSTANCE_ID", ""),
			Hostname:          getEnvString("LEADER_ELECTION_HOSTNAME", ""),
		},

		// Invite Email
		InviteEmail: InviteEmailConfig{
			MaxEmailsPerDay: getEnvInt("MAPLEFILE_INVITE_MAX_EMAILS_PER_DAY", 3),
		},

		// Login Rate Limiting
		LoginRateLimit: LoginRateLimitConfig{
			MaxAttemptsPerIP:            getEnvInt("LOGIN_RATE_LIMIT_MAX_ATTEMPTS_PER_IP", 50),
			IPWindow:                    getEnvDuration("LOGIN_RATE_LIMIT_IP_WINDOW", 15*time.Minute),
			MaxFailedAttemptsPerAccount: getEnvInt("LOGIN_RATE_LIMIT_MAX_FAILED_PER_ACCOUNT", 10),
			AccountLockoutDuration:      getEnvDuration("LOGIN_RATE_LIMIT_LOCKOUT_DURATION", 30*time.Minute),
		},
	}

	return cfg, nil
}

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

func getEnvInt(key string, defaultValue int) int {
	if value := os.Getenv(key); value != "" {
		if intValue, err := strconv.Atoi(value); err == nil {
			return intValue
		}
	}
	return defaultValue
}

func getEnvBool(key string, defaultValue bool) bool {
	if value := os.Getenv(key); value != "" {
		if boolValue, err := strconv.ParseBool(value); err == nil {
			return boolValue
		}
	}
	return defaultValue
}

func getEnvDuration(key string, defaultValue time.Duration) time.Duration {
	if value := os.Getenv(key); value != "" {
		if duration, err := time.ParseDuration(value); err == nil {
			return duration
		}
	}
	return defaultValue
}

func (c *Config) Validate() error {
	// For backward compatibility, call ValidateProduction for production environments
	if c.App.Environment == "production" {
		return c.ValidateProduction()
	}
	return nil
}

// ValidateProduction performs comprehensive validation of all critical configuration
// parameters for production environments to prevent security misconfigurations.
// CWE-798: Use of Hard-coded Credentials
// OWASP A05:2021: Security Misconfiguration
func (c *Config) ValidateProduction() error {
	var errors []string

	// JWT Secret Validation
	if c.JWT.Secret == "" {
		errors = append(errors, "JWT_SECRET is required in production")
	} else if len(c.JWT.Secret) < 32 {
		errors = append(errors, "JWT_SECRET must be at least 32 characters for production security")
	}

	// Database Credentials Validation
	if len(c.Database.Hosts) == 0 {
		errors = append(errors, "DATABASE_HOSTS is required in production")
	}
	if c.Database.Keyspace == "" {
		errors = append(errors, "DATABASE_KEYSPACE is required in production")
	}
	// Password is optional for some Cassandra setups, but username requires password
	if c.Database.Username != "" && c.Database.Password == "" {
		errors = append(errors, "DATABASE_PASSWORD is required when DATABASE_USERNAME is set")
	}

	// S3/Object Storage Credentials Validation
	if c.S3.AccessKey == "" {
		errors = append(errors, "S3_ACCESS_KEY is required in production")
	}

	if c.S3.SecretKey == "" {
		errors = append(errors, "S3_SECRET_KEY is required in production")
	}

	if c.S3.BucketName == "" {
		errors = append(errors, "S3_BUCKET is required in production")
	}

	if c.S3.Endpoint == "" {
		errors = append(errors, "S3_ENDPOINT is required in production")
	}

	// Mailgun/Email Service Validation
	if c.Mailgun.APIKey == "" {
		errors = append(errors, "MAILGUN_API_KEY is required in production (email service needed)")
	}
	if c.Mailgun.Domain == "" {
		errors = append(errors, "MAILGUN_DOMAIN is required in production")
	}
	if c.Mailgun.SenderEmail == "" {
		errors = append(errors, "MAILGUN_FROM_EMAIL is required in production")
	}

	// Redis/Cache Configuration Validation
	if c.Cache.Host == "" {
		errors = append(errors, "CACHE_HOST is required in production")
	}
	// Note: Cache password is optional for some Redis setups

	// Security Configuration Validation
	if c.App.Environment != "production" {
		errors = append(errors, "APP_ENVIRONMENT must be set to 'production' for production deployments")
	}

	// CORS Security - Warn if allowing all origins in production
	for _, origin := range c.Security.AllowedOrigins {
		if origin == "*" {
			errors = append(errors, "SECURITY_ALLOWED_ORIGINS='*' is not recommended in production (security risk)")
		}
	}

	// SSL/TLS Validation
	if c.S3.UseSSL == false {
		// This is a warning, not a hard error, as some internal networks don't use SSL
		// errors = append(errors, "S3_USE_SSL should be 'true' in production for security")
	}

	// Return all validation errors
	if len(errors) > 0 {
		return fmt.Errorf("production configuration validation failed:\n  - %s", strings.Join(errors, "\n  - "))
	}

	return nil
}

// ValidateDevelopment validates configuration for development environments
// This is less strict but still checks for basic configuration issues
func (c *Config) ValidateDevelopment() error {
	var errors []string

	// Basic validations that apply to all environments
	if c.JWT.Secret == "" {
		errors = append(errors, "JWT_SECRET is required")
	}

	if c.Database.Keyspace == "" {
		errors = append(errors, "DATABASE_KEYSPACE is required")
	}

	if c.S3.BucketName == "" {
		errors = append(errors, "S3_BUCKET is required")
	}

	if len(errors) > 0 {
		return fmt.Errorf("development configuration validation failed:\n  - %s", strings.Join(errors, "\n  - "))
	}

	return nil
}
