package config

import (
	"context"
	"fmt"
	"os"
	"strconv"
)

type Config struct {
	ServerPort        string
	BaseURL           string
	JWTSecret         string
	UploadPath        string
	DatabaseHost      string
	DatabasePort      string
	DatabaseUser      string
	DatabasePassword  string
	DatabaseName      string
	TestMode          bool
	RateLimitEnabled  bool
	RequestsPerMinute int
}

func LoadConfig() *Config {
	port := getEnv("SERVER_PORT", "8765")
	return &Config{
		ServerPort:        port,
		BaseURL:           getEnv("BASE_URL", "http://localhost:"+port),
		DatabaseHost:      getEnv("DATABASE_HOST", "localhost"),
		DatabasePort:      getEnv("DATABASE_PORT", "5432"),
		DatabaseUser:      getEnv("DATABASE_USER", "postgres"),
		DatabasePassword:  getEnv("DATABASE_PASSWORD", "password"),
		DatabaseName:      getEnv("DATABASE_NAME", "bookhoard"),
		JWTSecret:         getEnv("JWT_SECRET", "your-secret-key"),
		UploadPath:        getEnv("UPLOAD_PATH", "./uploads"),
		TestMode:          getEnvBool("TEST_MODE", false),
		RateLimitEnabled:  getEnvBool("RATE_LIMIT_ENABLED", true),
		RequestsPerMinute: getEnvInt("REQUESTS_PER_MINUTE", 10),
	}
}

func (c *Config) DatabaseURL() string {
	return fmt.Sprintf("postgres://%s:%s@%s:%s/%s?sslmode=disable",
		c.DatabaseUser, c.DatabasePassword, c.DatabaseHost, c.DatabasePort, c.DatabaseName)
}

// SystemConfigGetter returns the value for a system config key, or an error.
type SystemConfigGetter func(ctx context.Context, key string) (string, error)

// GetBaseURL returns the base URL from system configuration database, or empty
// string if not set. The getter abstraction avoids importing the database package.
func GetBaseURL(ctx context.Context, getter SystemConfigGetter) string {
	val, err := getter(ctx, "base_url")
	if err == nil && val != "" {
		return val
	}
	return ""
}

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

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

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