package config

import (
	"errors"
	"fmt"
	"log"
	"os"
	"strconv"
	"time"

	"github.com/joho/godotenv"
	"gorm.io/gorm/logger"
)

// DatabaseConfig contains the configuration data for the database connection
type DatabaseConfig struct {
	Host         string
	Port         int
	User         string
	Password     string
	DBName       string
	SSLMode      string
	MaxIdleConns int           // Maximum number of idle connections
	MaxOpenConns int           // Maximum number of open connections
	MaxLifetime  time.Duration // Maximum lifetime of a connection
	LogLevel     logger.LogLevel
}

// DefaultDatabaseConfig returns a default configuration with sensible values
func DefaultDatabaseConfig() DatabaseConfig {
	return DatabaseConfig{
		Host:         "localhost",
		Port:         5432,
		User:         "timetracker",
		Password:     "password",
		DBName:       "timetracker",
		SSLMode:      "disable",
		MaxIdleConns: 10,
		MaxOpenConns: 100,
		MaxLifetime:  time.Hour,
		LogLevel:     logger.Info,
	}
}

// JWTConfig represents the configuration for JWT authentication
type JWTConfig struct {
	Secret        string
	TokenDuration time.Duration
	KeyGenerate   bool
	KeyDir        string
	PrivKeyFile   string
	PubKeyFile    string
	KeyBits       int
}

// Config represents the application configuration
type Config struct {
	Database  DatabaseConfig
	JWTConfig JWTConfig
	APIKey    string
}

// LoadConfig loads configuration from environment variables and .env file
func LoadConfig() (*Config, error) {
	// Try loading .env file, but don't fail if it doesn't exist
	_ = godotenv.Load()

	cfg := &Config{
		Database:  DefaultDatabaseConfig(),
		JWTConfig: JWTConfig{},
	}

	// Load database configuration
	if err := loadDatabaseConfig(cfg); err != nil {
		return nil, fmt.Errorf("failed to load database config: %w", err)
	}

	// Load JWT configuration
	if err := loadJWTConfig(cfg); err != nil {
		return nil, fmt.Errorf("failed to load JWT config: %w", err)
	}

	// Load API key
	cfg.APIKey = getEnv("API_KEY", "")

	return cfg, nil
}

// loadJWTConfig loads JWT configuration from environment
func loadJWTConfig(cfg *Config) error {
	cfg.JWTConfig.Secret = getEnv("JWT_SECRET", "")
	defaultDuration := 24 * time.Hour
	durationStr := getEnv("JWT_TOKEN_DURATION", defaultDuration.String())

	duration, err := time.ParseDuration(durationStr)
	if err != nil {
		return fmt.Errorf("invalid JWT_TOKEN_DURATION: %w", err)
	}
	cfg.JWTConfig.TokenDuration = duration

	keyGenerateStr := getEnv("JWT_KEY_GENERATE", "true")
	keyGenerate, err := strconv.ParseBool(keyGenerateStr)
	if err != nil {
		return fmt.Errorf("invalid JWT_KEY_GENERATE: %w", err)
	}
	cfg.JWTConfig.KeyGenerate = keyGenerate

	cfg.JWTConfig.KeyDir = getEnv("JWT_KEY_DIR", "./keys")
	cfg.JWTConfig.PrivKeyFile = getEnv("JWT_PRIV_KEY_FILE", "jwt.key")
	cfg.JWTConfig.PubKeyFile = getEnv("JWT_PUB_KEY_FILE", "jwt.key.pub")

	keyBitsStr := getEnv("JWT_KEY_BITS", "2048")
	keyBits, err := strconv.Atoi(keyBitsStr)
	if err != nil {
		return fmt.Errorf("invalid JWT_KEY_BITS: %w", err)
	}
	cfg.JWTConfig.KeyBits = keyBits

	return nil
}

// loadDatabaseConfig loads database configuration from environment
func loadDatabaseConfig(cfg *Config) error {
	// Required fields
	cfg.Database.Host = getEnv("DB_HOST", cfg.Database.Host)
	cfg.Database.User = getEnv("DB_USER", cfg.Database.User)
	cfg.Database.Password = getEnv("DB_PASSWORD", cfg.Database.Password)
	cfg.Database.DBName = getEnv("DB_NAME", cfg.Database.DBName)
	cfg.Database.SSLMode = getEnv("DB_SSLMODE", cfg.Database.SSLMode)

	// Optional fields with parsing
	if port := getEnv("DB_PORT", ""); port != "" {
		portInt, err := strconv.Atoi(port)
		if err != nil || portInt <= 0 {
			return errors.New("invalid DB_PORT value")
		}
		cfg.Database.Port = portInt
	}

	// Log level based on environment
	if os.Getenv("ENVIRONMENT") == "production" {
		cfg.Database.LogLevel = logger.Error
	} else {
		cfg.Database.LogLevel = logger.Info
	}

	// Validate required fields
	if cfg.Database.Host == "" || cfg.Database.User == "" ||
		cfg.Database.Password == "" || cfg.Database.DBName == "" {
		return errors.New("missing required database configuration")
	}

	return nil
}

// getEnv gets an environment variable with fallback
func getEnv(key, fallback string) string {
	if value, exists := os.LookupEnv(key); exists {
		return value
	}
	return fallback
}

// MustLoadConfig loads configuration or panics on failure
func MustLoadConfig() *Config {
	cfg, err := LoadConfig()
	if err != nil {
		log.Fatalf("Failed to load config: %v", err)
	}
	return cfg
}
