package config

import (
	"fmt"
	"os"
	"path/filepath"
	"strconv"
)

type Config struct {
	// Server
	Port string

	// Database
	DatabaseURL string

	// Storage
	RepoRoot     string
	ArtifactRoot string

	// Security
	SessionSecret string // must be 32 or 64 bytes for AES-GCM
	CSRFSecret    string // must be 32 bytes

	// OIDC (optional)
	OIDCIssuer       string
	OIDCClientID     string
	OIDCClientSecret string

	// Event bus
	NATSUrl string

	// GitOps
	GitOpsReconcileInterval int // seconds between periodic drift checks; 0 disables

	// Federation
	InstanceURL  string
	InstanceName string

	// Artifact signing (Phase 4)
	// PEM-encoded ECDSA P-256 private key.  If empty an ephemeral key is generated.
	ArtifactSigningKey string

	// OCI Registry
	OCIRoot string

	// Dev
	Debug bool
}

func Load() (*Config, error) {
	repoRoot := getEnv("REPO_ROOT", "/var/lib/forgebucket/repos")
	cfg := &Config{
		Port:         getEnv("PORT", "8080"),
		RepoRoot:     repoRoot,
		ArtifactRoot: getEnv("ARTIFACT_ROOT", filepath.Join(filepath.Dir(repoRoot), "artifacts")),
		Debug:        getEnvBool("DEBUG", false),

		NATSUrl:                 getEnv("NATS_URL", ""),
		GitOpsReconcileInterval: getEnvInt("GITOPS_RECONCILE_INTERVAL", 300),
		InstanceURL:  getEnv("INSTANCE_URL", ""),
		InstanceName: getEnv("INSTANCE_NAME", "ForgeBucket"),
	}

	var missing []string

	cfg.DatabaseURL = requireEnv("DATABASE_URL", &missing)
	cfg.SessionSecret = requireEnv("SESSION_SECRET", &missing)
	cfg.CSRFSecret = requireEnv("CSRF_SECRET", &missing)

	// Optional signing key
	cfg.ArtifactSigningKey = os.Getenv("ARTIFACT_SIGNING_KEY")
	cfg.OCIRoot = getEnv("OCI_ROOT", filepath.Join(filepath.Dir(cfg.RepoRoot), "oci"))

	// Optional OIDC
	cfg.OIDCIssuer = os.Getenv("OIDC_ISSUER")
	cfg.OIDCClientID = os.Getenv("OIDC_CLIENT_ID")
	cfg.OIDCClientSecret = os.Getenv("OIDC_CLIENT_SECRET")

	if len(missing) > 0 {
		return nil, fmt.Errorf("missing required env vars: %v", missing)
	}

	if len(cfg.SessionSecret) < 32 {
		return nil, fmt.Errorf("SESSION_SECRET must be at least 32 characters")
	}
	if len(cfg.CSRFSecret) != 32 {
		return nil, fmt.Errorf("CSRF_SECRET must be exactly 32 characters")
	}

	return cfg, nil
}

func requireEnv(key string, missing *[]string) string {
	v := os.Getenv(key)
	if v == "" {
		*missing = append(*missing, key)
	}
	return v
}

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

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

func getEnvBool(key string, fallback bool) bool {
	v := os.Getenv(key)
	if v == "" {
		return fallback
	}
	b, err := strconv.ParseBool(v)
	if err != nil {
		return fallback
	}
	return b
}
