package config

import (
	"fmt"
	"os"
	"strconv"

	"github.com/joho/godotenv"
)

// Config holds all application configuration.
type Config struct {
	Port        string
	DatabaseURL string
	DBDriver    string // "postgres" (default) or "sqlite"
	JWTSecret   string
	Environment string
	BasePath    string // URL path prefix (e.g. "/dev", "/test", or "" for root)

	// Admin bootstrap (optional — set via K8s secret)
	AdminUsername string
	AdminPassword string
	AdminEmail    string

	// Seed users (dev/test only) — CSV: "user:pass:role,user2:pass2:role2"
	SeedUsers string

	// Seed providers (dev/test only) — CSV: "provider:api_key[:name]"
	// Shortcuts: "openai:sk-xxx", "anthropic:sk-ant-xxx", "openrouter:sk-or-xxx"
	// Skips if provider with same name exists (idempotent on restart).
	SeedProviders string

	// API key encryption (required for v0.9.4+)
	// Used to derive AES-256 key for global/team provider API keys.
	// Personal keys use per-user UEK (derived from password).
	EncryptionKey string

	// File storage (v0.12.0+)
	// STORAGE_BACKEND: "pvc" or "s3". Empty = auto-detect (pvc if path writable).
	// STORAGE_PATH: mount point for PVC backend (default /data/storage).
	StorageBackend string
	StoragePath    string

	// S3-compatible storage (v0.12.0+)
	// Works with AWS S3, MinIO, Ceph RGW, or any S3-compatible API.
	S3Endpoint       string // custom endpoint URL (required for MinIO/Ceph, optional for AWS)
	S3Bucket         string // bucket name (required when STORAGE_BACKEND=s3)
	S3Region         string // AWS region (default "us-east-1")
	S3AccessKey      string // access key ID
	S3SecretKey      string // secret access key
	S3Prefix         string // optional key prefix within bucket (e.g. "switchboard/")
	S3ForcePathStyle bool   // use path-style URLs (required for MinIO, most self-hosted)

	// Extraction pipeline (v0.12.0+)
	// EXTRACTION_MODE: "inline" (in-process) or "sidecar" (shared PVC watcher).
	// EXTRACTION_CONCURRENCY: max concurrent extraction jobs (default 3).
	ExtractionMode        string
	ExtractionConcurrency int

	// Workspace indexing (v0.21.2)
	// WORKSPACE_INDEXING_ENABLED: global kill switch for workspace file indexing (default true).
	// WORKSPACE_INDEX_CONCURRENCY: max concurrent indexing goroutines (default 2).
	WorkspaceIndexingEnabled bool
	WorkspaceIndexConcurrency int

	// Provider auto-disable (v0.22.4)
	// PROVIDER_AUTO_DISABLE_THRESHOLD: consecutive "down" hourly windows before a
	// provider is automatically deactivated. Default 3. Set to 0 to disable.
	ProviderAutoDisableThreshold int

	// Config-file provider types (v0.29.1)
	// PROVIDER_TYPES_FILE: path to JSON file defining custom provider types
	// (e.g., Ollama, LiteLLM, vLLM). Empty = no custom types.
	ProviderTypesFile string

	// SESSION_EXPIRY_DAYS: anonymous sessions older than this with no messages
	// are cleaned up by the background session sweeper. Default 30. Set to 0
	// to disable cleanup.
	SessionExpiryDays int

	// WORKFLOW_STALE_HOURS: workflow instances with no activity for this many
	// hours are marked 'stale'. Default 72 (3 days). Set to 0 to disable.
	WorkflowStaleHours int

	// Structured logging (v0.33.0)
	// LOG_FORMAT: "text" (default, backward-compatible) or "json" (structured).
	// LOG_LEVEL: "debug", "info" (default), "warn", "error".
	LogFormat string
	LogLevel  string

	// Auth mode (v0.24.0): "builtin" (default) | "mtls" | "oidc"
	AuthMode string

	// mTLS (v0.24.1)
	// Headers injected by TLS-terminating reverse proxy.
	MTLSHeaderDN          string // default "X-SSL-Client-DN"
	MTLSHeaderVerify      string // default "X-SSL-Client-Verify"
	MTLSHeaderFingerprint string // default "X-SSL-Client-Fingerprint"
	MTLSAutoActivate      bool   // auto-activate new users (default true)
	MTLSDefaultTeam       string // team ID for auto-provisioned users (optional)
	MTLSDefaultRole       string // "user" (default) or "admin"

	// OIDC (v0.24.1)
	OIDCIssuerURL    string // e.g. "https://keycloak.corp/realms/switchboard"
	OIDCExternalIssuerURL string  // OIDC_EXTERNAL_ISSUER_URL
	OIDCClientID     string
	OIDCClientSecret string
	OIDCRedirectURL  string // e.g. "https://switchboard.corp/api/v1/auth/oidc/callback"
	OIDCAutoActivate bool   // auto-activate new users (default true)
	OIDCDefaultTeam  string // team ID for auto-provisioned users (optional)
	OIDCDefaultRole  string // "user" (default) or "admin"
	OIDCRolesClaim   string // JWT claim for role mapping (default "realm_access.roles")
	OIDCGroupsClaim  string // JWT claim for group sync (default "groups")
	OIDCAdminRole    string // IdP role value that maps to admin (default "admin")
}

// Load reads configuration from environment variables.
// A .env file is loaded if present but not required.
func Load() *Config {
	// Best-effort .env load (not required in production)
	_ = godotenv.Load()

	return &Config{
		Port:          getEnv("PORT", "8080"),
		DatabaseURL:   resolveDatabaseURL(),
		DBDriver:      getEnv("DB_DRIVER", ""),
		JWTSecret:     getEnv("JWT_SECRET", "dev-secret-change-me"),
		Environment:   getEnv("ENVIRONMENT", "development"),
		BasePath:      sanitizeBasePath(getEnv("BASE_PATH", "")),
		AdminUsername: getEnv("SWITCHBOARD_ADMIN_USERNAME", ""),
		AdminPassword: getEnv("SWITCHBOARD_ADMIN_PASSWORD", ""),
		AdminEmail:    getEnv("SWITCHBOARD_ADMIN_EMAIL", ""),
		SeedUsers:     getEnv("SEED_USERS", ""),
		SeedProviders: getEnv("SEED_PROVIDERS", ""),
		EncryptionKey: getEnv("ENCRYPTION_KEY", ""),

		StorageBackend:        getEnv("STORAGE_BACKEND", ""),
		StoragePath:           getEnv("STORAGE_PATH", "/data/storage"),
		S3Endpoint:            getEnv("S3_ENDPOINT", ""),
		S3Bucket:              getEnv("S3_BUCKET", ""),
		S3Region:              getEnv("S3_REGION", "us-east-1"),
		S3AccessKey:           getEnv("S3_ACCESS_KEY", ""),
		S3SecretKey:           getEnv("S3_SECRET_KEY", ""),
		S3Prefix:              getEnv("S3_PREFIX", ""),
		S3ForcePathStyle:      getEnv("S3_FORCE_PATH_STYLE", "true") == "true",
		ExtractionMode:        getEnv("EXTRACTION_MODE", "inline"),
		ExtractionConcurrency: getEnvInt("EXTRACTION_CONCURRENCY", 3),

		WorkspaceIndexingEnabled:  getEnvBool("WORKSPACE_INDEXING_ENABLED", true),
		WorkspaceIndexConcurrency: getEnvInt("WORKSPACE_INDEX_CONCURRENCY", 2),

		ProviderAutoDisableThreshold: getEnvInt("PROVIDER_AUTO_DISABLE_THRESHOLD", 3),

		ProviderTypesFile: getEnv("PROVIDER_TYPES_FILE", ""),

		SessionExpiryDays: getEnvInt("SESSION_EXPIRY_DAYS", 30),

		WorkflowStaleHours: getEnvInt("WORKFLOW_STALE_HOURS", 72),

		LogFormat: getEnv("LOG_FORMAT", "text"),
		LogLevel:  getEnv("LOG_LEVEL", "info"),

		AuthMode: getEnv("AUTH_MODE", "builtin"),

		// mTLS
		MTLSHeaderDN:          getEnv("MTLS_HEADER_DN", "X-SSL-Client-DN"),
		MTLSHeaderVerify:      getEnv("MTLS_HEADER_VERIFY", "X-SSL-Client-Verify"),
		MTLSHeaderFingerprint: getEnv("MTLS_HEADER_FINGERPRINT", "X-SSL-Client-Fingerprint"),
		MTLSAutoActivate:      getEnvBool("MTLS_AUTO_ACTIVATE", true),
		MTLSDefaultTeam:       getEnv("MTLS_DEFAULT_TEAM", ""),
		MTLSDefaultRole:       getEnv("MTLS_DEFAULT_ROLE", "user"),

		// OIDC
		OIDCIssuerURL:            getEnv("OIDC_ISSUER_URL", ""),
		OIDCExternalIssuerURL:    getEnv("OIDC_EXTERNAL_ISSUER_URL", ""),
		OIDCClientID:     getEnv("OIDC_CLIENT_ID", ""),
		OIDCClientSecret: getEnv("OIDC_CLIENT_SECRET", ""),
		OIDCRedirectURL:  getEnv("OIDC_REDIRECT_URL", ""),
		OIDCAutoActivate: getEnvBool("OIDC_AUTO_ACTIVATE", true),
		OIDCDefaultTeam:  getEnv("OIDC_DEFAULT_TEAM", ""),
		OIDCDefaultRole:  getEnv("OIDC_DEFAULT_ROLE", "user"),
		OIDCRolesClaim:   getEnv("OIDC_ROLES_CLAIM", "realm_access.roles"),
		OIDCGroupsClaim:  getEnv("OIDC_GROUPS_CLAIM", "groups"),
		OIDCAdminRole:    getEnv("OIDC_ADMIN_ROLE", "admin"),
	}
}

// resolveDatabaseURL returns the database connection string.
//
// Resolution order:
//  1. DATABASE_URL — explicit DSN (K8s, .env, manual)
//  2. POSTGRES_* vars — assembled from individual env vars:
//     POSTGRES_HOST, POSTGRES_PORT, POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB
//     (set by K8s manifest and docker-compose)
//  3. Empty string — backend runs without database
func resolveDatabaseURL() string {
	if dsn := os.Getenv("DATABASE_URL"); dsn != "" {
		return dsn
	}
	// Assemble from individual vars (K8s sets POSTGRES_*, docker-compose may too)
	host := getEnv("POSTGRES_HOST", "")
	if host == "" {
		return ""
	}
	port := getEnv("POSTGRES_PORT", "5432")
	user := getEnv("POSTGRES_USER", "")
	pass := getEnv("POSTGRES_PASSWORD", "")
	db := getEnv("POSTGRES_DB", "chat_switchboard")
	ssl := getEnv("POSTGRES_SSLMODE", "disable")
	return fmt.Sprintf("postgres://%s:%s@%s:%s/%s?sslmode=%s", user, pass, host, port, db, ssl)
}

// sanitizeBasePath ensures the path starts with / and doesn't end with /.
// Empty string means root (no prefix).
func sanitizeBasePath(p string) string {
	if p == "" || p == "/" {
		return ""
	}
	// Ensure leading slash
	if p[0] != '/' {
		p = "/" + p
	}
	// Strip trailing slash
	for len(p) > 1 && p[len(p)-1] == '/' {
		p = p[:len(p)-1]
	}
	return p
}

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
}

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