// Package config provides configuration management for MCA SDK.
package config

import (
	"errors"
	"fmt"
	"net/url"
	"os"
	"strconv"
	"strings"
)

// Config holds the configuration for MCAClient.
// Supports initialization from structs or environment variables.
type Config struct {
	// Required fields
	ServiceName string // Service name for resource attributes
	ModelID     string // Model identifier
	TeamName    string // Team name for resource attributes

	// Optional fields with defaults
	ModelVersion            string // Model version (default: "1.0.0")
	ModelType               string // Model type (default: "internal")
	ModelCategory           string // Model category (default: "internal")
	CollectorEndpoint       string // OTel Collector endpoint (default: "http://localhost:4318")
	Protocol                string // OTLP protocol: "http" or "grpc" (default: "http")
	TLSInsecure             bool   // Allow insecure TLS connections (default: false, only for local dev)
	DebugMode               bool   // Enable debug logging (default: false)
	CircuitBreakerThreshold int    // Circuit breaker failure threshold (default: 5)
	CircuitBreakerTimeout   int    // Circuit breaker timeout in seconds (default: 60)
}

// LoadFromEnv creates a Config from environment variables with field defaults.
// Environment variables follow the pattern MCA_<FIELD_NAME> (uppercase with underscores).
func LoadFromEnv() *Config {
	cfg := &Config{
		ServiceName:             os.Getenv("MCA_SERVICE_NAME"),
		ModelID:                 os.Getenv("MCA_MODEL_ID"),
		TeamName:                os.Getenv("MCA_TEAM_NAME"),
		ModelVersion:            getEnvOrDefault("MCA_MODEL_VERSION", "1.0.0"),
		ModelType:               getEnvOrDefault("MCA_MODEL_TYPE", "internal"),
		ModelCategory:           getEnvOrDefault("MCA_MODEL_CATEGORY", "internal"),
		CollectorEndpoint:       getEnvOrDefault("MCA_COLLECTOR_ENDPOINT", "http://localhost:4318"),
		Protocol:                getEnvOrDefault("MCA_OTEL_PROTOCOL", "http"),
		TLSInsecure:             getEnvBool("MCA_TLS_INSECURE", false),
		DebugMode:               getEnvBool("MCA_DEBUG_MODE", false),
		CircuitBreakerThreshold: getEnvInt("MCA_CIRCUIT_BREAKER_THRESHOLD", 5),
		CircuitBreakerTimeout:   getEnvInt("MCA_CIRCUIT_BREAKER_TIMEOUT", 60),
	}
	return cfg
}

// Validate checks that required fields are set and values are valid.
// Returns an error if validation fails with actionable message.
func (c *Config) Validate() error {
	var errs []string

	// Check required fields
	if c.ServiceName == "" {
		errs = append(errs, "service_name is required. Set via Config{ServiceName: \"...\"} or environment variable MCA_SERVICE_NAME")
	}
	if c.ModelID == "" {
		errs = append(errs, "model_id is required. Set via Config{ModelID: \"...\"} or environment variable MCA_MODEL_ID")
	}
	if c.TeamName == "" {
		errs = append(errs, "team_name is required. Set via Config{TeamName: \"...\"} or environment variable MCA_TEAM_NAME")
	}

	// Validate collector endpoint URL
	if c.CollectorEndpoint != "" {
		if _, err := url.Parse(c.CollectorEndpoint); err != nil {
			errs = append(errs, fmt.Sprintf("invalid collector_endpoint URL: %v", err))
		}
	}

	// Validate protocol
	if c.Protocol != "http" && c.Protocol != "grpc" {
		errs = append(errs, fmt.Sprintf("invalid protocol %q, must be \"http\" or \"grpc\"", c.Protocol))
	}

	// Validate model type
	validModelTypes := map[string]bool{"internal": true, "vendor": true, "genai": true, "agentic": true}
	if !validModelTypes[c.ModelType] {
		errs = append(errs, fmt.Sprintf("invalid model_type %q, must be one of: internal, vendor, genai, agentic", c.ModelType))
	}

	// Validate model category
	validCategories := map[string]bool{"internal": true, "external": true}
	if !validCategories[c.ModelCategory] {
		errs = append(errs, fmt.Sprintf("invalid model_category %q, must be one of: internal, external", c.ModelCategory))
	}

	if len(errs) > 0 {
		return &ConfigurationError{Message: strings.Join(errs, "; ")}
	}

	return nil
}

// ApplyDefaults sets default values for optional fields if not already set.
func (c *Config) ApplyDefaults() {
	if c.ModelVersion == "" {
		c.ModelVersion = "1.0.0"
	}
	if c.ModelType == "" {
		c.ModelType = "internal"
	}
	if c.ModelCategory == "" {
		c.ModelCategory = "internal"
	}
	if c.CollectorEndpoint == "" {
		c.CollectorEndpoint = "http://localhost:4318"
	}
	if c.Protocol == "" {
		c.Protocol = "http"
	}
	if c.CircuitBreakerThreshold == 0 {
		c.CircuitBreakerThreshold = 5
	}
	if c.CircuitBreakerTimeout == 0 {
		c.CircuitBreakerTimeout = 60
	}
}

// ConfigurationError represents a configuration validation error.
type ConfigurationError struct {
	Message string
}

func (e *ConfigurationError) Error() string {
	return fmt.Sprintf("configuration error: %s", e.Message)
}

// Helper functions

func getEnvOrDefault(key, defaultValue string) string {
	if value := os.Getenv(key); value != "" {
		return value
	}
	return defaultValue
}

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

func getEnvInt(key string, defaultValue int) int {
	value := os.Getenv(key)
	if value == "" {
		return defaultValue
	}
	i, err := strconv.Atoi(value)
	if err != nil {
		return defaultValue
	}
	return i
}

// IsConfigurationError checks if an error is a ConfigurationError.
func IsConfigurationError(err error) bool {
	var configErr *ConfigurationError
	return errors.As(err, &configErr)
}
