// Package config provides configuration management for the SigNoz MCP server.
//
// # Environment Variable Override System
//
// ANY configuration value can be overridden using environment variables with the prefix "SIGNOZ_MCP_".
// The environment variable names follow this pattern:
//
//	SIGNOZ_MCP_<SECTION>_<KEY>
//
// Examples:
//   - SIGNOZ_MCP_SIGNOZ_URL="https://your-signoz.example.com"
//   - SIGNOZ_MCP_SIGNOZ_API_KEY="your-api-key-here"
//   - SIGNOZ_MCP_LOG_LEVEL="debug"
//   - SIGNOZ_MCP_SERVER_TIMEOUT="60s"
//   - SIGNOZ_MCP_TELEMETRY_ENABLED="false"
//
// # Configuration Sources (in order of precedence):
//  1. Environment variables (highest priority)
//  2. Configuration file (config.yaml)
//  3. Default values (lowest priority)
//
// # All Available Environment Variables:
//
// ## Server Configuration:
//   - SIGNOZ_MCP_SERVER_NAME
//   - SIGNOZ_MCP_SERVER_VERSION
//   - SIGNOZ_MCP_SERVER_ENVIRONMENT
//   - SIGNOZ_MCP_SERVER_DEBUG
//   - SIGNOZ_MCP_SERVER_TIMEOUT
//
// ## SigNoz Configuration:
//   - SIGNOZ_MCP_SIGNOZ_URL
//   - SIGNOZ_MCP_SIGNOZ_API_KEY
//   - SIGNOZ_MCP_SIGNOZ_TIMEOUT
//   - SIGNOZ_MCP_SIGNOZ_RETRY_ATTEMPTS
//   - SIGNOZ_MCP_SIGNOZ_RETRY_DELAY
//   - SIGNOZ_MCP_SIGNOZ_MAX_RETRY_DELAY
//   - SIGNOZ_MCP_SIGNOZ_TLS_ENABLED
//   - SIGNOZ_MCP_SIGNOZ_TLS_SKIP_VERIFY
//   - SIGNOZ_MCP_SIGNOZ_MAX_CONNECTIONS
//   - SIGNOZ_MCP_SIGNOZ_IDLE_TIMEOUT
//   - SIGNOZ_MCP_SIGNOZ_KEEP_ALIVE
//
// ## Logging Configuration:
//   - SIGNOZ_MCP_LOG_LEVEL
//   - SIGNOZ_MCP_LOG_FORMAT
//   - SIGNOZ_MCP_LOG_OUTPUT
//   - SIGNOZ_MCP_LOG_STRUCTURED
//
// ## Telemetry Configuration:
//   - SIGNOZ_MCP_TELEMETRY_ENABLED
//   - SIGNOZ_MCP_TELEMETRY_SERVICE_NAME
//   - SIGNOZ_MCP_TELEMETRY_SERVICE_VERSION
//   - SIGNOZ_MCP_TELEMETRY_ENVIRONMENT
//   - SIGNOZ_MCP_TELEMETRY_TRACE_ENDPOINT
//   - SIGNOZ_MCP_TELEMETRY_METRICS_ENDPOINT
//   - SIGNOZ_MCP_TELEMETRY_SAMPLE_RATE
//   - SIGNOZ_MCP_TELEMETRY_BATCH_TIMEOUT
//   - SIGNOZ_MCP_TELEMETRY_EXPORT_TIMEOUT
//   - SIGNOZ_MCP_TELEMETRY_MAX_EXPORT_BATCH_SIZE
//
// ## Cache Configuration:
//   - SIGNOZ_MCP_CACHE_ENABLED
//   - SIGNOZ_MCP_CACHE_TTL
//   - SIGNOZ_MCP_CACHE_MAX_ENTRIES
package config

import (
	"fmt"
	"strings"
	"time"

	"github.com/CalmoAI/mcp-server-signoz/internal/domain"
	"github.com/spf13/viper"
)

// Config represents the application configuration
type Config struct {
	Server    ServerConfig    `mapstructure:"server"`
	Signoz    SignozConfig    `mapstructure:"signoz"`
	Log       LogConfig       `mapstructure:"log"`
	Telemetry TelemetryConfig `mapstructure:"telemetry"`
	Cache     CacheConfig     `mapstructure:"cache"`
}

// ServerConfig represents server configuration
type ServerConfig struct {
	Name        string        `mapstructure:"name"`
	Version     string        `mapstructure:"version"`
	Environment string        `mapstructure:"environment"`
	Debug       bool          `mapstructure:"debug"`
	Timeout     time.Duration `mapstructure:"timeout"`
}

// SignozConfig represents Signoz configuration
type SignozConfig struct {
	URL            string        `mapstructure:"url"`
	APIKey         string        `mapstructure:"api_key"`
	Timeout        time.Duration `mapstructure:"timeout"`
	RetryAttempts  int           `mapstructure:"retry_attempts"`
	RetryDelay     time.Duration `mapstructure:"retry_delay"`
	MaxRetryDelay  time.Duration `mapstructure:"max_retry_delay"`
	TLSEnabled     bool          `mapstructure:"tls_enabled"`
	TLSSkipVerify  bool          `mapstructure:"tls_skip_verify"`
	MaxConnections int           `mapstructure:"max_connections"`
	IdleTimeout    time.Duration `mapstructure:"idle_timeout"`
	KeepAlive      time.Duration `mapstructure:"keep_alive"`
}

// LogConfig represents logging configuration
type LogConfig struct {
	Level      string `mapstructure:"level"`
	Format     string `mapstructure:"format"`
	Output     string `mapstructure:"output"`
	Structured bool   `mapstructure:"structured"`
}

// TelemetryConfig represents telemetry configuration
type TelemetryConfig struct {
	Enabled            bool          `mapstructure:"enabled"`
	ServiceName        string        `mapstructure:"service_name"`
	ServiceVersion     string        `mapstructure:"service_version"`
	Environment        string        `mapstructure:"environment"`
	TraceEndpoint      string        `mapstructure:"trace_endpoint"`
	MetricsEndpoint    string        `mapstructure:"metrics_endpoint"`
	SampleRate         float64       `mapstructure:"sample_rate"`
	BatchTimeout       time.Duration `mapstructure:"batch_timeout"`
	ExportTimeout      time.Duration `mapstructure:"export_timeout"`
	MaxExportBatchSize int           `mapstructure:"max_export_batch_size"`
}

// CacheConfig represents cache configuration
type CacheConfig struct {
	Enabled    bool          `mapstructure:"enabled"`
	TTL        time.Duration `mapstructure:"ttl"`
	MaxEntries int           `mapstructure:"max_entries"`
}

// Repository implements the ConfigRepository interface
type Repository struct {
	config *Config
}

// NewRepository creates a new config repository
func NewRepository() *Repository {
	return &Repository{}
}

// Load loads configuration from various sources
func (r *Repository) Load() error {
	// Set default values
	r.setDefaults()

	// Configure viper
	viper.SetConfigName("config")
	viper.SetConfigType("yaml")
	viper.AddConfigPath(".")
	viper.AddConfigPath("./configs")
	viper.AddConfigPath("/etc/signoz-mcp-server")

	// Enable environment variable substitution
	viper.AutomaticEnv()
	viper.SetEnvPrefix("SIGNOZ_MCP")
	viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))

	// Bind all environment variables explicitly to ensure nested structures work
	r.bindEnvironmentVariables()

	// Read config file (optional)
	if err := viper.ReadInConfig(); err != nil {
		if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
			return fmt.Errorf("failed to read config file: %w", err)
		}
		// Config file not found is OK, we'll use defaults and env vars
	}

	// Initialize config struct
	r.config = &Config{}

	// Unmarshal into config struct
	if err := viper.Unmarshal(r.config); err != nil {
		return fmt.Errorf("failed to unmarshal config: %w", err)
	}

	// Validate configuration
	if err := r.Validate(); err != nil {
		return fmt.Errorf("config validation failed: %w", err)
	}

	return nil
}

// bindEnvironmentVariables explicitly binds all config keys to environment variables
// This ensures that nested structures can be overridden via env vars
func (r *Repository) bindEnvironmentVariables() {
	// Server config bindings
	viper.BindEnv("server.name", "SIGNOZ_MCP_SERVER_NAME")
	viper.BindEnv("server.version", "SIGNOZ_MCP_SERVER_VERSION")
	viper.BindEnv("server.environment", "SIGNOZ_MCP_SERVER_ENVIRONMENT")
	viper.BindEnv("server.debug", "SIGNOZ_MCP_SERVER_DEBUG")
	viper.BindEnv("server.timeout", "SIGNOZ_MCP_SERVER_TIMEOUT")

	// Signoz config bindings
	viper.BindEnv("signoz.url", "SIGNOZ_MCP_SIGNOZ_URL")
	viper.BindEnv("signoz.api_key", "SIGNOZ_MCP_SIGNOZ_API_KEY")
	viper.BindEnv("signoz.timeout", "SIGNOZ_MCP_SIGNOZ_TIMEOUT")
	viper.BindEnv("signoz.retry_attempts", "SIGNOZ_MCP_SIGNOZ_RETRY_ATTEMPTS")
	viper.BindEnv("signoz.retry_delay", "SIGNOZ_MCP_SIGNOZ_RETRY_DELAY")
	viper.BindEnv("signoz.max_retry_delay", "SIGNOZ_MCP_SIGNOZ_MAX_RETRY_DELAY")
	viper.BindEnv("signoz.tls_enabled", "SIGNOZ_MCP_SIGNOZ_TLS_ENABLED")
	viper.BindEnv("signoz.tls_skip_verify", "SIGNOZ_MCP_SIGNOZ_TLS_SKIP_VERIFY")
	viper.BindEnv("signoz.max_connections", "SIGNOZ_MCP_SIGNOZ_MAX_CONNECTIONS")
	viper.BindEnv("signoz.idle_timeout", "SIGNOZ_MCP_SIGNOZ_IDLE_TIMEOUT")
	viper.BindEnv("signoz.keep_alive", "SIGNOZ_MCP_SIGNOZ_KEEP_ALIVE")

	// Log config bindings
	viper.BindEnv("log.level", "SIGNOZ_MCP_LOG_LEVEL")
	viper.BindEnv("log.format", "SIGNOZ_MCP_LOG_FORMAT")
	viper.BindEnv("log.output", "SIGNOZ_MCP_LOG_OUTPUT")
	viper.BindEnv("log.structured", "SIGNOZ_MCP_LOG_STRUCTURED")

	// Telemetry config bindings
	viper.BindEnv("telemetry.enabled", "SIGNOZ_MCP_TELEMETRY_ENABLED")
	viper.BindEnv("telemetry.service_name", "SIGNOZ_MCP_TELEMETRY_SERVICE_NAME")
	viper.BindEnv("telemetry.service_version", "SIGNOZ_MCP_TELEMETRY_SERVICE_VERSION")
	viper.BindEnv("telemetry.environment", "SIGNOZ_MCP_TELEMETRY_ENVIRONMENT")
	viper.BindEnv("telemetry.trace_endpoint", "SIGNOZ_MCP_TELEMETRY_TRACE_ENDPOINT")
	viper.BindEnv("telemetry.metrics_endpoint", "SIGNOZ_MCP_TELEMETRY_METRICS_ENDPOINT")
	viper.BindEnv("telemetry.sample_rate", "SIGNOZ_MCP_TELEMETRY_SAMPLE_RATE")
	viper.BindEnv("telemetry.batch_timeout", "SIGNOZ_MCP_TELEMETRY_BATCH_TIMEOUT")
	viper.BindEnv("telemetry.export_timeout", "SIGNOZ_MCP_TELEMETRY_EXPORT_TIMEOUT")
	viper.BindEnv("telemetry.max_export_batch_size", "SIGNOZ_MCP_TELEMETRY_MAX_EXPORT_BATCH_SIZE")

	// Cache config bindings
	viper.BindEnv("cache.enabled", "SIGNOZ_MCP_CACHE_ENABLED")
	viper.BindEnv("cache.ttl", "SIGNOZ_MCP_CACHE_TTL")
	viper.BindEnv("cache.max_entries", "SIGNOZ_MCP_CACHE_MAX_ENTRIES")
}

// setDefaults sets default configuration values
func (r *Repository) setDefaults() {
	// Server defaults
	viper.SetDefault("server.name", "signoz-mcp-server")
	viper.SetDefault("server.version", "1.0.0")
	viper.SetDefault("server.environment", "development")
	viper.SetDefault("server.debug", false)
	viper.SetDefault("server.timeout", "30s")

	// Signoz defaults
	viper.SetDefault("signoz.url", "http://localhost:3301")
	viper.SetDefault("signoz.timeout", "30s")
	viper.SetDefault("signoz.retry_attempts", 3)
	viper.SetDefault("signoz.retry_delay", "1s")
	viper.SetDefault("signoz.max_retry_delay", "30s")
	viper.SetDefault("signoz.tls_enabled", true)
	viper.SetDefault("signoz.tls_skip_verify", false)
	viper.SetDefault("signoz.max_connections", 100)
	viper.SetDefault("signoz.idle_timeout", "90s")
	viper.SetDefault("signoz.keep_alive", "30s")

	// Log defaults
	viper.SetDefault("log.level", "info")
	viper.SetDefault("log.format", "json")
	viper.SetDefault("log.output", "stderr")
	viper.SetDefault("log.structured", true)

	// Telemetry defaults
	viper.SetDefault("telemetry.enabled", true)
	viper.SetDefault("telemetry.service_name", "signoz-mcp-server")
	viper.SetDefault("telemetry.service_version", "1.0.0")
	viper.SetDefault("telemetry.environment", "development")
	viper.SetDefault("telemetry.sample_rate", 1.0)
	viper.SetDefault("telemetry.batch_timeout", "5s")
	viper.SetDefault("telemetry.export_timeout", "30s")
	viper.SetDefault("telemetry.max_export_batch_size", 512)

	// Cache defaults
	viper.SetDefault("cache.enabled", true)
	viper.SetDefault("cache.ttl", "5m")
	viper.SetDefault("cache.max_entries", 1000)
}

// GetSignozURL implements ConfigRepository interface
func (r *Repository) GetSignozURL() string {
	if r.config == nil {
		return ""
	}
	return r.config.Signoz.URL
}

// GetSignozAPIKey implements ConfigRepository interface
func (r *Repository) GetSignozAPIKey() string {
	if r.config == nil {
		return ""
	}
	return r.config.Signoz.APIKey
}

// GetTimeout implements ConfigRepository interface
func (r *Repository) GetTimeout() time.Duration {
	if r.config == nil {
		return 30 * time.Second
	}
	return r.config.Signoz.Timeout
}

// GetRetryAttempts implements ConfigRepository interface
func (r *Repository) GetRetryAttempts() int {
	if r.config == nil {
		return 3
	}
	return r.config.Signoz.RetryAttempts
}

// GetRetryDelay implements ConfigRepository interface
func (r *Repository) GetRetryDelay() time.Duration {
	if r.config == nil {
		return time.Second
	}
	return r.config.Signoz.RetryDelay
}

// Validate implements ConfigRepository interface
func (r *Repository) Validate() error {
	if r.config == nil {
		return fmt.Errorf("config is not loaded")
	}

	var errors []string

	// Validate server config
	if r.config.Server.Name == "" {
		errors = append(errors, "server.name is required")
	}
	if r.config.Server.Version == "" {
		errors = append(errors, "server.version is required")
	}
	if r.config.Server.Timeout <= 0 {
		errors = append(errors, "server.timeout must be positive")
	}

	// Validate Signoz config
	if r.config.Signoz.URL == "" {
		errors = append(errors, "signoz.url is required")
	}
	if r.config.Signoz.Timeout <= 0 {
		errors = append(errors, "signoz.timeout must be positive")
	}
	if r.config.Signoz.RetryAttempts < 0 {
		errors = append(errors, "signoz.retry_attempts must be non-negative")
	}
	if r.config.Signoz.RetryDelay <= 0 {
		errors = append(errors, "signoz.retry_delay must be positive")
	}
	if r.config.Signoz.MaxRetryDelay <= 0 {
		errors = append(errors, "signoz.max_retry_delay must be positive")
	}
	if r.config.Signoz.MaxConnections <= 0 {
		errors = append(errors, "signoz.max_connections must be positive")
	}

	// Validate log config
	validLogLevels := []string{"debug", "info", "warn", "error", "fatal"}
	if !contains(validLogLevels, r.config.Log.Level) {
		errors = append(errors, fmt.Sprintf("log.level must be one of: %v", validLogLevels))
	}

	validLogFormats := []string{"json", "text"}
	if !contains(validLogFormats, r.config.Log.Format) {
		errors = append(errors, fmt.Sprintf("log.format must be one of: %v", validLogFormats))
	}

	// Validate telemetry config
	if r.config.Telemetry.Enabled {
		if r.config.Telemetry.ServiceName == "" {
			errors = append(errors, "telemetry.service_name is required when telemetry is enabled")
		}
		if r.config.Telemetry.SampleRate < 0 || r.config.Telemetry.SampleRate > 1 {
			errors = append(errors, "telemetry.sample_rate must be between 0 and 1")
		}
		if r.config.Telemetry.BatchTimeout <= 0 {
			errors = append(errors, "telemetry.batch_timeout must be positive")
		}
		if r.config.Telemetry.ExportTimeout <= 0 {
			errors = append(errors, "telemetry.export_timeout must be positive")
		}
		if r.config.Telemetry.MaxExportBatchSize <= 0 {
			errors = append(errors, "telemetry.max_export_batch_size must be positive")
		}
	}

	// Validate cache config
	if r.config.Cache.Enabled {
		if r.config.Cache.TTL <= 0 {
			errors = append(errors, "cache.ttl must be positive when cache is enabled")
		}
		if r.config.Cache.MaxEntries <= 0 {
			errors = append(errors, "cache.max_entries must be positive when cache is enabled")
		}
	}

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

	return nil
}

// GetConfig returns the loaded configuration
func (r *Repository) GetConfig() *Config {
	return r.config
}

// GetServerConfig returns the server configuration
func (r *Repository) GetServerConfig() *ServerConfig {
	if r.config == nil {
		return nil
	}
	return &r.config.Server
}

// GetSignozConfig returns the Signoz configuration
func (r *Repository) GetSignozConfig() *SignozConfig {
	if r.config == nil {
		return nil
	}
	return &r.config.Signoz
}

// GetLogConfig returns the log configuration
func (r *Repository) GetLogConfig() *LogConfig {
	if r.config == nil {
		return nil
	}
	return &r.config.Log
}

// GetTelemetryConfig returns the telemetry configuration
func (r *Repository) GetTelemetryConfig() *TelemetryConfig {
	if r.config == nil {
		return nil
	}
	return &r.config.Telemetry
}

// GetCacheConfig returns the cache configuration
func (r *Repository) GetCacheConfig() *CacheConfig {
	if r.config == nil {
		return nil
	}
	return &r.config.Cache
}

// IsDebugEnabled returns whether debug mode is enabled
func (r *Repository) IsDebugEnabled() bool {
	if r.config == nil {
		return false
	}
	return r.config.Server.Debug
}

// IsTelemetryEnabled returns whether telemetry is enabled
func (r *Repository) IsTelemetryEnabled() bool {
	if r.config == nil {
		return false
	}
	return r.config.Telemetry.Enabled
}

// IsCacheEnabled returns whether cache is enabled
func (r *Repository) IsCacheEnabled() bool {
	if r.config == nil {
		return false
	}
	return r.config.Cache.Enabled
}

// GetAllEnvironmentVariables returns a list of all supported environment variables
// This is useful for documentation and debugging
func (r *Repository) GetAllEnvironmentVariables() []string {
	return []string{
		// Server config
		"SIGNOZ_MCP_SERVER_NAME",
		"SIGNOZ_MCP_SERVER_VERSION",
		"SIGNOZ_MCP_SERVER_ENVIRONMENT",
		"SIGNOZ_MCP_SERVER_DEBUG",
		"SIGNOZ_MCP_SERVER_TIMEOUT",

		// Signoz config
		"SIGNOZ_MCP_SIGNOZ_URL",
		"SIGNOZ_MCP_SIGNOZ_API_KEY",
		"SIGNOZ_MCP_SIGNOZ_TIMEOUT",
		"SIGNOZ_MCP_SIGNOZ_RETRY_ATTEMPTS",
		"SIGNOZ_MCP_SIGNOZ_RETRY_DELAY",
		"SIGNOZ_MCP_SIGNOZ_MAX_RETRY_DELAY",
		"SIGNOZ_MCP_SIGNOZ_TLS_ENABLED",
		"SIGNOZ_MCP_SIGNOZ_TLS_SKIP_VERIFY",
		"SIGNOZ_MCP_SIGNOZ_MAX_CONNECTIONS",
		"SIGNOZ_MCP_SIGNOZ_IDLE_TIMEOUT",
		"SIGNOZ_MCP_SIGNOZ_KEEP_ALIVE",

		// Log config
		"SIGNOZ_MCP_LOG_LEVEL",
		"SIGNOZ_MCP_LOG_FORMAT",
		"SIGNOZ_MCP_LOG_OUTPUT",
		"SIGNOZ_MCP_LOG_STRUCTURED",

		// Telemetry config
		"SIGNOZ_MCP_TELEMETRY_ENABLED",
		"SIGNOZ_MCP_TELEMETRY_SERVICE_NAME",
		"SIGNOZ_MCP_TELEMETRY_SERVICE_VERSION",
		"SIGNOZ_MCP_TELEMETRY_ENVIRONMENT",
		"SIGNOZ_MCP_TELEMETRY_TRACE_ENDPOINT",
		"SIGNOZ_MCP_TELEMETRY_METRICS_ENDPOINT",
		"SIGNOZ_MCP_TELEMETRY_SAMPLE_RATE",
		"SIGNOZ_MCP_TELEMETRY_BATCH_TIMEOUT",
		"SIGNOZ_MCP_TELEMETRY_EXPORT_TIMEOUT",
		"SIGNOZ_MCP_TELEMETRY_MAX_EXPORT_BATCH_SIZE",

		// Cache config
		"SIGNOZ_MCP_CACHE_ENABLED",
		"SIGNOZ_MCP_CACHE_TTL",
		"SIGNOZ_MCP_CACHE_MAX_ENTRIES",
	}
}

// contains checks if a slice contains a specific string
func contains(slice []string, item string) bool {
	for _, s := range slice {
		if s == item {
			return true
		}
	}
	return false
}

// Ensure Repository implements ConfigRepository interface
var _ domain.ConfigRepository = (*Repository)(nil)
