package config

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

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

type Config struct {
	Server       ServerConfig
	Databases    map[string]DatabaseConfig
	ReadReplicas map[string][]DatabaseConfig // For read replicas
	Keycloak     KeycloakConfig
	Bpjs         BpjsConfig
	SatuSehat    SatuSehatConfig
	Swagger      SwaggerConfig
	WebSocket    WebSocketConfig // Tambahkan ini
	Validator    *validator.Validate
}

type SwaggerConfig struct {
	Title          string
	Description    string
	Version        string
	TermsOfService string
	ContactName    string
	ContactURL     string
	ContactEmail   string
	LicenseName    string
	LicenseURL     string
	Host           string
	BasePath       string
	Schemes        []string
}

type ServerConfig struct {
	Port int
	Mode string
}

type DatabaseConfig struct {
	Name            string
	Type            string // postgres, mysql, sqlserver, sqlite, mongodb
	Host            string
	Port            int
	Username        string
	Password        string
	Database        string
	Schema          string
	SSLMode         string
	Path            string        // For SQLite
	Options         string        // Additional connection options
	MaxOpenConns    int           // Max open connections
	MaxIdleConns    int           // Max idle connections
	ConnMaxLifetime time.Duration // Connection max lifetime
}

type KeycloakConfig struct {
	Issuer   string
	Audience string
	JwksURL  string
	Enabled  bool
}

type BpjsConfig struct {
	BaseURL   string        `json:"base_url"`
	ConsID    string        `json:"cons_id"`
	UserKey   string        `json:"user_key"`
	SecretKey string        `json:"secret_key"`
	Timeout   time.Duration `json:"timeout"`
}

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

type WebSocketConfig struct {
	// Timeout configurations
	ReadTimeout      time.Duration `json:"read_timeout"`
	WriteTimeout     time.Duration `json:"write_timeout"`
	PingInterval     time.Duration `json:"ping_interval"`
	PongTimeout      time.Duration `json:"pong_timeout"`
	HandshakeTimeout time.Duration `json:"handshake_timeout"`

	// Buffer sizes
	ReadBufferSize    int `json:"read_buffer_size"`
	WriteBufferSize   int `json:"write_buffer_size"`
	ChannelBufferSize int `json:"channel_buffer_size"`
	MessageQueueSize  int `json:"message_queue_size"`

	// Connection limits
	MaxMessageSize int `json:"max_message_size"`
	QueueWorkers   int `json:"queue_workers"`

	// Monitoring
	ActivityLogSize int           `json:"activity_log_size"`
	CleanupInterval time.Duration `json:"cleanup_interval"`
	InactiveTimeout time.Duration `json:"inactive_timeout"`

	// Server info
	ServerID string `json:"server_id"`

	// Features
	EnableCompression bool `json:"enable_compression"`
	EnableMetrics     bool `json:"enable_metrics"`
	EnableMonitoring  bool `json:"enable_monitoring"`
}

// SetHeader generates required headers for BPJS VClaim API
// 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 lowercase hexits
// 	hex.EncodeToString(hash.Sum(nil))
// 	// to base64
// 	xSignature := base64.StdEncoding.EncodeToString(hash.Sum(nil))

//		return cfg.ConsID, cfg.SecretKey, cfg.UserKey, fmt.Sprint(tstamp), xSignature
//	}
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 lowercase hexits
	hex.EncodeToString(hash.Sum(nil))
	// to base64
	xSignature := base64.StdEncoding.EncodeToString(hash.Sum(nil))

	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{
		// Configuration for the server
		Server: ServerConfig{
			Port: getEnvAsInt("PORT", 8080),
			Mode: getEnv("GIN_MODE", "debug"),
		},
		// Configuration for the database
		Databases: make(map[string]DatabaseConfig),
		// Configuration for read replicas
		ReadReplicas: make(map[string][]DatabaseConfig),
		// Configuration for Keycloak authentication and authorization
		Keycloak: KeycloakConfig{
			Issuer:   getEnv("KEYCLOAK_ISSUER", "https://keycloak.example.com/auth/realms/yourrealm"),
			Audience: getEnv("KEYCLOAK_AUDIENCE", "your-client-id"),
			JwksURL:  getEnv("KEYCLOAK_JWKS_URL", "https://keycloak.example.com/auth/realms/yourrealm/protocol/openid-connect/certs"),
			Enabled:  getEnvAsBool("KEYCLOAK_ENABLED", true),
		},
		// Configuration for BPJS service bridging API
		Bpjs: BpjsConfig{
			BaseURL:   getEnv("BPJS_BASEURL", "https://apijkn.bpjs-kesehatan.go.id"),
			ConsID:    getEnv("BPJS_CONSID", ""),
			UserKey:   getEnv("BPJS_USERKEY", ""),
			SecretKey: getEnv("BPJS_SECRETKEY", ""),
			Timeout:   parseDuration(getEnv("BPJS_TIMEOUT", "30s")),
		},
		// Configuration for Satu Sehat service bridging API
		SatuSehat: SatuSehatConfig{
			OrgID:        getEnv("BRIDGING_SATUSEHAT_ORG_ID", ""),
			FasyakesID:   getEnv("BRIDGING_SATUSEHAT_FASYAKES_ID", ""),
			ClientID:     getEnv("BRIDGING_SATUSEHAT_CLIENT_ID", ""),
			ClientSecret: getEnv("BRIDGING_SATUSEHAT_CLIENT_SECRET", ""),
			AuthURL:      getEnv("BRIDGING_SATUSEHAT_AUTH_URL", "https://api-satusehat.kemkes.go.id/oauth2/v1"),
			BaseURL:      getEnv("BRIDGING_SATUSEHAT_BASE_URL", "https://api-satusehat.kemkes.go.id/fhir-r4/v1"),
			ConsentURL:   getEnv("BRIDGING_SATUSEHAT_CONSENT_URL", "https://api-satusehat.dto.kemkes.go.id/consent/v1"),
			KFAURL:       getEnv("BRIDGING_SATUSEHAT_KFA_URL", "https://api-satusehat.kemkes.go.id/kfa-v2"),
			Timeout:      parseDuration(getEnv("BRIDGING_SATUSEHAT_TIMEOUT", "30s")),
		},
		// Configuration for WebSocket server
		WebSocket: WebSocketConfig{ // Tambahkan ini
			// Timeout configurations
			ReadTimeout:      parseDuration(getEnv("WS_READ_TIMEOUT", "300s")),
			WriteTimeout:     parseDuration(getEnv("WS_WRITE_TIMEOUT", "30s")),
			PingInterval:     parseDuration(getEnv("WS_PING_INTERVAL", "60s")),
			PongTimeout:      parseDuration(getEnv("WS_PONG_TIMEOUT", "70s")),
			HandshakeTimeout: parseDuration(getEnv("WS_HANDSHAKE_TIMEOUT", "45s")),

			// Buffer sizes
			ReadBufferSize:    getEnvAsInt("WS_READ_BUFFER_SIZE", 8192),
			WriteBufferSize:   getEnvAsInt("WS_WRITE_BUFFER_SIZE", 8192),
			ChannelBufferSize: getEnvAsInt("WS_CHANNEL_BUFFER_SIZE", 512),
			MessageQueueSize:  getEnvAsInt("WS_MESSAGE_QUEUE_SIZE", 5000),

			// Connection limits
			MaxMessageSize: getEnvAsInt("WS_MAX_MESSAGE_SIZE", 8192),
			QueueWorkers:   getEnvAsInt("WS_QUEUE_WORKERS", 10),

			// Monitoring
			ActivityLogSize: getEnvAsInt("WS_ACTIVITY_LOG_SIZE", 1000),
			CleanupInterval: parseDuration(getEnv("WS_CLEANUP_INTERVAL", "2m")),
			InactiveTimeout: parseDuration(getEnv("WS_INACTIVE_TIMEOUT", "5m")),

			// Server info
			ServerID: getEnv("WS_SERVER_ID", "api-service-v1"),

			// Features
			EnableCompression: getEnvAsBool("WS_ENABLE_COMPRESSION", true),
			EnableMetrics:     getEnvAsBool("WS_ENABLE_METRICS", true),
			EnableMonitoring:  getEnvAsBool("WS_ENABLE_MONITORING", true),
		},
		// Configuration for Swagger
		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")),
		},
	}

	// Initialize validator
	config.Validator = validator.New()

	// Load database configurations
	config.loadDatabaseConfigs()

	// Load read replica configurations
	config.loadReadReplicaConfigs()

	return config
}

func (c *Config) loadDatabaseConfigs() {
	// Simplified approach: Directly load from environment variables
	// This ensures we get the exact values specified in .env

	// Primary database configuration
	c.Databases["default"] = DatabaseConfig{
		Name:            "default",
		Type:            getEnv("DB_CONNECTION", "postgres"),
		Host:            getEnv("DB_HOST", "localhost"),
		Port:            getEnvAsInt("DB_PORT", 5432),
		Username:        getEnv("DB_USERNAME", ""),
		Password:        getEnv("DB_PASSWORD", ""),
		Database:        getEnv("DB_DATABASE", "satu_db"),
		Schema:          getEnv("DB_SCHEMA", "public"),
		SSLMode:         getEnv("DB_SSLMODE", "disable"),
		MaxOpenConns:    getEnvAsInt("DB_MAX_OPEN_CONNS", 25),
		MaxIdleConns:    getEnvAsInt("DB_MAX_IDLE_CONNS", 25),
		ConnMaxLifetime: parseDuration(getEnv("DB_CONN_MAX_LIFETIME", "5m")),
	}

	// SATUDATA database configuration
	c.addPostgreSQLConfigs()

	// MongoDB database configuration
	c.addMongoDBConfigs()

	// Legacy support for backward compatibility
	envVars := os.Environ()
	dbConfigs := make(map[string]map[string]string)

	// Parse database configurations from environment variables
	for _, envVar := range envVars {
		parts := strings.SplitN(envVar, "=", 2)
		if len(parts) != 2 {
			continue
		}

		key := parts[0]
		value := parts[1]

		// Parse specific database configurations
		if strings.HasSuffix(key, "_CONNECTION") || strings.HasSuffix(key, "_HOST") ||
			strings.HasSuffix(key, "_DATABASE") || strings.HasSuffix(key, "_USERNAME") ||
			strings.HasSuffix(key, "_PASSWORD") || strings.HasSuffix(key, "_PORT") ||
			strings.HasSuffix(key, "_NAME") {

			segments := strings.Split(key, "_")
			if len(segments) >= 2 {
				dbName := strings.ToLower(strings.Join(segments[:len(segments)-1], "_"))
				property := strings.ToLower(segments[len(segments)-1])

				if dbConfigs[dbName] == nil {
					dbConfigs[dbName] = make(map[string]string)
				}
				dbConfigs[dbName][property] = value
			}
		}
	}

	// Create DatabaseConfig from parsed configurations for additional databases
	for name, config := range dbConfigs {
		// Skip empty configurations or system configurations
		if name == "" || strings.Contains(name, "chrome_crashpad_pipe") || name == "primary" {
			continue
		}

		dbConfig := DatabaseConfig{
			Name:            name,
			Type:            getEnvFromMap(config, "connection", getEnvFromMap(config, "type", "postgres")),
			Host:            getEnvFromMap(config, "host", "localhost"),
			Port:            getEnvAsIntFromMap(config, "port", 5432),
			Username:        getEnvFromMap(config, "username", ""),
			Password:        getEnvFromMap(config, "password", ""),
			Database:        getEnvFromMap(config, "database", getEnvFromMap(config, "name", name)),
			Schema:          getEnvFromMap(config, "schema", "public"),
			SSLMode:         getEnvFromMap(config, "sslmode", "disable"),
			Path:            getEnvFromMap(config, "path", ""),
			Options:         getEnvFromMap(config, "options", ""),
			MaxOpenConns:    getEnvAsIntFromMap(config, "max_open_conns", 25),
			MaxIdleConns:    getEnvAsIntFromMap(config, "max_idle_conns", 25),
			ConnMaxLifetime: parseDuration(getEnvFromMap(config, "conn_max_lifetime", "5m")),
		}

		// Skip if username is empty and it's not a system config
		if dbConfig.Username == "" && !strings.HasPrefix(name, "chrome") {
			continue
		}

		c.Databases[name] = dbConfig
	}
}

func (c *Config) loadReadReplicaConfigs() {
	envVars := os.Environ()

	for _, envVar := range envVars {
		parts := strings.SplitN(envVar, "=", 2)
		if len(parts) != 2 {
			continue
		}

		key := parts[0]
		value := parts[1]

		// Parse read replica configurations (format: [DBNAME]_REPLICA_[INDEX]_[PROPERTY])
		if strings.Contains(key, "_REPLICA_") {
			segments := strings.Split(key, "_")
			if len(segments) >= 5 && strings.ToUpper(segments[2]) == "REPLICA" {
				dbName := strings.ToLower(segments[1])
				replicaIndex := segments[3]
				property := strings.ToLower(strings.Join(segments[4:], "_"))

				replicaKey := dbName + "_replica_" + replicaIndex

				if c.ReadReplicas[dbName] == nil {
					c.ReadReplicas[dbName] = []DatabaseConfig{}
				}

				// Find or create replica config
				var replicaConfig *DatabaseConfig
				for i := range c.ReadReplicas[dbName] {
					if c.ReadReplicas[dbName][i].Name == replicaKey {
						replicaConfig = &c.ReadReplicas[dbName][i]
						break
					}
				}

				if replicaConfig == nil {
					// Create new replica config
					newConfig := DatabaseConfig{
						Name:            replicaKey,
						Type:            c.Databases[dbName].Type,
						Host:            getEnv("DB_"+strings.ToUpper(dbName)+"_REPLICA_"+replicaIndex+"_HOST", c.Databases[dbName].Host),
						Port:            getEnvAsInt("DB_"+strings.ToUpper(dbName)+"_REPLICA_"+replicaIndex+"_PORT", c.Databases[dbName].Port),
						Username:        getEnv("DB_"+strings.ToUpper(dbName)+"_REPLICA_"+replicaIndex+"_USERNAME", c.Databases[dbName].Username),
						Password:        getEnv("DB_"+strings.ToUpper(dbName)+"_REPLICA_"+replicaIndex+"_PASSWORD", c.Databases[dbName].Password),
						Database:        getEnv("DB_"+strings.ToUpper(dbName)+"_REPLICA_"+replicaIndex+"_DATABASE", c.Databases[dbName].Database),
						Schema:          getEnv("DB_"+strings.ToUpper(dbName)+"_REPLICA_"+replicaIndex+"_SCHEMA", c.Databases[dbName].Schema),
						SSLMode:         getEnv("DB_"+strings.ToUpper(dbName)+"_REPLICA_"+replicaIndex+"_SSLMODE", c.Databases[dbName].SSLMode),
						MaxOpenConns:    getEnvAsInt("DB_"+strings.ToUpper(dbName)+"_REPLICA_"+replicaIndex+"_MAX_OPEN_CONNS", c.Databases[dbName].MaxOpenConns),
						MaxIdleConns:    getEnvAsInt("DB_"+strings.ToUpper(dbName)+"_REPLICA_"+replicaIndex+"_MAX_IDLE_CONNS", c.Databases[dbName].MaxIdleConns),
						ConnMaxLifetime: parseDuration(getEnv("DB_"+strings.ToUpper(dbName)+"_REPLICA_"+replicaIndex+"_CONN_MAX_LIFETIME", "5m")),
					}
					c.ReadReplicas[dbName] = append(c.ReadReplicas[dbName], newConfig)
					replicaConfig = &c.ReadReplicas[dbName][len(c.ReadReplicas[dbName])-1]
				}

				// Update the specific replica
				switch property {
				case "host":
					replicaConfig.Host = value
				case "port":
					replicaConfig.Port = getEnvAsInt(key, 5432)
				case "username":
					replicaConfig.Username = value
				case "password":
					replicaConfig.Password = value
				case "database":
					replicaConfig.Database = value
				case "schema":
					replicaConfig.Schema = value
				case "sslmode":
					replicaConfig.SSLMode = value
				case "max_open_conns":
					replicaConfig.MaxOpenConns = getEnvAsInt(key, 25)
				case "max_idle_conns":
					replicaConfig.MaxIdleConns = getEnvAsInt(key, 25)
				case "conn_max_lifetime":
					replicaConfig.ConnMaxLifetime = parseDuration(value)
				}
			}
		}
	}
}

func (c *Config) addSpecificDatabase(prefix, defaultType string) {
	connection := getEnv(strings.ToUpper(prefix)+"_CONNECTION", defaultType)
	host := getEnv(strings.ToUpper(prefix)+"_HOST", "")
	if host != "" {
		dbConfig := DatabaseConfig{
			Name:            prefix,
			Type:            connection,
			Host:            host,
			Port:            getEnvAsInt(strings.ToUpper(prefix)+"_PORT", 5432),
			Username:        getEnv(strings.ToUpper(prefix)+"_USERNAME", ""),
			Password:        getEnv(strings.ToUpper(prefix)+"_PASSWORD", ""),
			Database:        getEnv(strings.ToUpper(prefix)+"_DATABASE", getEnv(strings.ToUpper(prefix)+"_NAME", prefix)),
			Schema:          getEnv(strings.ToUpper(prefix)+"_SCHEMA", "public"),
			SSLMode:         getEnv(strings.ToUpper(prefix)+"_SSLMODE", "disable"),
			MaxOpenConns:    getEnvAsInt(strings.ToUpper(prefix)+"_MAX_OPEN_CONNS", 25),
			MaxIdleConns:    getEnvAsInt(strings.ToUpper(prefix)+"_MAX_IDLE_CONNS", 25),
			ConnMaxLifetime: parseDuration(getEnv(strings.ToUpper(prefix)+"_CONN_MAX_LIFETIME", "5m")),
		}
		c.Databases[prefix] = dbConfig
	}
}

// PostgreSQL database
func (c *Config) addPostgreSQLConfigs() {
	// SATUDATA database configuration
	// defaultPOSTGRESHost := getEnv("POSTGRES_HOST", "localhost")
	// if defaultPOSTGRESHost != "" {
	//     c.Databases["postgres"] = DatabaseConfig{
	//         Name:             "postgres",
	//         Type:             getEnv("POSTGRES_CONNECTION", "postgres"),
	//         Host:             defaultPOSTGRESHost,
	//         Port:             getEnvAsInt("POSTGRES_PORT", 5432),
	//         Username:         getEnv("POSTGRES_USERNAME", ""),
	//         Password:         getEnv("POSTGRES_PASSWORD", ""),
	//         Database:         getEnv("POSTGRES_DATABASE", "postgres"),
	//         Schema:           getEnv("POSTGRES_SCHEMA", "public"),
	//         SSLMode:          getEnv("POSTGRES_SSLMODE", "disable"),
	//         MaxOpenConns:     getEnvAsInt("POSTGRES_MAX_OPEN_CONNS", 25),
	//         MaxIdleConns:     getEnvAsInt("POSTGRES_MAX_IDLE_CONNS", 25),
	//         ConnMaxLifetime:  parseDuration(getEnv("POSTGRES_CONN_MAX_LIFETIME", "5m")),
	//     }
	// }

	// Support for custom PostgreSQL configurations with POSTGRES_ prefix
	envVars := os.Environ()
	for _, envVar := range envVars {
		parts := strings.SplitN(envVar, "=", 2)
		if len(parts) != 2 {
			continue
		}

		key := parts[0]
		// Parse PostgreSQL configurations (format: POSTGRES_[NAME]_[PROPERTY])
		if strings.HasPrefix(key, "POSTGRES_") && strings.Contains(key, "_") {
			segments := strings.Split(key, "_")
			if len(segments) >= 3 {
				dbName := strings.ToLower(strings.Join(segments[1:len(segments)-1], "_"))

				// Skip if it's a standard PostgreSQL configuration
				if dbName == "connection" || dbName == "dev" || dbName == "default" || dbName == "satudata" {
					continue
				}

				// Create or update PostgreSQL configuration
				if _, exists := c.Databases[dbName]; !exists {
					c.Databases[dbName] = 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:    getEnvAsInt("POSTGRES_MAX_OPEN_CONNS", 25),
						MaxIdleConns:    getEnvAsInt("POSTGRES_MAX_IDLE_CONNS", 25),
						ConnMaxLifetime: parseDuration(getEnv("POSTGRES_CONN_MAX_LIFETIME", "5m")),
					}
				}
			}
		}
	}
}

// addMYSQLConfigs adds MYSQL database
func (c *Config) addMySQLConfigs() {
	// Primary MySQL configuration
	defaultMySQLHost := getEnv("MYSQL_HOST", "")
	if defaultMySQLHost != "" {
		c.Databases["mysql"] = DatabaseConfig{
			Name:            "mysql",
			Type:            getEnv("MYSQL_CONNECTION", "mysql"),
			Host:            defaultMySQLHost,
			Port:            getEnvAsInt("MYSQL_PORT", 3306),
			Username:        getEnv("MYSQL_USERNAME", ""),
			Password:        getEnv("MYSQL_PASSWORD", ""),
			Database:        getEnv("MYSQL_DATABASE", "mysql"),
			SSLMode:         getEnv("MYSQL_SSLMODE", "disable"),
			MaxOpenConns:    getEnvAsInt("MYSQL_MAX_OPEN_CONNS", 25),
			MaxIdleConns:    getEnvAsInt("MYSQL_MAX_IDLE_CONNS", 25),
			ConnMaxLifetime: parseDuration(getEnv("MYSQL_CONN_MAX_LIFETIME", "5m")),
		}
	}

	// Support for custom MySQL configurations with MYSQL_ prefix
	envVars := os.Environ()
	for _, envVar := range envVars {
		parts := strings.SplitN(envVar, "=", 2)
		if len(parts) != 2 {
			continue
		}

		key := parts[0]
		// Parse MySQL configurations (format: MYSQL_[NAME]_[PROPERTY])
		if strings.HasPrefix(key, "MYSQL_") && strings.Contains(key, "_") {
			segments := strings.Split(key, "_")
			if len(segments) >= 3 {
				dbName := strings.ToLower(strings.Join(segments[1:len(segments)-1], "_"))

				// Skip if it's a standard MySQL configuration
				if dbName == "connection" || dbName == "dev" || dbName == "max" || dbName == "conn" {
					continue
				}

				// Create or update MySQL configuration
				if _, exists := c.Databases[dbName]; !exists {
					mysqlHost := getEnv("MYSQL_"+strings.ToUpper(dbName)+"_HOST", "")
					if mysqlHost != "" {
						c.Databases[dbName] = DatabaseConfig{
							Name:            dbName,
							Type:            getEnv("MYSQL_"+strings.ToUpper(dbName)+"_CONNECTION", "mysql"),
							Host:            mysqlHost,
							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),
							SSLMode:         getEnv("MYSQL_"+strings.ToUpper(dbName)+"_SSLMODE", "disable"),
							MaxOpenConns:    getEnvAsInt("MYSQL_MAX_OPEN_CONNS", 25),
							MaxIdleConns:    getEnvAsInt("MYSQL_MAX_IDLE_CONNS", 25),
							ConnMaxLifetime: parseDuration(getEnv("MYSQL_CONN_MAX_LIFETIME", "5m")),
						}
					}
				}
			}
		}
	}
}

// addMongoDBConfigs adds MongoDB database configurations from environment variables
func (c *Config) addMongoDBConfigs() {
	// Primary MongoDB configuration
	mongoHost := getEnv("MONGODB_HOST", "")
	if mongoHost != "" {
		c.Databases["mongodb"] = DatabaseConfig{
			Name:            "mongodb",
			Type:            getEnv("MONGODB_CONNECTION", "mongodb"),
			Host:            mongoHost,
			Port:            getEnvAsInt("MONGODB_PORT", 27017),
			Username:        getEnv("MONGODB_USER", ""),
			Password:        getEnv("MONGODB_PASS", ""),
			Database:        getEnv("MONGODB_MASTER", "master"),
			SSLMode:         getEnv("MONGODB_SSLMODE", "disable"),
			MaxOpenConns:    getEnvAsInt("MONGODB_MAX_OPEN_CONNS", 100),
			MaxIdleConns:    getEnvAsInt("MONGODB_MAX_IDLE_CONNS", 10),
			ConnMaxLifetime: parseDuration(getEnv("MONGODB_CONN_MAX_LIFETIME", "30m")),
		}
	}

	// Additional MongoDB configurations for local database
	mongoLocalHost := getEnv("MONGODB_LOCAL_HOST", "")
	if mongoLocalHost != "" {
		c.Databases["mongodb_local"] = DatabaseConfig{
			Name:            "mongodb_local",
			Type:            getEnv("MONGODB_CONNECTION", "mongodb"),
			Host:            mongoLocalHost,
			Port:            getEnvAsInt("MONGODB_LOCAL_PORT", 27017),
			Username:        getEnv("MONGODB_LOCAL_USER", ""),
			Password:        getEnv("MONGODB_LOCAL_PASS", ""),
			Database:        getEnv("MONGODB_LOCAL_DB", "local"),
			SSLMode:         getEnv("MONGOD_SSLMODE", "disable"),
			MaxOpenConns:    getEnvAsInt("MONGODB_MAX_OPEN_CONNS", 100),
			MaxIdleConns:    getEnvAsInt("MONGODB_MAX_IDLE_CONNS", 10),
			ConnMaxLifetime: parseDuration(getEnv("MONGODB_CONN_MAX_LIFETIME", "30m")),
		}
	}

	// Support for custom MongoDB configurations with MONGODB_ prefix
	envVars := os.Environ()
	for _, envVar := range envVars {
		parts := strings.SplitN(envVar, "=", 2)
		if len(parts) != 2 {
			continue
		}

		key := parts[0]
		// Parse MongoDB configurations (format: MONGODB_[NAME]_[PROPERTY])
		if strings.HasPrefix(key, "MONGODB_") && strings.Contains(key, "_") {
			segments := strings.Split(key, "_")
			if len(segments) >= 3 {
				dbName := strings.ToLower(strings.Join(segments[1:len(segments)-1], "_"))
				// Skip if it's a standard MongoDB configuration
				if dbName == "connection" || dbName == "dev" || dbName == "local" {
					continue
				}

				// Create or update MongoDB configuration
				if _, exists := c.Databases[dbName]; !exists {
					c.Databases[dbName] = 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)+"_USER", ""),
						Password:        getEnv("MONGODB_"+strings.ToUpper(dbName)+"_PASS", ""),
						Database:        getEnv("MONGODB_"+strings.ToUpper(dbName)+"_DB", dbName),
						SSLMode:         getEnv("MONGOD_SSLMODE", "disable"),
						MaxOpenConns:    getEnvAsInt("MONGODB_MAX_OPEN_CONNS", 100),
						MaxIdleConns:    getEnvAsInt("MONGODB_MAX_IDLE_CONNS", 10),
						ConnMaxLifetime: parseDuration(getEnv("MONGODB_CONN_MAX_LIFETIME", "30m")),
					}
				}
			}
		}
	}
}

func getEnvFromMap(config map[string]string, key, defaultValue string) string {
	if value, exists := config[key]; exists {
		return value
	}
	return defaultValue
}

func getEnvAsIntFromMap(config map[string]string, key string, defaultValue int) int {
	if value, exists := config[key]; exists {
		if intValue, err := strconv.Atoi(value); err == nil {
			return intValue
		}
	}
	return defaultValue
}

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

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
}

// parseSchemes parses comma-separated schemes string into a slice
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 (c *Config) Validate() error {
	if len(c.Databases) == 0 {
		log.Fatal("At least one database configuration is required")
	}

	for name, db := range c.Databases {
		if db.Host == "" {
			log.Fatalf("Database host is required for %s", name)
		}
		if db.Username == "" {
			log.Fatalf("Database username is required for %s", name)
		}
		if db.Password == "" {
			log.Fatalf("Database password is required for %s", name)
		}
		if db.Database == "" {
			log.Fatalf("Database name is required for %s", name)
		}
	}

	if c.Bpjs.BaseURL == "" {
		log.Fatal("BPJS Base URL is required")
	}
	if c.Bpjs.ConsID == "" {
		log.Fatal("BPJS Consumer ID is required")
	}
	if c.Bpjs.UserKey == "" {
		log.Fatal("BPJS User Key is required")
	}
	if c.Bpjs.SecretKey == "" {
		log.Fatal("BPJS Secret Key is required")
	}

	// Validate Keycloak configuration if enabled
	if c.Keycloak.Enabled {
		if c.Keycloak.Issuer == "" {
			log.Fatal("Keycloak issuer is required when Keycloak is enabled")
		}
		if c.Keycloak.Audience == "" {
			log.Fatal("Keycloak audience is required when Keycloak is enabled")
		}
		if c.Keycloak.JwksURL == "" {
			log.Fatal("Keycloak JWKS URL is required when Keycloak is enabled")
		}
	}

	// Validate SatuSehat configuration
	if c.SatuSehat.OrgID == "" {
		log.Fatal("SatuSehat Organization ID is required")
	}
	if c.SatuSehat.FasyakesID == "" {
		log.Fatal("SatuSehat Fasyankes ID is required")
	}
	if c.SatuSehat.ClientID == "" {
		log.Fatal("SatuSehat Client ID is required")
	}
	if c.SatuSehat.ClientSecret == "" {
		log.Fatal("SatuSehat Client Secret is required")
	}
	if c.SatuSehat.AuthURL == "" {
		log.Fatal("SatuSehat Auth URL is required")
	}
	if c.SatuSehat.BaseURL == "" {
		log.Fatal("SatuSehat Base URL is required")
	}

	// Validate WebSocket configuration
	if c.WebSocket.ReadTimeout <= 0 {
		log.Fatal("WebSocket Read Timeout must be greater than 0")
	}
	if c.WebSocket.WriteTimeout <= 0 {
		log.Fatal("WebSocket Write Timeout must be greater than 0")
	}
	if c.WebSocket.PingInterval <= 0 {
		log.Fatal("WebSocket Ping Interval must be greater than 0")
	}
	if c.WebSocket.PongTimeout <= 0 {
		log.Fatal("WebSocket Pong Timeout must be greater than 0")
	}
	if c.WebSocket.ReadBufferSize <= 0 {
		log.Fatal("WebSocket Read Buffer Size must be greater than 0")
	}
	if c.WebSocket.WriteBufferSize <= 0 {
		log.Fatal("WebSocket Write Buffer Size must be greater than 0")
	}
	if c.WebSocket.ChannelBufferSize <= 0 {
		log.Fatal("WebSocket Channel Buffer Size must be greater than 0")
	}
	if c.WebSocket.MessageQueueSize <= 0 {
		log.Fatal("WebSocket Message Queue Size must be greater than 0")
	}
	if c.WebSocket.MaxMessageSize <= 0 {
		log.Fatal("WebSocket Max Message Size must be greater than 0")
	}
	if c.WebSocket.QueueWorkers <= 0 {
		log.Fatal("WebSocket Queue Workers must be greater than 0")
	}
	if c.WebSocket.ActivityLogSize <= 0 {
		log.Fatal("WebSocket Activity Log Size must be greater than 0")
	}
	if c.WebSocket.CleanupInterval <= 0 {
		log.Fatal("WebSocket Cleanup Interval must be greater than 0")
	}
	if c.WebSocket.InactiveTimeout <= 0 {
		log.Fatal("WebSocket Inactive Timeout must be greater than 0")
	}
	if c.WebSocket.ServerID == "" {
		log.Fatal("WebSocket Server ID is required")
	}

	return nil
}

//** WebSocket **//

// DefaultWebSocketConfig mengembalikan konfigurasi default untuk WebSocket
func DefaultWebSocketConfig() WebSocketConfig {
	return WebSocketConfig{
		// Timeout configurations
		ReadTimeout:      300 * time.Second,
		WriteTimeout:     30 * time.Second,
		PingInterval:     60 * time.Second,
		PongTimeout:      70 * time.Second,
		HandshakeTimeout: 45 * time.Second,

		// Buffer sizes
		ReadBufferSize:    8192,
		WriteBufferSize:   8192,
		ChannelBufferSize: 512,
		MessageQueueSize:  5000,

		// Connection limits
		MaxMessageSize: 8192,
		QueueWorkers:   10,

		// Monitoring
		ActivityLogSize: 1000,
		CleanupInterval: 2 * time.Minute,
		InactiveTimeout: 5 * time.Minute,

		// Server info
		ServerID: "api-service-v1",

		// Features
		EnableCompression: true,
		EnableMetrics:     true,
		EnableMonitoring:  true,
	}
}

// HighPerformanceWebSocketConfig mengembalikan konfigurasi untuk performa tinggi
func HighPerformanceWebSocketConfig() WebSocketConfig {
	return WebSocketConfig{
		// Timeout configurations
		ReadTimeout:      300 * time.Second,
		WriteTimeout:     30 * time.Second,
		PingInterval:     30 * time.Second,
		PongTimeout:      40 * time.Second,
		HandshakeTimeout: 30 * time.Second,

		// Buffer sizes
		ReadBufferSize:    16384,
		WriteBufferSize:   16384,
		ChannelBufferSize: 1024,
		MessageQueueSize:  10000,

		// Connection limits
		MaxMessageSize: 16384,
		QueueWorkers:   20,

		// Monitoring
		ActivityLogSize: 2000,
		CleanupInterval: 1 * time.Minute,
		InactiveTimeout: 3 * time.Minute,

		// Server info
		ServerID: "api-service-hp",

		// Features
		EnableCompression: true,
		EnableMetrics:     true,
		EnableMonitoring:  true,
	}
}

// LowResourceWebSocketConfig mengembalikan konfigurasi untuk sumber daya terbatas
func LowResourceWebSocketConfig() WebSocketConfig {
	return WebSocketConfig{
		// Timeout configurations
		ReadTimeout:      300 * time.Second,
		WriteTimeout:     30 * time.Second,
		PingInterval:     120 * time.Second,
		PongTimeout:      130 * time.Second,
		HandshakeTimeout: 60 * time.Second,

		// Buffer sizes
		ReadBufferSize:    4096,
		WriteBufferSize:   4096,
		ChannelBufferSize: 256,
		MessageQueueSize:  2500,

		// Connection limits
		MaxMessageSize: 4096,
		QueueWorkers:   5,

		// Monitoring
		ActivityLogSize: 500,
		CleanupInterval: 5 * time.Minute,
		InactiveTimeout: 10 * time.Minute,

		// Server info
		ServerID: "api-service-lr",

		// Features
		EnableCompression: false,
		EnableMetrics:     false,
		EnableMonitoring:  true,
	}
}

// CustomWebSocketConfig memungkinkan kustomisasi konfigurasi
func CustomWebSocketConfig(
	readTimeout, writeTimeout, pingInterval, pongTimeout time.Duration,
	readBufferSize, writeBufferSize, channelBufferSize, messageQueueSize int,
	maxMessageSize, queueWorkers int,
	activityLogSize int,
	cleanupInterval, inactiveTimeout time.Duration,
	serverID string,
	enableCompression, enableMetrics, enableMonitoring bool,
) WebSocketConfig {
	return WebSocketConfig{
		ReadTimeout:      readTimeout,
		WriteTimeout:     writeTimeout,
		PingInterval:     pingInterval,
		PongTimeout:      pongTimeout,
		HandshakeTimeout: 45 * time.Second,

		ReadBufferSize:    readBufferSize,
		WriteBufferSize:   writeBufferSize,
		ChannelBufferSize: channelBufferSize,
		MessageQueueSize:  messageQueueSize,

		MaxMessageSize: maxMessageSize,
		QueueWorkers:   queueWorkers,

		ActivityLogSize: activityLogSize,
		CleanupInterval: cleanupInterval,
		InactiveTimeout: inactiveTimeout,

		ServerID: serverID,

		EnableCompression: enableCompression,
		EnableMetrics:     enableMetrics,
		EnableMonitoring:  enableMonitoring,
	}
}
