package config

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/go-playground/validator/v10"
)

type Config struct {
	Server       ServerConfig                `mapstructure:"server"`
	Databases    map[string]DatabaseConfig   `mapstructure:"databases"`
	ReadReplicas map[string][]DatabaseConfig `mapstructure:"read_replicas"`
	Auth         AuthConfig                  `mapstructure:"auth"`
	Keycloak     KeycloakConfig              `mapstructure:"keycloak"`
	Bpjs         BpjsConfig                  `mapstructure:"bpjs"`
	SatuSehat    SatuSehatConfig             `mapstructure:"satu_sehat"`
	Swagger      SwaggerConfig               `mapstructure:"swagger"`
	Security     SecurityConfig              `mapstructure:"security"`
	Cache        CacheConfig                 `mapstructure:"cache"`  // Tambahkan ini
	Logger       LoggerConfig                `mapstructure:"logger"` // Tambahkan ini
	Minio        MinioConfig                 `mapstructure:"minio"`
	Validator    *validator.Validate         `mapstructure:"-"`
}

type ServerConfig struct {
	REST         ServerRESTConfig `mapstructure:"rest"`
	GRPC         ServerGRPCConfig `mapstructure:"grpc"`
	Port         int              `mapstructure:"port"`
	Mode         string           `mapstructure:"mode"`
	ReadTimeout  int              `mapstructure:"read_timeout"`
	WriteTimeout int              `mapstructure:"write_timeout"`
}

type ServerRESTConfig struct {
	Enabled bool `mapstructure:"enabled" validate:"required"`
	Port    int  `mapstructure:"port" validate:"required,min=1,max=65535"`
}

type ServerGRPCConfig struct {
	Enabled bool `mapstructure:"enabled" validate:"required"`
	Port    int  `mapstructure:"port" validate:"required,min=1,max=65535"`
}

// GRPCConfig untuk pengaturan spesifik server gRPC.
type GRPCConfig struct {
	ReflectionEnabled bool          `mapstructure:"reflection_enabled"`
	Timeout           time.Duration `mapstructure:"timeout" validate:"required"`
}

type DatabaseConfig struct {
	Name            string        `mapstructure:"name"`
	Type            string        `mapstructure:"type"`
	Host            string        `mapstructure:"host"`
	Port            int           `mapstructure:"port"`
	Username        string        `mapstructure:"username"`
	Password        string        `mapstructure:"password"`
	Database        string        `mapstructure:"database"`
	Schema          string        `mapstructure:"schema"`
	SSLMode         string        `mapstructure:"ssl_mode"`
	Path            string        `mapstructure:"path"`
	Options         string        `mapstructure:"options"`
	MaxOpenConns    int           `mapstructure:"max_open_conns"`
	MaxIdleConns    int           `mapstructure:"max_idle_conns"`
	ConnMaxLifetime time.Duration `mapstructure:"conn_max_lifetime"`
	// Security settings
	RequireSSL       bool          `mapstructure:"require_ssl"`
	SSLRootCert      string        `mapstructure:"ssl_root_cert"`
	SSLCert          string        `mapstructure:"ssl_cert"`
	SSLKey           string        `mapstructure:"ssl_key"`
	Timeout          time.Duration `mapstructure:"timeout"`
	ConnectTimeout   time.Duration `mapstructure:"connect_timeout"`
	ReadTimeout      time.Duration `mapstructure:"read_timeout"`
	WriteTimeout     time.Duration `mapstructure:"write_timeout"`
	StatementTimeout time.Duration `mapstructure:"statement_timeout"`
	// Connection pool settings
	MaxLifetime       time.Duration `mapstructure:"max_lifetime"`
	MaxIdleTime       time.Duration `mapstructure:"max_idle_time"`
	HealthCheckPeriod time.Duration `mapstructure:"health_check_period"`
	IsActive          bool          `mapstructure:"is_active"`
}

type AuthConfig struct {
	Type         string   `mapstructure:"type"`
	StaticTokens []string `mapstructure:"static_tokens"`
	FallbackTo   string   `mapstructure:"fallback_to"`
}

type KeycloakConfig struct {
	Enabled      bool   `mapstructure:"enabled"`
	URL          string `mapstructure:"url"`           // URL base Keycloak, e.g., http://localhost:8080
	Realm        string `mapstructure:"realm"`         // Realm yang digunakan
	ClientID     string `mapstructure:"client_id"`     // Client ID dari service-account
	ClientSecret string `mapstructure:"client_secret"` // Client Secret
	// Fields untuk validasi token (jika service ini juga memvalidasi)
	Issuer   string `mapstructure:"issuer"`   // e.g., http://localhost:8080/realms/my-realm
	Audience string `mapstructure:"audience"` // Biasanya sama dengan ClientID
	JwksURL  string `mapstructure:"jwks_url"` // URL untuk mengambil public keys
}

type BpjsConfig struct {
	Enabled         bool          `mapstructure:"enabled"`
	KdPpk           string        `mapstructure:"kd_ppk"`
	BaseURL         string        `mapstructure:"base_url"`
	ServiceName     string        `mapstructure:"service_name"`
	ConsID          string        `mapstructure:"cons_id"`
	SecretKey       string        `mapstructure:"secret_key"`
	ApotekConsID    string        `mapstructure:"apotek_cons_id"`
	ApotekSecretKey string        `mapstructure:"apotek_secret_key"`
	UserKey         string        `mapstructure:"user_key"` // Internal active UserKey
	VclaimUserKey   string        `mapstructure:"vclaim_user_key"`
	AntrolUserKey   string        `mapstructure:"antrol_user_key"`   // Tambahan untuk Antrean RS
	ApotekUserKey   string        `mapstructure:"apotek_user_key"`   // Tambahan untuk Apotek
	AplicareUserKey string        `mapstructure:"aplicare_user_key"` // Tambahan untuk Aplicares
	IhsUserKey      string        `mapstructure:"ihs_user_key"`      // Tambahan untuk IHS / e-Rekam Medis
	Timeout         time.Duration `mapstructure:"timeout"`
}

type SatuSehatConfig struct {
	Enabled      bool          `mapstructure:"enabled"`
	OrgID        string        `mapstructure:"org_id"`
	FasyakesID   string        `mapstructure:"fasyakes_id"`
	ClientID     string        `mapstructure:"client_id"`
	ClientSecret string        `mapstructure:"client_secret"`
	AuthURL      string        `mapstructure:"auth_url"`
	BaseURL      string        `mapstructure:"base_url"`
	ConsentURL   string        `mapstructure:"consent_url"`
	KFAURL       string        `mapstructure:"kfa_url"`
	Timeout      time.Duration `mapstructure:"timeout"`
}

type SwaggerConfig struct {
	Title          string   `mapstructure:"title"`
	Description    string   `mapstructure:"description"`
	Version        string   `mapstructure:"version"`
	TermsOfService string   `mapstructure:"terms_of_service"`
	ContactName    string   `mapstructure:"contact_name"`
	ContactURL     string   `mapstructure:"contact_url"`
	ContactEmail   string   `mapstructure:"contact_email"`
	LicenseName    string   `mapstructure:"license_name"`
	LicenseURL     string   `mapstructure:"license_url"`
	Host           string   `mapstructure:"host"`
	BasePath       string   `mapstructure:"base_path"`
	Schemes        []string `mapstructure:"schemes"`
}

type SecurityConfig struct {
	TrustedOrigins []string        `mapstructure:"trusted_origins"`
	RateLimit      RateLimitConfig `mapstructure:"rate_limit"`
	MaxInputLength int             `mapstructure:"max_input_length"`
}

type RateLimitConfig struct {
	RequestsPerMinute int         `mapstructure:"requests_per_minute"`
	Redis             RedisConfig `mapstructure:"redis"`
}

type RedisConfig struct {
	Host     string `mapstructure:"host" env:"CACHE_REDIS_HOST"`
	Port     int    `mapstructure:"port" env:"CACHE_REDIS_PORT"`
	Password string `mapstructure:"password" env:"REDIS_PASSWORD"`
	DB       int    `mapstructure:"db" env:"REDIS_DB"`
}

type CacheConfig struct {
	Enabled         bool          `mapstructure:"enabled"`
	DefaultTTL      time.Duration `mapstructure:"default_ttl"`
	SessionTTL      time.Duration `mapstructure:"session_ttl"`
	RateLimitTTL    time.Duration `mapstructure:"rate_limit_ttl"`
	CleanupInterval time.Duration `mapstructure:"cleanup_interval"`
	MaxRetries      int           `mapstructure:"max_retries"`
	RetryDelay      time.Duration `mapstructure:"retry_delay"`
	Redis           RedisConfig   `mapstructure:"redis"`
}

type LoggerConfig struct {
	Level  string `mapstructure:"level"`
	Format string `mapstructure:"format"`
}

type MinioConfig struct {
	Endpoint   string   `mapstructure:"endpoint"`
	Region     string   `mapstructure:"region"`
	AccessKey  string   `mapstructure:"access_key"`
	SecretKey  string   `mapstructure:"secret_key"`
	UseSSL     bool     `mapstructure:"use_ssl"`
	BucketName []string `mapstructure:"bucket_name"`
}

func (cfg BpjsConfig) SetHeader() (string, string, string, string, string) {
	timenow := time.Now().UTC()
	t, err := time.Parse(time.RFC3339, "1970-01-01T00:00:00Z")
	if err != nil {
		log.Fatal(err)
	}

	tstamp := timenow.Unix() - t.Unix()
	secret := []byte(cfg.SecretKey)
	message := []byte(cfg.ConsID + "&" + fmt.Sprint(tstamp))
	hash := hmac.New(sha256.New, secret)
	hash.Write(message)

	// to base64
	xSignature := base64.StdEncoding.EncodeToString(hash.Sum(nil))

	// Gunakan Active UserKey alih-alih selalu menembak VClaim
	return cfg.ConsID, cfg.SecretKey, cfg.UserKey, fmt.Sprint(tstamp), xSignature
}

type ConfigBpjs struct {
	Cons_id    string
	Secret_key string
	User_key   string
}

// SetHeader for backward compatibility
func (cfg ConfigBpjs) SetHeader() (string, string, string, string, string) {
	bpjsConfig := BpjsConfig{
		ConsID:    cfg.Cons_id,
		SecretKey: cfg.Secret_key,
		UserKey:   cfg.User_key,
	}
	return bpjsConfig.SetHeader()
}

func LoadConfig() *Config {
	config := &Config{
		Server: ServerConfig{
			// Baca dari env untuk konfigurasi umum
			Port:         getEnvAsInt("SERVER_PORT", 8080), // Fallback jika REST port tidak set
			Mode:         getEnv("GIN_MODE", "debug"),
			ReadTimeout:  getEnvAsInt("SERVER_READ_TIMEOUT", 10),
			WriteTimeout: getEnvAsInt("SERVER_WRITE_TIMEOUT", 10),

			REST: ServerRESTConfig{
				Enabled: getEnvAsBool("SERVER_REST_ENABLED", true),
				Port:    getEnvAsInt("SERVER_REST_PORT", 8080),
			},
			GRPC: ServerGRPCConfig{
				Enabled: getEnvAsBool("SERVER_GRPC_ENABLED", false),
				Port:    getEnvAsInt("SERVER_GRPC_PORT", 50051),
			},
		},
		Databases:    make(map[string]DatabaseConfig),
		ReadReplicas: make(map[string][]DatabaseConfig),
		Auth:         loadAuthConfig(),
		Keycloak:     loadKeycloakConfig(),

		// ... (Config BPJS, SatuSehat, dll tetap sama seperti kode asli Anda)
		Bpjs: BpjsConfig{
			Enabled:         getEnvAsBool("BPJS_ENABLED", false),
			KdPpk:           strings.TrimSpace(getEnv("BPJS_KDPPK", "")),
			BaseURL:         strings.TrimSpace(getEnv("BPJS_BASEURL", "https://apijkn.bpjs-kesehatan.go.id")),
			ServiceName:     strings.TrimSpace(getEnv("BPJS_SERVICE_NAME", "")),
			ConsID:          strings.TrimSpace(getEnv("BPJS_CONSID", "")),
			SecretKey:       strings.TrimSpace(getEnv("BPJS_SECRETKEY", "")),
			ApotekConsID:    strings.TrimSpace(getEnv("BPJS_APOTEK_CONSID", "")),
			ApotekSecretKey: strings.TrimSpace(getEnv("BPJS_APOTEK_SECRETKEY", "")),
			VclaimUserKey:   strings.TrimSpace(getEnv("BPJS_VCLAIM_USERKEY", getEnv("BPJS_USERKEY", ""))),
			AntrolUserKey:   strings.TrimSpace(getEnv("BPJS_ANTROL_USERKEY", "")),
			ApotekUserKey:   strings.TrimSpace(getEnv("BPJS_APOTEK_USERKEY", "")),
			AplicareUserKey: strings.TrimSpace(getEnv("BPJS_APLICARE_USERKEY", "")),
			IhsUserKey:      strings.TrimSpace(getEnv("BPJS_IHS_USERKEY", "")),
			Timeout:         parseDuration(getEnv("BPJS_TIMEOUT", "30s")),
		},
		SatuSehat: SatuSehatConfig{
			Enabled:      getEnvAsBool("SATUSEHAT_ENABLED", getEnvAsBool("SATU_SEHAT_ENABLED", false)),
			OrgID:        getEnv("SATUSEHAT_ORG_ID", getEnv("BRIDGING_SATUSEHAT_ORG_ID", "")),
			FasyakesID:   getEnv("SATUSEHAT_FASYAKES_ID", getEnv("BRIDGING_SATUSEHAT_FASYAKES_ID", "")),
			ClientID:     getEnv("SATUSEHAT_CLIENT_ID", getEnv("BRIDGING_SATUSEHAT_CLIENT_ID", "")),
			ClientSecret: getEnv("SATUSEHAT_CLIENT_SECRET", getEnv("BRIDGING_SATUSEHAT_CLIENT_SECRET", "")),
			AuthURL:      getEnv("SATUSEHAT_AUTH_URL", getEnv("BRIDGING_SATUSEHAT_AUTH_URL", "https://api-satusehat.kemkes.go.id/oauth2/v1")),
			BaseURL:      getEnv("SATUSEHAT_BASE_URL", getEnv("BRIDGING_SATUSEHAT_BASE_URL", "https://api-satusehat.kemkes.go.id/fhir-r4/v1")),
			ConsentURL:   getEnv("SATUSEHAT_CONSENT_URL", getEnv("BRIDGING_SATUSEHAT_CONSENT_URL", "https://api-satusehat.dto.kemkes.go.id/consent/v1")),
			KFAURL:       getEnv("SATUSEHAT_KFA_URL", getEnv("BRIDGING_SATUSEHAT_KFA_URL", "https://api-satusehat.kemkes.go.id/kfa-v2")),
			Timeout:      parseDuration(getEnv("SATUSEHAT_TIMEOUT", getEnv("BRIDGING_SATUSEHAT_TIMEOUT", "30s"))),
		},
		Swagger: SwaggerConfig{
			Title:          getEnv("SWAGGER_TITLE", "SERVICE API"),
			Description:    getEnv("SWAGGER_DESCRIPTION", "CUSTUM SERVICE API"),
			Version:        getEnv("SWAGGER_VERSION", "1.0.0"),
			TermsOfService: getEnv("SWAGGER_TERMS_OF_SERVICE", "http://swagger.io/terms/"),
			ContactName:    getEnv("SWAGGER_CONTACT_NAME", "API Support"),
			ContactURL:     getEnv("SWAGGER_CONTACT_URL", "http://rssa.example.com/support"),
			ContactEmail:   getEnv("SWAGGER_CONTACT_EMAIL", "support@swagger.io"),
			LicenseName:    getEnv("SWAGGER_LICENSE_NAME", "Apache 2.0"),
			LicenseURL:     getEnv("SWAGGER_LICENSE_URL", "http://www.apache.org/licenses/LICENSE-2.0.html"),
			Host:           getEnv("SWAGGER_HOST", "localhost:8080"),
			BasePath:       getEnv("SWAGGER_BASE_PATH", "/api/v1"),
			Schemes:        parseSchemes(getEnv("SWAGGER_SCHEMES", "http,https")),
		},
		Security: SecurityConfig{
			TrustedOrigins: parseOrigins(getEnv("SECURITY_TRUSTED_ORIGINS", "http://localhost:3000,http://localhost:8080")),
			MaxInputLength: getEnvAsInt("SECURITY_MAX_INPUT_LENGTH", 500),
			RateLimit: RateLimitConfig{
				RequestsPerMinute: getEnvAsInt("RATE_LIMIT_REQUESTS_PER_MINUTE", 60),
				Redis: RedisConfig{
					Host:     getEnv("CACHE_REDIS_HOST", "localhost"),
					Port:     getEnvAsInt("CACHE_REDIS_PORT", 6379),
					Password: getEnv("REDIS_PASSWORD", ""),
					DB:       getEnvAsInt("CACHE_REDIS_DB", 0),
				},
			},
		},
		Cache: CacheConfig{
			Enabled:         getEnvAsBool("CACHE_ENABLED", true),
			DefaultTTL:      parseDuration(getEnv("CACHE_DEFAULT_TTL", "1h")),
			SessionTTL:      parseDuration(getEnv("CACHE_SESSION_TTL", "24h")),
			RateLimitTTL:    parseDuration(getEnv("CACHE_RATE_LIMIT_TTL", "1m")),
			CleanupInterval: parseDuration(getEnv("CACHE_CLEANUP_INTERVAL", "5m")),
			MaxRetries:      getEnvAsInt("CACHE_MAX_RETRIES", 3),
			RetryDelay:      parseDuration(getEnv("CACHE_RETRY_DELAY", "100ms")),
			Redis: RedisConfig{
				Host:     getEnv("CACHE_REDIS_HOST", "localhost"),
				Port:     getEnvAsInt("CACHE_REDIS_PORT", 6379),
				Password: getEnv("REDIS_PASSWORD", ""),
				DB:       getEnvAsInt("CACHE_REDIS_DB", 1),
			},
		},
		Logger: LoggerConfig{
			Level:  getEnv("LOGGER_LEVEL", "info"),
			Format: getEnv("LOGGER_FORMAT", "text"),
		},
		Minio: MinioConfig{
			Endpoint:   getEnv("MINIO_ENDPOINT", ""),
			Region:     getEnv("MINIO_REGION", ""),
			AccessKey:  getEnv("MINIO_ACCESSKEY", ""),
			SecretKey:  getEnv("MINIO_SECRETKEY", ""),
			UseSSL:     getEnvAsBool("MINIO_USESSL", false),
			BucketName: parseStaticTokens(getEnv("MINIO_BUCKETNAME", "")), // Pisahkan berdasarkan koma
		},
	}
	// Initialize validator
	config.Validator = validator.New()

	// Load database configurations
	config.loadDatabaseConfigs()

	// Load read replica configurations
	config.loadReadReplicaConfigs()

	return config
}

func (c *Config) Validate() error {
	var errs []string

	if len(c.Databases) == 0 {
		errs = append(errs, "at least one database configuration is required")
	}

	for name, db := range c.Databases {
		if db.Type != "sqlite" && db.Host == "" {
			errs = append(errs, fmt.Sprintf("database host is required for %s", name))
		}
		if db.Type != "sqlite" && db.Username == "" {
			errs = append(errs, fmt.Sprintf("database username is required for %s", name))
		}
		// Opsional: Matikan validasi password mutlak jika Anda menggunakan database lokal tanpa password
		// if db.Type != "sqlite" && db.Password == "" {
		// 	errs = append(errs, fmt.Sprintf("database password is required for %s", name))
		// }
		if db.Type == "sqlite" && db.Path == "" {
			errs = append(errs, fmt.Sprintf("database path is required for SQLite database %s", name))
		}
		if db.Type != "sqlite" && db.Database == "" {
			errs = append(errs, fmt.Sprintf("database name is required for %s", name))
		}
	}

	if c.Bpjs.Enabled {
		if c.Bpjs.BaseURL == "" {
			errs = append(errs, "BPJS Base URL is required when BPJS is enabled")
		}
		if c.Bpjs.ConsID == "" && c.Bpjs.ApotekConsID == "" {
			errs = append(errs, "At least one BPJS Consumer ID (Default or Apotek) is required when BPJS is enabled")
		}
		if c.Bpjs.SecretKey == "" && c.Bpjs.ApotekSecretKey == "" {
			errs = append(errs, "At least one BPJS Secret Key (Default or Apotek) is required when BPJS is enabled")
		}
		if c.Bpjs.ServiceName == "" {
			errs = append(errs, "BPJS Service Name is required when BPJS is enabled")
		}
	}

	// Validate authentication configuration (auth.type is already loaded from env)
	switch c.Auth.Type {
	case "keycloak":
		if !c.Keycloak.Enabled {
			errs = append(errs, "keycloak.enabled must be true when auth.type is 'keycloak'")
		}
		if c.Keycloak.Issuer == "" {
			errs = append(errs, "keycloak.issuer is required when auth.type is 'keycloak'")
		}
		if c.Keycloak.Audience == "" {
			errs = append(errs, "keycloak.audience is required when auth.type is 'keycloak'")
		}
		if c.Keycloak.JwksURL == "" {
			errs = append(errs, "keycloak.jwks_url is required when auth.type is 'keycloak'")
		}
	case "static":
		if len(c.Auth.StaticTokens) == 0 {
			errs = append(errs, "auth.static_tokens is required when auth.type is 'static'")
		}
	case "hybrid":
		if c.Auth.FallbackTo == "" {
			errs = append(errs, "auth.fallback_to is required when auth.type is 'hybrid'")
		}
		// Validate fallback configuration
		switch c.Auth.FallbackTo {
		case "keycloak":
			if !c.Keycloak.Enabled {
				errs = append(errs, "keycloak.enabled must be true when auth.fallback_to is 'keycloak'")
			}
		case "static":
			if len(c.Auth.StaticTokens) == 0 {
				errs = append(errs, "auth.static_tokens is required when auth.fallback_to is 'static'")
			}
		}
	}

	// Legacy validation for backward compatibility
	if c.Auth.Type != "keycloak" && c.Keycloak.Enabled {
		if c.Keycloak.Issuer == "" {
			errs = append(errs, "Keycloak issuer is required when Keycloak is enabled")
		}
		if c.Keycloak.Audience == "" {
			errs = append(errs, "Keycloak audience is required when Keycloak is enabled")
		}
		if c.Keycloak.JwksURL == "" {
			errs = append(errs, "Keycloak JWKS URL is required when Keycloak is enabled")
		}
	}

	// Validate SatuSehat configuration
	if c.SatuSehat.Enabled {
		if c.SatuSehat.OrgID == "" {
			errs = append(errs, "SatuSehat Organization ID is required when SatuSehat is enabled")
		}
		if c.SatuSehat.FasyakesID == "" {
			errs = append(errs, "SatuSehat Fasyankes ID is required when SatuSehat is enabled")
		}
		if c.SatuSehat.ClientID == "" {
			errs = append(errs, "SatuSehat Client ID is required when SatuSehat is enabled")
		}
		if c.SatuSehat.ClientSecret == "" {
			errs = append(errs, "SatuSehat Client Secret is required when SatuSehat is enabled")
		}
		if c.SatuSehat.AuthURL == "" {
			errs = append(errs, "SatuSehat Auth URL is required when SatuSehat is enabled")
		}
		if c.SatuSehat.BaseURL == "" {
			errs = append(errs, "SatuSehat Base URL is required when SatuSehat is enabled")
		}
	}

	if len(errs) > 0 {
		return fmt.Errorf("configuration validation failed: %s", strings.Join(errs, "; "))
	}
	return nil
}

// Additional helper functions for configuration validation
func (c *Config) IsJWTAuthEnabled() bool {
	return c.Auth.Type == "jwt"
}

func (c *Config) IsKeycloakAuthEnabled() bool {
	return c.Auth.Type == "keycloak" && c.Keycloak.Enabled
}

func (c *Config) IsStaticAuthEnabled() bool {
	return c.Auth.Type == "static"
}

func (c *Config) IsHybridAuthEnabled() bool {
	return c.Auth.Type == "hybrid"
}

func (c *Config) IsBPJSEnabled() bool {
	hasCons := c.Bpjs.ConsID != "" || c.Bpjs.ApotekConsID != ""
	return c.Bpjs.Enabled && c.Bpjs.BaseURL != "" && c.Bpjs.ServiceName != "" && hasCons
}

func (c *Config) IsSatuSehatEnabled() bool {
	return c.SatuSehat.Enabled && c.SatuSehat.BaseURL != "" && c.SatuSehat.ClientID != "" && c.SatuSehat.ClientSecret != ""
}

func (c *Config) IsProductionMode() bool {
	return strings.ToLower(c.Server.Mode) == "release"
}

func (c *Config) IsDevelopmentMode() bool {
	return strings.ToLower(c.Server.Mode) == "debug" || strings.ToLower(c.Server.Mode) == "test"
}

func (c *Config) GetLogLevel() string {
	if c.IsProductionMode() {
		return "info"
	}
	return "debug"
}

func (c *Config) ShouldUseHTTPS() bool {
	return c.IsProductionMode() || strings.Contains(strings.ToLower(c.Server.Mode), "prod")
}

func (c *Config) GetServerPorts() (int, int) {
	return c.Server.REST.Port, c.Server.GRPC.Port
}

func (c *Config) GetMaxUploadSize() int64 {
	sizeStr := os.Getenv("MAX_UPLOAD_SIZE")
	if sizeStr == "" {
		sizeStr = "10485760" // 10MB default
	}
	if size, err := strconv.ParseInt(sizeStr, 10, 64); err == nil {
		return size
	}
	return 10485760
}

func (c *Config) GetRateLimitConfig() (int, int) {
	requestsPerMinute := c.Security.RateLimit.RequestsPerMinute
	if requestsPerMinute <= 0 {
		requestsPerMinute = 60 // Default 60 requests per minute
	}
	burstSize := 10 // Default burst size
	return requestsPerMinute, burstSize
}

func (c *Config) GetTrustedOrigins() []string {
	origins := make([]string, len(c.Security.TrustedOrigins))
	for i, origin := range c.Security.TrustedOrigins {
		origins[i] = strings.TrimSpace(origin)
	}
	return origins
}

func (c *Config) IsOriginTrusted(origin string) bool {
	for _, trusted := range c.GetTrustedOrigins() {
		if origin == trusted {
			return true
		}
	}
	return false
}

// Helper functions for environment variables
func getEnv(key, defaultValue string) string {
	if value := os.Getenv(key); value != "" {
		return value
	}
	return defaultValue
}

func getEnvAsInt(key string, defaultValue int) int {
	valueStr := getEnv(key, "")
	if value, err := strconv.Atoi(valueStr); err == nil {
		return value
	}
	return defaultValue
}

func getEnvAsBool(key string, defaultValue bool) bool {
	valueStr := getEnv(key, "")
	if value, err := strconv.ParseBool(valueStr); err == nil {
		return value
	}
	return defaultValue
}

func parseDuration(durationStr string) time.Duration {
	if duration, err := time.ParseDuration(durationStr); err == nil {
		return duration
	}
	return 5 * time.Minute
}

func parseSchemes(schemesStr string) []string {
	if schemesStr == "" {
		return []string{"http"}
	}

	schemes := strings.Split(schemesStr, ",")
	for i, scheme := range schemes {
		schemes[i] = strings.TrimSpace(scheme)
	}
	return schemes
}

func parseStaticTokens(tokensStr string) []string {
	if tokensStr == "" {
		return []string{}
	}

	tokens := strings.Split(tokensStr, ",")
	for i, token := range tokens {
		tokens[i] = strings.TrimSpace(token)
		// Remove empty tokens
		if tokens[i] == "" {
			tokens = append(tokens[:i], tokens[i+1:]...)
			i--
		}
	}
	return tokens
}

func parseOrigins(originsStr string) []string {
	if originsStr == "" {
		return []string{"http://localhost:8080"} // Default untuk pengembangan
	}
	origins := strings.Split(originsStr, ",")
	for i, origin := range origins {
		origins[i] = strings.TrimSpace(origin)
	}
	return origins
}

// Additional database loading functions would go here...
func (c *Config) loadDatabaseConfigs() {
	// Load PostgreSQL configurations
	c.addPostgreSQLConfigs()

	// Load MySQL configurations
	c.addMySQLConfigs()

	// Load MongoDB configurations
	c.addMongoDBConfigs()

	// Load SQLite configurations
	c.addSQLiteConfigs()
	// Load SQL Server configurations
	c.addSQLServerConfigs()
	// Load custom database configurations from environment variables
	c.loadCustomDatabaseConfigs()

	// Remove duplicate database configurations
	c.removeDuplicateDatabases()
}

func (c *Config) addPostgreSQLConfigs() {
	envVars := os.Environ()
	for _, envVar := range envVars {
		parts := strings.SplitN(envVar, "=", 2)
		if len(parts) != 2 {
			continue
		}

		key := parts[0]

		// Format yang diharapkan: POSTGRES_[NAMA_DB]_[PROPERTY]
		// Contoh: POSTGRES_DEFAULT_HOST
		if strings.HasPrefix(key, "POSTGRES_") && strings.Count(key, "_") >= 2 {
			segments := strings.Split(key, "_")
			// segments[0] = "POSTGRES"
			// segments[1...n-1] = Nama Database
			// segments[n] = Property (HOST, PORT, dll)

			dbName := strings.ToLower(strings.Join(segments[1:len(segments)-1], "_"))
			property := strings.ToLower(segments[len(segments)-1])
			value := parts[1]

			// Ambil config yang sudah ada, atau buat baru
			dbCfg, exists := c.Databases[dbName]
			if !exists {
				dbCfg = DatabaseConfig{
					Name:            dbName,
					Type:            "postgres",
					Host:            getEnv("POSTGRES_"+strings.ToUpper(dbName)+"_HOST", "localhost"),
					Port:            getEnvAsInt("POSTGRES_"+strings.ToUpper(dbName)+"_PORT", 5432),
					Username:        getEnv("POSTGRES_"+strings.ToUpper(dbName)+"_USERNAME", ""),
					Password:        getEnv("POSTGRES_"+strings.ToUpper(dbName)+"_PASSWORD", ""),
					Database:        getEnv("POSTGRES_"+strings.ToUpper(dbName)+"_DATABASE", dbName),
					Schema:          getEnv("POSTGRES_"+strings.ToUpper(dbName)+"_SCHEMA", "public"),
					SSLMode:         getEnv("POSTGRES_"+strings.ToUpper(dbName)+"_SSLMODE", "disable"),
					MaxOpenConns:    25,
					MaxIdleConns:    25,
					ConnMaxLifetime: 5 * time.Minute,
					IsActive:        true,
				}
			}

			// Logic update dinamis berdasarkan property yang sedang dibaca
			// Ini penting agar Host, Port, dan Password terisi dengan benar
			switch property {
			case "host":
				dbCfg.Host = value
			case "port":
				if port, err := strconv.Atoi(value); err == nil {
					dbCfg.Port = port
				}
			case "username":
				dbCfg.Username = value
			case "password":
				dbCfg.Password = value
			case "database":
				dbCfg.Database = value
			case "schema":
				dbCfg.Schema = value
			case "sslmode":
				dbCfg.SSLMode = value
			}

			c.Databases[dbName] = dbCfg
		}
	}
}

// Additional helper functions would be implemented here...
func (c *Config) addMySQLConfigs() {
	envVars := os.Environ()
	for _, envVar := range envVars {
		parts := strings.SplitN(envVar, "=", 2)
		if len(parts) != 2 {
			continue
		}

		key := parts[0]
		if strings.HasPrefix(key, "MYSQL_") && strings.Count(key, "_") >= 2 {
			segments := strings.Split(key, "_")
			dbName := strings.ToLower(strings.Join(segments[1:len(segments)-1], "_"))
			property := strings.ToLower(segments[len(segments)-1])
			value := parts[1]

			dbCfg, exists := c.Databases[dbName]
			if !exists {
				dbCfg = DatabaseConfig{
					Name:            dbName,
					Type:            "mysql",
					Host:            getEnv("MYSQL_"+strings.ToUpper(dbName)+"_HOST", "localhost"),
					Port:            getEnvAsInt("MYSQL_"+strings.ToUpper(dbName)+"_PORT", 3306),
					Username:        getEnv("MYSQL_"+strings.ToUpper(dbName)+"_USERNAME", ""),
					Password:        getEnv("MYSQL_"+strings.ToUpper(dbName)+"_PASSWORD", ""),
					Database:        getEnv("MYSQL_"+strings.ToUpper(dbName)+"_DATABASE", dbName),
					MaxOpenConns:    25,
					MaxIdleConns:    25,
					ConnMaxLifetime: 5 * time.Minute,
					IsActive:        true,
				}
			}

			switch property {
			case "host":
				dbCfg.Host = value
			case "port":
				if port, err := strconv.Atoi(value); err == nil {
					dbCfg.Port = port
				}
			case "username":
				dbCfg.Username = value
			case "password":
				dbCfg.Password = value
			case "database":
				dbCfg.Database = value
			}
			c.Databases[dbName] = dbCfg
		}
	}
}

func (c *Config) addMongoDBConfigs() {
	envVars := os.Environ()
	for _, envVar := range envVars {
		parts := strings.SplitN(envVar, "=", 2)
		if len(parts) != 2 {
			continue
		}

		key := parts[0]
		if strings.HasPrefix(key, "MONGODB_") && strings.Count(key, "_") >= 2 {
			segments := strings.Split(key, "_")
			dbName := strings.ToLower(strings.Join(segments[1:len(segments)-1], "_"))
			property := strings.ToLower(segments[len(segments)-1])
			value := parts[1]

			dbCfg, exists := c.Databases[dbName]
			if !exists {
				dbCfg = DatabaseConfig{
					Name:     dbName,
					Type:     "mongodb",
					Host:     getEnv("MONGODB_"+strings.ToUpper(dbName)+"_HOST", "localhost"),
					Port:     getEnvAsInt("MONGODB_"+strings.ToUpper(dbName)+"_PORT", 27017),
					Username: getEnv("MONGODB_"+strings.ToUpper(dbName)+"_USERNAME", ""),
					Password: getEnv("MONGODB_"+strings.ToUpper(dbName)+"_PASSWORD", ""),
					Database: getEnv("MONGODB_"+strings.ToUpper(dbName)+"_DATABASE", dbName),
					IsActive: true,
				}
			}

			switch property {
			case "host":
				dbCfg.Host = value
			case "port":
				if port, err := strconv.Atoi(value); err == nil {
					dbCfg.Port = port
				}
			case "username":
				dbCfg.Username = value
			case "password":
				dbCfg.Password = value
			case "database":
				dbCfg.Database = value
			}
			c.Databases[dbName] = dbCfg
		}
	}
}

func (c *Config) addSQLiteConfigs() {
	envVars := os.Environ()
	for _, envVar := range envVars {
		parts := strings.SplitN(envVar, "=", 2)
		if len(parts) != 2 {
			continue
		}

		key := parts[0]
		if strings.HasPrefix(key, "SQLITE_") && strings.Count(key, "_") >= 2 {
			segments := strings.Split(key, "_")
			dbName := strings.ToLower(strings.Join(segments[1:len(segments)-1], "_"))
			property := strings.ToLower(segments[len(segments)-1])
			value := parts[1]

			dbCfg, exists := c.Databases[dbName]
			if !exists {
				dbCfg = DatabaseConfig{
					Name:     dbName,
					Type:     "sqlite",
					Path:     getEnv("SQLITE_"+strings.ToUpper(dbName)+"_PATH", "./"+dbName+".db"),
					IsActive: true,
				}
			}

			if property == "path" {
				dbCfg.Path = value
			}
			c.Databases[dbName] = dbCfg
		}
	}
}

func (c *Config) addSQLServerConfigs() {
	envVars := os.Environ()
	for _, envVar := range envVars {
		parts := strings.SplitN(envVar, "=", 2)
		if len(parts) != 2 {
			continue
		}

		key := parts[0]

		// Format yang diharapkan: SQLSERVER_[NAMA_DB]_[PROPERTY]
		// Contoh: SQLSERVER_FARMASI_HOST
		if strings.HasPrefix(key, "SQLSERVER_") && strings.Count(key, "_") >= 2 {
			segments := strings.Split(key, "_")
			// segments[0] = "SQLSERVER"
			// segments[1...n-1] = Nama Database
			// segments[n] = Property (HOST, PORT, dll)

			dbName := strings.ToLower(strings.Join(segments[1:len(segments)-1], "_"))
			property := strings.ToLower(segments[len(segments)-1])
			value := parts[1]

			// Ambil config yang sudah ada, atau buat baru
			dbCfg, exists := c.Databases[dbName]
			if !exists {
				dbCfg = DatabaseConfig{
					Name:            dbName,
					Type:            "sqlserver",
					Host:            getEnv("SQLSERVER_"+strings.ToUpper(dbName)+"_HOST", "localhost"),
					Port:            getEnvAsInt("SQLSERVER_"+strings.ToUpper(dbName)+"_PORT", 1433),
					Username:        getEnv("SQLSERVER_"+strings.ToUpper(dbName)+"_USERNAME", ""),
					Password:        getEnv("SQLSERVER_"+strings.ToUpper(dbName)+"_PASSWORD", ""),
					Database:        getEnv("SQLSERVER_"+strings.ToUpper(dbName)+"_DATABASE", dbName),
					SSLMode:         getEnv("SQLSERVER_"+strings.ToUpper(dbName)+"_SSLMODE", "disable"),
					MaxOpenConns:    25,
					MaxIdleConns:    25,
					ConnMaxLifetime: 5 * time.Minute,
				}
			}

			// Update field berdasarkan property
			switch property {
			case "host":
				dbCfg.Host = value
			case "port":
				if port, err := strconv.Atoi(value); err == nil {
					dbCfg.Port = port
				}
			case "username":
				dbCfg.Username = value
			case "password":
				dbCfg.Password = value
			case "database":
				dbCfg.Database = value
			case "sslmode":
				dbCfg.SSLMode = value
			case "timeout":
				dbCfg.Timeout = parseDuration(value)
			case "max_open_conns":
				if conns, err := strconv.Atoi(value); err == nil {
					dbCfg.MaxOpenConns = conns
				}
			case "max_idle_conns":
				if conns, err := strconv.Atoi(value); err == nil {
					dbCfg.MaxIdleConns = conns
				}
			case "conn_max_lifetime":
				dbCfg.ConnMaxLifetime = parseDuration(value)
			}

			c.Databases[dbName] = dbCfg
		}
	}
}

func (c *Config) loadCustomDatabaseConfigs() {
	envVars := os.Environ()
	for _, envVar := range envVars {
		parts := strings.SplitN(envVar, "=", 2)
		if len(parts) != 2 {
			continue
		}

		key := parts[0]
		// Format yang diharapkan: DB_[NAMA_DB]_[PROPERTY]
		if strings.HasPrefix(key, "DB_") && strings.Count(key, "_") >= 2 {
			segments := strings.Split(key, "_")
			dbName := strings.ToLower(strings.Join(segments[1:len(segments)-1], "_"))
			property := strings.ToLower(segments[len(segments)-1])
			value := parts[1]

			dbCfg, exists := c.Databases[dbName]
			if !exists {
				dbCfg = DatabaseConfig{
					Name:            dbName,
					MaxOpenConns:    25,
					MaxIdleConns:    25,
					ConnMaxLifetime: 5 * time.Minute,
					IsActive:        true,
				}
			}

			switch property {
			case "type":
				dbCfg.Type = value
			case "host":
				dbCfg.Host = value
			case "port":
				if port, err := strconv.Atoi(value); err == nil {
					dbCfg.Port = port
				}
			case "username":
				dbCfg.Username = value
			case "password":
				dbCfg.Password = value
			case "database":
				dbCfg.Database = value
			case "schema":
				dbCfg.Schema = value
			case "path":
				dbCfg.Path = value
			case "sslmode":
				dbCfg.SSLMode = value
			}
			c.Databases[dbName] = dbCfg
		}
	}
}

func (c *Config) removeDuplicateDatabases() {
	// Menghapus konfigurasi database yang tidak valid / kosong
	for name, db := range c.Databases {
		if db.Type == "" {
			delete(c.Databases, name)
			continue
		}
		// SQLite tidak butuh Host, sisanya butuh
		if db.Type != "sqlite" && db.Host == "" {
			delete(c.Databases, name)
			continue
		}
	}
}

func (c *Config) loadReadReplicaConfigs() {
	envVars := os.Environ()
	for _, envVar := range envVars {
		parts := strings.SplitN(envVar, "=", 2)
		if len(parts) != 2 {
			continue
		}

		key := parts[0]
		// Format: REPLICA_[DBNAME]_[INDEX]_[PROPERTY], cth: REPLICA_DEFAULT_0_HOST
		if strings.HasPrefix(key, "REPLICA_") && strings.Count(key, "_") >= 3 {
			segments := strings.Split(key, "_")
			property := strings.ToLower(segments[len(segments)-1])
			indexStr := segments[len(segments)-2]
			dbName := strings.ToLower(strings.Join(segments[1:len(segments)-2], "_"))
			value := parts[1]

			index, err := strconv.Atoi(indexStr)
			if err != nil {
				continue
			}

			replicas := c.ReadReplicas[dbName]
			for len(replicas) <= index {
				// Turunkan pengaturan dasar dari database master (seperti port, user)
				baseCfg := c.Databases[dbName]
				baseCfg.Name = fmt.Sprintf("%s-replica-%d", dbName, len(replicas))
				replicas = append(replicas, baseCfg)
			}

			replicaCfg := replicas[index]
			switch property {
			case "host":
				replicaCfg.Host = value
			case "port":
				if p, err := strconv.Atoi(value); err == nil {
					replicaCfg.Port = p
				}
			case "username":
				replicaCfg.Username = value
			case "password":
				replicaCfg.Password = value
			case "database":
				replicaCfg.Database = value
			}

			replicas[index] = replicaCfg
			c.ReadReplicas[dbName] = replicas
		}
	}
}

func loadAuthConfig() AuthConfig {
	return AuthConfig{
		Type:         getEnv("AUTH_TYPE", "jwt"),
		FallbackTo:   getEnv("AUTH_FALLBACK_TO", "jwt"),
		StaticTokens: parseStaticTokens(getEnv("AUTH_STATIC_TOKENS", "")),
	}
}

func loadKeycloakConfig() KeycloakConfig {
	return KeycloakConfig{
		Enabled:      getEnvAsBool("KEYCLOAK_ENABLED", false),
		URL:          getEnv("KEYCLOAK_URL", ""),
		Realm:        getEnv("KEYCLOAK_REALM", ""),
		ClientID:     getEnv("KEYCLOAK_CLIENT_ID", ""),
		ClientSecret: getEnv("KEYCLOAK_CLIENT_SECRET", ""),
		Issuer:       getEnv("KEYCLOAK_ISSUER", ""),
		Audience:     getEnv("KEYCLOAK_AUDIENCE", ""),
		JwksURL:      getEnv("KEYCLOAK_JWKS_URL", ""),
	}
}
