package config

import (
	"os"
	"testing"
)

// TestValidate_Success verifies valid configuration passes validation.
func TestValidate_Success(t *testing.T) {
	cfg := &Config{
		ServiceName:       "test-service",
		ModelID:           "mdl-001",
		TeamName:          "test-team",
		ModelVersion:      "1.0.0",
		ModelType:         "internal",
		ModelCategory:     "internal",
		CollectorEndpoint: "http://localhost:4318",
		Protocol:          "http",
	}

	err := cfg.Validate()
	if err != nil {
		t.Errorf("Validate() unexpected error: %v", err)
	}
}

// TestValidate_MissingRequired verifies validation fails for missing fields.
func TestValidate_MissingRequired(t *testing.T) {
	tests := []struct {
		name   string
		config *Config
	}{
		{
			name: "missing service name",
			config: &Config{
				ModelID:  "mdl-001",
				TeamName: "test-team",
			},
		},
		{
			name: "missing model id",
			config: &Config{
				ServiceName: "test-service",
				TeamName:    "test-team",
			},
		},
		{
			name: "missing team name",
			config: &Config{
				ServiceName: "test-service",
				ModelID:     "mdl-001",
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := tt.config.Validate()
			if err == nil {
				t.Error("Validate() expected error, got nil")
			}
			if !IsConfigurationError(err) {
				t.Errorf("Validate() error type = %T, want ConfigurationError", err)
			}
		})
	}
}

// TestValidate_InvalidURL verifies validation fails for invalid URLs.
func TestValidate_InvalidURL(t *testing.T) {
	cfg := &Config{
		ServiceName:       "test-service",
		ModelID:           "mdl-001",
		TeamName:          "test-team",
		CollectorEndpoint: "not a valid url",
		Protocol:          "http",
	}

	err := cfg.Validate()
	if err == nil {
		t.Error("Validate() expected error for invalid URL, got nil")
	}
}

// TestValidate_InvalidProtocol verifies validation fails for invalid protocol.
func TestValidate_InvalidProtocol(t *testing.T) {
	cfg := &Config{
		ServiceName:       "test-service",
		ModelID:           "mdl-001",
		TeamName:          "test-team",
		CollectorEndpoint: "http://localhost:4318",
		Protocol:          "ftp", // Invalid protocol
	}

	err := cfg.Validate()
	if err == nil {
		t.Error("Validate() expected error for invalid protocol, got nil")
	}
}

// TestValidate_InvalidModelType verifies validation fails for invalid model type.
func TestValidate_InvalidModelType(t *testing.T) {
	cfg := &Config{
		ServiceName:       "test-service",
		ModelID:           "mdl-001",
		TeamName:          "test-team",
		CollectorEndpoint: "http://localhost:4318",
		Protocol:          "http",
		ModelType:         "invalid", // Invalid model type
	}

	err := cfg.Validate()
	if err == nil {
		t.Error("Validate() expected error for invalid model type, got nil")
	}
}

// TestValidate_InvalidModelCategory verifies validation fails for invalid category.
func TestValidate_InvalidModelCategory(t *testing.T) {
	cfg := &Config{
		ServiceName:       "test-service",
		ModelID:           "mdl-001",
		TeamName:          "test-team",
		CollectorEndpoint: "http://localhost:4318",
		Protocol:          "http",
		ModelType:         "internal",
		ModelCategory:     "invalid", // Invalid category
	}

	err := cfg.Validate()
	if err == nil {
		t.Error("Validate() expected error for invalid model category, got nil")
	}
}

// TestApplyDefaults verifies defaults are applied correctly.
func TestApplyDefaults(t *testing.T) {
	cfg := &Config{
		ServiceName: "test-service",
		ModelID:     "mdl-001",
		TeamName:    "test-team",
		// Don't set optional fields
	}

	cfg.ApplyDefaults()

	if cfg.ModelVersion != "1.0.0" {
		t.Errorf("ModelVersion = %q, want %q", cfg.ModelVersion, "1.0.0")
	}
	if cfg.ModelType != "internal" {
		t.Errorf("ModelType = %q, want %q", cfg.ModelType, "internal")
	}
	if cfg.ModelCategory != "internal" {
		t.Errorf("ModelCategory = %q, want %q", cfg.ModelCategory, "internal")
	}
	if cfg.CollectorEndpoint != "http://localhost:4318" {
		t.Errorf("CollectorEndpoint = %q, want %q", cfg.CollectorEndpoint, "http://localhost:4318")
	}
	if cfg.Protocol != "http" {
		t.Errorf("Protocol = %q, want %q", cfg.Protocol, "http")
	}
	if cfg.CircuitBreakerThreshold != 5 {
		t.Errorf("CircuitBreakerThreshold = %d, want %d", cfg.CircuitBreakerThreshold, 5)
	}
	if cfg.CircuitBreakerTimeout != 60 {
		t.Errorf("CircuitBreakerTimeout = %d, want %d", cfg.CircuitBreakerTimeout, 60)
	}
}

// TestLoadFromEnv verifies environment variable loading.
func TestLoadFromEnv(t *testing.T) {
	// Save original environment
	originalEnv := map[string]string{
		"MCA_SERVICE_NAME":       os.Getenv("MCA_SERVICE_NAME"),
		"MCA_MODEL_ID":           os.Getenv("MCA_MODEL_ID"),
		"MCA_TEAM_NAME":          os.Getenv("MCA_TEAM_NAME"),
		"MCA_MODEL_VERSION":      os.Getenv("MCA_MODEL_VERSION"),
		"MCA_COLLECTOR_ENDPOINT": os.Getenv("MCA_COLLECTOR_ENDPOINT"),
	}
	defer func() {
		// Restore original environment
		for k, v := range originalEnv {
			if v == "" {
				os.Unsetenv(k)
			} else {
				os.Setenv(k, v)
			}
		}
	}()

	// Set test environment variables
	os.Setenv("MCA_SERVICE_NAME", "env-service")
	os.Setenv("MCA_MODEL_ID", "mdl-env-001")
	os.Setenv("MCA_TEAM_NAME", "env-team")
	os.Setenv("MCA_MODEL_VERSION", "2.0.0")
	os.Setenv("MCA_COLLECTOR_ENDPOINT", "http://collector:4318")

	cfg := LoadFromEnv()

	if cfg.ServiceName != "env-service" {
		t.Errorf("ServiceName = %q, want %q", cfg.ServiceName, "env-service")
	}
	if cfg.ModelID != "mdl-env-001" {
		t.Errorf("ModelID = %q, want %q", cfg.ModelID, "mdl-env-001")
	}
	if cfg.TeamName != "env-team" {
		t.Errorf("TeamName = %q, want %q", cfg.TeamName, "env-team")
	}
	if cfg.ModelVersion != "2.0.0" {
		t.Errorf("ModelVersion = %q, want %q", cfg.ModelVersion, "2.0.0")
	}
	if cfg.CollectorEndpoint != "http://collector:4318" {
		t.Errorf("CollectorEndpoint = %q, want %q", cfg.CollectorEndpoint, "http://collector:4318")
	}
}

// TestLoadFromEnv_WithDefaults verifies defaults are used when env vars are not set.
func TestLoadFromEnv_WithDefaults(t *testing.T) {
	// Save original environment
	originalEnv := map[string]string{
		"MCA_MODEL_VERSION":      os.Getenv("MCA_MODEL_VERSION"),
		"MCA_COLLECTOR_ENDPOINT": os.Getenv("MCA_COLLECTOR_ENDPOINT"),
		"MCA_OTEL_PROTOCOL":      os.Getenv("MCA_OTEL_PROTOCOL"),
	}
	defer func() {
		// Restore original environment
		for k, v := range originalEnv {
			if v == "" {
				os.Unsetenv(k)
			} else {
				os.Setenv(k, v)
			}
		}
	}()

	// Unset optional environment variables
	os.Unsetenv("MCA_MODEL_VERSION")
	os.Unsetenv("MCA_COLLECTOR_ENDPOINT")
	os.Unsetenv("MCA_OTEL_PROTOCOL")

	cfg := LoadFromEnv()

	if cfg.ModelVersion != "1.0.0" {
		t.Errorf("ModelVersion = %q, want %q (default)", cfg.ModelVersion, "1.0.0")
	}
	if cfg.CollectorEndpoint != "http://localhost:4318" {
		t.Errorf("CollectorEndpoint = %q, want %q (default)", cfg.CollectorEndpoint, "http://localhost:4318")
	}
	if cfg.Protocol != "http" {
		t.Errorf("Protocol = %q, want %q (default)", cfg.Protocol, "http")
	}
}

// TestIsConfigurationError verifies error type checking.
func TestIsConfigurationError(t *testing.T) {
	tests := []struct {
		name string
		err  error
		want bool
	}{
		{
			name: "configuration error",
			err:  &ConfigurationError{Message: "test error"},
			want: true,
		},
		{
			name: "not configuration error",
			err:  os.ErrNotExist,
			want: false,
		},
		{
			name: "nil error",
			err:  nil,
			want: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := IsConfigurationError(tt.err)
			if got != tt.want {
				t.Errorf("IsConfigurationError() = %v, want %v", got, tt.want)
			}
		})
	}
}
