//loads environment-based configuration.
package config

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

const (
	defaultPort = 3001
	defaultDBPort = 5432
	defaultPollIntervalMS = 60000
	minPollIntervalMS = 1000
	defaultDigestIntervalHours = 24
)

type Config struct {
	Port              int
	APIBasePath       string
	DatabaseURL       string
	DBHost            string
	DBPort            int
	DBUser            string
	DBPassword        string
	DBName            string
	FirebaseProjectID string
	EthRPCURL         string
	PollIntervalMS    int
	NodeEnv           string
	ResendAPIKey         string
	EmailFrom            string
	DigestIntervalHours  int
	StripeSecretKey      string
	StripeWebhookSecret  string
	StripePriceID        string
	StripePublishableKey string
	FrontendURL          string
}

// Load reads configuration from environment variables and returns a Config.
func Load() (*Config, error) {
	cfg := &Config{
		Port:              getEnvInt("PORT", defaultPort),
		APIBasePath:       getEnv("API_BASE_PATH", "/v1"),
		DatabaseURL:       os.Getenv("DATABASE_URL"),
		DBHost:            getEnv("DB_HOST", "localhost"),
		DBPort:            getEnvInt("DB_PORT", defaultDBPort),
		DBUser:            os.Getenv("DB_USER"),
		DBPassword:        os.Getenv("DB_PASSWORD"),
		DBName:            os.Getenv("DB_NAME"),
		FirebaseProjectID: os.Getenv("FIREBASE_PROJECT_ID"),
		EthRPCURL:         os.Getenv("ETH_RPC_URL"),
		PollIntervalMS:    getEnvInt("POLL_INTERVAL_MS", defaultPollIntervalMS),
		NodeEnv:           getEnv("NODE_ENV", "development"),
		ResendAPIKey:        os.Getenv("RESEND_API_KEY"),
		EmailFrom:           getEnv("EMAIL_FROM", "Koin Ping <alerts@koinping.com>"),
		DigestIntervalHours:  getEnvInt("DIGEST_INTERVAL_HOURS", defaultDigestIntervalHours),
		StripeSecretKey:      os.Getenv("STRIPE_SECRET_KEY"),
		StripeWebhookSecret:  os.Getenv("STRIPE_WEBHOOK_SECRET"),
		StripePriceID:        os.Getenv("STRIPE_PRICE_ID"),
		StripePublishableKey: os.Getenv("STRIPE_PUBLISHABLE_KEY"),
		FrontendURL:          getEnv("FRONTEND_URL", "http://localhost:3000"),
	}

	if cfg.PollIntervalMS < minPollIntervalMS {
		return nil, fmt.Errorf("POLL_INTERVAL_MS must be >= 1000, got %d", cfg.PollIntervalMS) //nolint:err113
	}

	return cfg, nil
}

func (c *Config) DSN() string {
	if c.DatabaseURL != "" {
		// Append sslmode=disable if not already specified.
		if !strings.Contains(c.DatabaseURL, "sslmode=") {
			sep := "?"
			if strings.Contains(c.DatabaseURL, "?") {
				sep = "&"
			}

			return c.DatabaseURL + sep + "sslmode=disable"
		}

		return c.DatabaseURL
	}

	return fmt.Sprintf(
		"host=%s port=%d user=%s password=%s dbname=%s sslmode=disable",
		c.DBHost, c.DBPort, c.DBUser, c.DBPassword, c.DBName,
	)
}

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

	return fallback
}

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

	return fallback
}
