package config

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

func init() {
	loadDotEnv()
}

func loadDotEnv() {
	file, err := os.Open(".env")
	if err != nil {
		return
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		if line == "" || strings.HasPrefix(line, "#") {
			continue
		}
		parts := strings.SplitN(line, "=", 2)
		if len(parts) != 2 {
			continue
		}
		key := strings.TrimSpace(parts[0])
		val := strings.TrimSpace(parts[1])
		if (strings.HasPrefix(val, "\"") && strings.HasSuffix(val, "\"")) ||
			(strings.HasPrefix(val, "'") && strings.HasSuffix(val, "'")) {
			val = val[1 : len(val)-1]
		}
		if os.Getenv(key) == "" {
			os.Setenv(key, val)
		}
	}
}

type Config struct {
	Host     string
	Port     string
	Database DatabaseConfig
	Valkey   ValkeyConfig
	MinIO    MinIOConfig
	LiveKit  LiveKitConfig
	WebPush  WebPushConfig
	Session  SessionConfig
	Giphy    GiphyConfig
	SMTP     SMTPConfig
}

type SMTPConfig struct {
	Host     string
	Port     string
	Username string
	Password string
}

type DatabaseConfig struct {
	Host     string
	Port     string
	User     string
	Password string
	Database string
	SSLMode  string
}

type ValkeyConfig struct {
	URL string
}

type MinIOConfig struct {
	Endpoint  string
	AccessKey string
	SecretKey string
	Bucket    string
	UseSSL    bool
}

type LiveKitConfig struct {
	URL    string
	APIKey string
	Secret string
}

type WebPushConfig struct {
	PublicKey  string
	PrivateKey string
	Subject    string
}

type GiphyConfig struct {
	APIKey string
}

type SessionConfig struct {
	CookieName string
	Duration   time.Duration
}

func Load() *Config {
	return &Config{
		Host: getEnv("DUMPSTER_HOST", "localhost"),
		Port: getEnv("DUMPSTER_PORT", "8080"),
		Database: DatabaseConfig{
			Host:     getEnv("POSTGRES_HOST", "localhost"),
			Port:     getEnv("POSTGRES_PORT", "5432"),
			User:     getEnv("POSTGRES_USER", "dumpster"),
			Password: getEnv("POSTGRES_PASSWORD", "dumpster"),
			Database: getEnv("POSTGRES_DB", "dumpster"),
			SSLMode:  getEnv("POSTGRES_SSLMODE", "require"),
		},
		Valkey: ValkeyConfig{
			URL: getEnv("VALKEY_URL", "redis://localhost:6379"),
		},
		MinIO: MinIOConfig{
			Endpoint:  getEnv("MINIO_ENDPOINT", ""),
			AccessKey: getEnv("MINIO_ACCESS_KEY", ""),
			SecretKey: getEnv("MINIO_SECRET_KEY", ""),
			Bucket:    getEnv("MINIO_BUCKET", "dumpster-files"),
			UseSSL:    getBool("MINIO_USE_SSL", false),
		},
		Session: SessionConfig{
			CookieName: getEnv("DUMPSTER_SESSION_COOKIE", "dumpster_session"),
			Duration:   time.Duration(getInt("DUMPSTER_SESSION_DAYS", 30)) * 24 * time.Hour,
		},
		LiveKit: LiveKitConfig{
			URL:    getEnv("LIVEKIT_URL", ""),
			APIKey: getEnv("LIVEKIT_API_KEY", ""),
			Secret: getEnv("LIVEKIT_API_SECRET", ""),
		},
		WebPush: WebPushConfig{
			PublicKey:  getEnv("VAPID_PUBLIC_KEY", ""),
			PrivateKey: getEnv("VAPID_PRIVATE_KEY", ""),
			Subject:    getEnv("VAPID_SUBJECT", ""),
		},
		Giphy: GiphyConfig{
			APIKey: getEnv("GIPHY_API_KEY", ""),
		},
		SMTP: SMTPConfig{
			Host:     getEnv("SMTP_HOST", ""),
			Port:     getEnv("SMTP_PORT", ""),
			Username: getEnv("SMTP_USERNAME", ""),
			Password: getEnv("SMTP_PASSWORD", ""),
		},
	}
}

func (c *Config) AppURL() string {
	if v := getEnv("APP_URL", ""); v != "" {
		return v
	}
	scheme := "http"
	port := c.Port
	if c.Database.SSLMode == "require" {
		// ponytail: using SSLMode as a rough https hint; add explicit APP_URL for real setups
		scheme = "https"
	}
	if port == "80" || port == "443" {
		return fmt.Sprintf("%s://%s", scheme, c.Host)
	}
	return fmt.Sprintf("%s://%s:%s", scheme, c.Host, port)
}

func (c *Config) DatabaseDSN() string {
	return "postgres://" + c.Database.User + ":" + c.Database.Password + "@" + c.Database.Host + ":" + c.Database.Port + "/" + c.Database.Database + "?sslmode=" + c.Database.SSLMode
}

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

func getBool(key string, fallback bool) bool {
	s := strings.ToLower(getEnv(key, ""))
	if s == "" {
		return fallback
	}
	return s == "true" || s == "1" || s == "yes"
}

func getInt(key string, fallback int) int {
	s := getEnv(key, "")
	if s == "" {
		return fallback
	}
	n, err := strconv.Atoi(s)
	if err != nil {
		return fallback
	}
	return n
}
