package config

import (
	"errors"
	"log/slog"
	"net/url"
	"os"
	"strconv"
	"strings"
)

const (
	DefaultLocalGeneratedStorageDir = "data/static/generated"
	DefaultLocalUploadedStorageDir  = "data/static/uploaded"
)

type Config struct {
	AppEnv                                        string
	HTTPAddr                                      string
	DatabaseURL                                   string
	IdentityMode                                  string
	JWTSecret                                     string
	ServerMainBaseURL                             string
	ServerMainInternalToken                       string
	ServerMainInternalKey                         string
	ServerMainInternalSecret                      string
	IdentitySecretStore                           string
	IdentitySecretDir                             string
	IdentityKubernetesNamespace                   string
	IdentityKubernetesSecretName                  string
	IdentityKubernetesAPIServer                   string
	IdentityKubernetesTokenFile                   string
	IdentityKubernetesCAFile                      string
	IdentitySecurityEventHeartbeatIntervalSeconds int
	IdentitySecurityEventStaleAfterSeconds        int
	IdentitySecurityEventClockSkewSeconds         int
	PublicBaseURL                                 string
	WebBaseURL                                    string
	LocalGeneratedStorageDir                      string
	LocalUploadedStorageDir                       string
	LocalTempAssetTTLHours                        int
	TaskProgressCallbackEnabled                   bool
	TaskProgressCallbackURL                       string
	TaskProgressCallbackTimeoutMS                 string
	TaskProgressCallbackMaxAttempts               string
	CORSAllowedOrigin                             string
	GlobalHTTPProxy                               string
	GlobalHTTPProxySource                         string
	LogLevel                                      slog.Level
	BillingEngineMode                             string
}

func Load() Config {
	globalProxy := LoadGlobalHTTPProxyStatus()
	appEnv := env("APP_ENV", "development")
	return Config{
		AppEnv:       appEnv,
		HTTPAddr:     env("HTTP_ADDR", ":8088"),
		DatabaseURL:  gatewayDatabaseURL(),
		IdentityMode: env("IDENTITY_MODE", "hybrid"),
		JWTSecret:    env("CONFIG_JWT_SECRET", "this is a very secret secret"),
		ServerMainBaseURL: strings.TrimRight(
			env("SERVER_MAIN_BASE_URL", "http://localhost:3000"),
			"/",
		),
		ServerMainInternalToken:                       env("SERVER_MAIN_INTERNAL_TOKEN", ""),
		ServerMainInternalKey:                         env("SERVER_MAIN_INTERNAL_KEY", "gateway"),
		ServerMainInternalSecret:                      env("SERVER_MAIN_INTERNAL_SECRET", env("SERVER_MAIN_INTERNAL_TOKEN", "")),
		IdentitySecretStore:                           env("IDENTITY_SECRET_STORE", "file"),
		IdentitySecretDir:                             env("IDENTITY_SECRET_DIR", ".local-secrets/identity"),
		IdentityKubernetesNamespace:                   env("IDENTITY_KUBERNETES_NAMESPACE", env("POD_NAMESPACE", "")),
		IdentityKubernetesSecretName:                  env("IDENTITY_KUBERNETES_SECRET_NAME", "easyai-gateway-identity"),
		IdentityKubernetesAPIServer:                   env("IDENTITY_KUBERNETES_API_SERVER", "https://kubernetes.default.svc"),
		IdentityKubernetesTokenFile:                   env("IDENTITY_KUBERNETES_TOKEN_FILE", "/var/run/secrets/kubernetes.io/serviceaccount/token"),
		IdentityKubernetesCAFile:                      env("IDENTITY_KUBERNETES_CA_FILE", "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"),
		IdentitySecurityEventHeartbeatIntervalSeconds: envInt("IDENTITY_SECURITY_EVENTS_HEARTBEAT_INTERVAL_SECONDS", 60),
		IdentitySecurityEventStaleAfterSeconds:        envInt("IDENTITY_SECURITY_EVENTS_STALE_AFTER_SECONDS", 180),
		IdentitySecurityEventClockSkewSeconds:         envInt("IDENTITY_SECURITY_EVENTS_CLOCK_SKEW_SECONDS", 60),
		PublicBaseURL:                                 strings.TrimRight(env("AI_GATEWAY_PUBLIC_BASE_URL", env("PUBLIC_BASE_URL", "")), "/"),
		WebBaseURL:                                    strings.TrimRight(env("AI_GATEWAY_WEB_BASE_URL", env("GATEWAY_WEB_BASE_URL", env("PUBLIC_WEB_BASE_URL", ""))), "/"),
		LocalGeneratedStorageDir:                      env("AI_GATEWAY_GENERATED_STORAGE_DIR", env("LOCAL_GENERATED_STORAGE_DIR", env("AI_GATEWAY_STATIC_STORAGE_DIR", DefaultLocalGeneratedStorageDir))),
		LocalUploadedStorageDir:                       env("AI_GATEWAY_UPLOADED_STORAGE_DIR", env("LOCAL_UPLOADED_STORAGE_DIR", DefaultLocalUploadedStorageDir)),
		LocalTempAssetTTLHours:                        envInt("AI_GATEWAY_LOCAL_TEMP_ASSET_TTL_HOURS", 24),
		TaskProgressCallbackEnabled:                   env("TASK_PROGRESS_CALLBACK_ENABLED", "true") == "true",
		TaskProgressCallbackURL: env("TASK_PROGRESS_CALLBACK_URL",
			strings.TrimRight(env("SERVER_MAIN_BASE_URL", "http://localhost:3000"), "/")+"/internal/platform/task-progress-callbacks",
		),
		TaskProgressCallbackTimeoutMS:   env("TASK_PROGRESS_CALLBACK_TIMEOUT_MS", "5000"),
		TaskProgressCallbackMaxAttempts: env("TASK_PROGRESS_CALLBACK_MAX_ATTEMPTS", "10"),
		CORSAllowedOrigin:               env("CORS_ALLOWED_ORIGIN", "http://localhost:5178,http://127.0.0.1:5178"),
		GlobalHTTPProxy:                 globalProxy.HTTPProxy,
		GlobalHTTPProxySource:           globalProxy.Source,
		LogLevel:                        logLevel(env("LOG_LEVEL", "info")),
		BillingEngineMode:               strings.ToLower(env("BILLING_ENGINE_MODE", "observe")),
	}
}

func (c Config) Validate() error {
	switch strings.ToLower(strings.TrimSpace(c.BillingEngineMode)) {
	case "", "observe", "enforce", "hold":
	default:
		return errors.New("BILLING_ENGINE_MODE must be observe, enforce, or hold")
	}
	switch strings.ToLower(strings.TrimSpace(c.IdentitySecretStore)) {
	case "":
	case "file":
		if strings.TrimSpace(c.IdentitySecretDir) == "" {
			return errors.New("IDENTITY_SECRET_DIR is required for the file SecretStore")
		}
	case "kubernetes":
		if strings.TrimSpace(c.IdentityKubernetesNamespace) == "" || strings.TrimSpace(c.IdentityKubernetesSecretName) == "" {
			return errors.New("Kubernetes identity SecretStore requires namespace and Secret name")
		}
	default:
		return errors.New("IDENTITY_SECRET_STORE must be file or kubernetes")
	}
	if c.IdentitySecurityEventHeartbeatIntervalSeconds != 0 || c.IdentitySecurityEventStaleAfterSeconds != 0 || c.IdentitySecurityEventClockSkewSeconds != 0 {
		if c.IdentitySecurityEventHeartbeatIntervalSeconds <= 0 ||
			c.IdentitySecurityEventStaleAfterSeconds < 2*c.IdentitySecurityEventHeartbeatIntervalSeconds ||
			c.IdentitySecurityEventClockSkewSeconds < 0 || c.IdentitySecurityEventClockSkewSeconds > 300 {
			return errors.New("identity security event heartbeat, stale threshold, or clock skew is invalid")
		}
	}
	return nil
}

type GlobalHTTPProxyStatus struct {
	HTTPProxy string
	Source    string
}

func LoadGlobalHTTPProxyStatus() GlobalHTTPProxyStatus {
	for _, key := range []string{
		"AI_GATEWAY_GLOBAL_HTTP_PROXY",
		"GLOBAL_HTTP_PROXY",
		"HTTPS_PROXY",
		"https_proxy",
		"HTTP_PROXY",
		"http_proxy",
		"ALL_PROXY",
		"all_proxy",
	} {
		if value := envValue(key); value != "" {
			return GlobalHTTPProxyStatus{HTTPProxy: value, Source: key}
		}
	}
	return GlobalHTTPProxyStatus{}
}

func gatewayDatabaseURL() string {
	if value := envValue("AI_GATEWAY_DATABASE_URL"); value != "" {
		return normalizePostgresURL(value)
	}
	if value := envValue("DATABASE_URL"); value != "" {
		return normalizePostgresURL(value)
	}
	if memoryURL := envValue("MEMORY_DATABASE_URL"); memoryURL != "" {
		return normalizePostgresURL(withDatabase(memoryURL, env("AI_GATEWAY_DATABASE_NAME", "easyai_ai_gateway")))
	}
	return normalizePostgresURL("postgresql://easyai:easyai2025@localhost:5432/easyai_ai_gateway?sslmode=disable")
}

func normalizePostgresURL(raw string) string {
	parsed, err := url.Parse(raw)
	if err != nil {
		return raw
	}
	values := parsed.Query()
	schema := values.Get("schema")
	if schema == "" {
		return raw
	}
	values.Del("schema")
	if values.Get("search_path") == "" {
		values.Set("search_path", schema)
	}
	parsed.RawQuery = values.Encode()
	return parsed.String()
}

func withDatabase(raw string, databaseName string) string {
	parsed, err := url.Parse(raw)
	if err != nil || databaseName == "" {
		return raw
	}
	parsed.Path = "/" + databaseName
	return parsed.String()
}

func envValue(key string) string {
	return strings.TrimSpace(os.Getenv(key))
}

func env(key string, fallback string) string {
	if value := envValue(key); value != "" {
		return value
	}
	return fallback
}

func envInt(key string, fallback int) int {
	value := envValue(key)
	if value == "" {
		return fallback
	}
	parsed, err := strconv.Atoi(value)
	if err != nil {
		return fallback
	}
	return parsed
}

func logLevel(value string) slog.Level {
	switch strings.ToLower(value) {
	case "debug":
		return slog.LevelDebug
	case "warn", "warning":
		return slog.LevelWarn
	case "error":
		return slog.LevelError
	default:
		return slog.LevelInfo
	}
}
