package config

import (
	"os"
	"path/filepath"
	"testing"
)

func TestDefaultConfigIncludesStorageControls(t *testing.T) {
	cfg := defaultConfig()

	if !cfg.Storage.CaptureRequestBody {
		t.Fatal("expected request body capture to be enabled by default")
	}
	if !cfg.Storage.CaptureResponseBody {
		t.Fatal("expected response body capture to be enabled by default")
	}
	if cfg.Storage.MetadataOnly {
		t.Fatal("expected metadata-only mode to be disabled by default")
	}
	if cfg.Storage.RetentionDays != 0 {
		t.Fatalf("expected retention to be disabled by default, got %d", cfg.Storage.RetentionDays)
	}
	if len(cfg.Storage.RedactedFields) == 0 {
		t.Fatal("expected default redacted field list to be populated")
	}
}

func TestLoadFromFileParsesStorageControls(t *testing.T) {
	tempDir := t.TempDir()
	configPath := filepath.Join(tempDir, "config.yaml")

	yaml := `
storage:
  db_path: /tmp/claude.db
  capture_request_body: false
  capture_response_body: false
  metadata_only: true
  retention_days: 7
  redacted_fields:
    - api_key
    - secret
`
	if err := os.WriteFile(configPath, []byte(yaml), 0o600); err != nil {
		t.Fatalf("WriteFile() error = %v", err)
	}

	cfg := defaultConfig()
	if err := cfg.loadFromFile(configPath); err != nil {
		t.Fatalf("loadFromFile() error = %v", err)
	}

	if cfg.Storage.DBPath != "/tmp/claude.db" {
		t.Fatalf("unexpected db path %q", cfg.Storage.DBPath)
	}
	if !cfg.Storage.MetadataOnly {
		t.Fatal("expected metadata-only mode to load from file")
	}
	if cfg.Storage.CaptureRequestBody {
		t.Fatal("expected request body capture to load as disabled")
	}
	if cfg.Storage.CaptureResponseBody {
		t.Fatal("expected response body capture to load as disabled")
	}
	if cfg.Storage.RetentionDays != 7 {
		t.Fatalf("unexpected retention days %d", cfg.Storage.RetentionDays)
	}
	if len(cfg.Storage.RedactedFields) != 2 {
		t.Fatalf("unexpected redacted field count %d", len(cfg.Storage.RedactedFields))
	}
}

func TestLoadFirstAvailableConfigReturnsParseError(t *testing.T) {
	tempDir := t.TempDir()
	configPath := filepath.Join(tempDir, "config.yaml")

	if err := os.WriteFile(configPath, []byte("server: ["), 0o600); err != nil {
		t.Fatalf("WriteFile() error = %v", err)
	}

	cfg := &Config{}
	if err := loadFirstAvailableConfig(cfg, []string{configPath}); err == nil {
		t.Fatal("expected parse error, got nil")
	}
}

func TestLoadFirstAvailableConfigSkipsMissingFiles(t *testing.T) {
	cfg := &Config{}
	if err := loadFirstAvailableConfig(cfg, []string{
		filepath.Join(t.TempDir(), "missing.yaml"),
	}); err != nil {
		t.Fatalf("expected nil error for missing config, got %v", err)
	}
}

func TestDefaultConfigUsesPublicBindAndWildcardCors(t *testing.T) {
	cfg := defaultConfig()

	if cfg.Server.Host != "0.0.0.0" {
		t.Fatalf("expected 0.0.0.0 host, got %q", cfg.Server.Host)
	}

	if cfg.Auth.Enabled {
		t.Fatal("expected auth to be disabled by default")
	}

	if len(cfg.CORS.AllowedOrigins) != 1 || cfg.CORS.AllowedOrigins[0] != "*" {
		t.Fatalf("expected default CORS origins to be [*], got %v", cfg.CORS.AllowedOrigins)
	}
}

func TestValidateSecurityRejectsPublicBindWithoutAuthOrTrustProxy(t *testing.T) {
	cfg := defaultConfig()
	cfg.Server.Host = "0.0.0.0"
	cfg.Auth.Enabled = false
	cfg.Auth.TrustProxy = false

	if err := validateSecurity(cfg); err == nil {
		t.Fatal("expected validation error for public bind without auth or trust_proxy")
	}
}

func TestValidateSecurityAllowsPublicBindWithAuthToken(t *testing.T) {
	cfg := defaultConfig()
	cfg.Server.Host = "0.0.0.0"
	cfg.Auth.Enabled = true
	cfg.Auth.Token = "secret"

	if err := validateSecurity(cfg); err != nil {
		t.Fatalf("expected public bind with auth token to be allowed, got %v", err)
	}
}

func TestValidateSecurityAllowsPublicBindWithTrustProxy(t *testing.T) {
	cfg := defaultConfig()
	cfg.Server.Host = "0.0.0.0"
	cfg.Auth.Enabled = false
	cfg.Auth.TrustProxy = true

	if err := validateSecurity(cfg); err != nil {
		t.Fatalf("expected public bind with trust_proxy to be allowed, got %v", err)
	}
}

func TestEnvBool(t *testing.T) {
	tests := []struct {
		name  string
		value string
		want  bool
	}{
		{"true lowercase", "true", true},
		{"true uppercase", "TRUE", true},
		{"true mixed case", "True", true},
		{"one", "1", true},
		{"yes lowercase", "yes", true},
		{"yes uppercase", "YES", true},
		{"false", "false", false},
		{"zero", "0", false},
		{"no", "no", false},
		{"empty", "", false},
		{"random string", "maybe", false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			key := "TEST_ENV_BOOL_" + tt.name
			if tt.value != "" {
				os.Setenv(key, tt.value)
				defer os.Unsetenv(key)
			} else {
				os.Unsetenv(key)
			}

			got := envBool(key)
			if got != tt.want {
				t.Errorf("envBool(%q) with value %q = %v, want %v", key, tt.value, got, tt.want)
			}
		})
	}
}

func TestCORSEnvOverride(t *testing.T) {
	tests := []struct {
		name    string
		envKey  string
		envVal  string
		check   func(*Config) bool
		desc    string
	}{
		{
			name:   "origins override",
			envKey: "CORS_ALLOWED_ORIGINS",
			envVal: "https://example.com, https://other.com",
			check: func(c *Config) bool {
				return len(c.CORS.AllowedOrigins) == 2 &&
					c.CORS.AllowedOrigins[0] == "https://example.com" &&
					c.CORS.AllowedOrigins[1] == "https://other.com"
			},
			desc: "should split and trim comma-separated origins",
		},
		{
			name:   "methods override",
			envKey: "CORS_ALLOWED_METHODS",
			envVal: "GET,POST",
			check: func(c *Config) bool {
				return len(c.CORS.AllowedMethods) == 2 &&
					c.CORS.AllowedMethods[0] == "GET" &&
					c.CORS.AllowedMethods[1] == "POST"
			},
			desc: "should split comma-separated methods",
		},
		{
			name:   "headers override",
			envKey: "CORS_ALLOWED_HEADERS",
			envVal: "Authorization",
			check: func(c *Config) bool {
				return len(c.CORS.AllowedHeaders) == 1 &&
					c.CORS.AllowedHeaders[0] == "Authorization"
			},
			desc: "should accept single header value",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Set the env var under test; clear all others to avoid cross-talk
			os.Setenv(tt.envKey, tt.envVal)
			defer os.Unsetenv(tt.envKey)

			// Also set TRUST_PROXY so validateSecurity doesn't reject default config
			os.Setenv("TRUST_PROXY", "true")
			defer os.Unsetenv("TRUST_PROXY")

			cfg, err := Load()
			if err != nil {
				t.Fatalf("Load() error = %v", err)
			}

			if !tt.check(cfg) {
				t.Fatalf("%s: check failed; origins=%v methods=%v headers=%v",
					tt.desc, cfg.CORS.AllowedOrigins, cfg.CORS.AllowedMethods, cfg.CORS.AllowedHeaders)
			}
		})
	}
}

func TestBoolEnvOverrides(t *testing.T) {
	tests := []struct {
		name   string
		envKey string
		envVal string
		check  func(*Config) bool
	}{
		{
			name:   "AUTH_ENABLED true",
			envKey: "AUTH_ENABLED",
			envVal: "true",
			check:  func(c *Config) bool { return c.Auth.Enabled },
		},
		{
			name:   "AUTH_ENABLED yes",
			envKey: "AUTH_ENABLED",
			envVal: "yes",
			check:  func(c *Config) bool { return c.Auth.Enabled },
		},
		{
			name:   "AUTH_ENABLED 1",
			envKey: "AUTH_ENABLED",
			envVal: "1",
			check:  func(c *Config) bool { return c.Auth.Enabled },
		},
		{
			name:   "TRUST_PROXY true",
			envKey: "TRUST_PROXY",
			envVal: "true",
			check:  func(c *Config) bool { return c.Auth.TrustProxy },
		},
		{
			name:   "STORAGE_METADATA_ONLY true",
			envKey: "STORAGE_METADATA_ONLY",
			envVal: "true",
			check:  func(c *Config) bool { return c.Storage.MetadataOnly },
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			os.Setenv(tt.envKey, tt.envVal)
			defer os.Unsetenv(tt.envKey)

			// Need TRUST_PROXY or AUTH_TOKEN for security validation when AUTH_ENABLED
			if tt.envKey == "AUTH_ENABLED" {
				os.Setenv("AUTH_TOKEN", "test-token")
				defer os.Unsetenv("AUTH_TOKEN")
			}
			if tt.envKey != "TRUST_PROXY" {
				os.Setenv("TRUST_PROXY", "true")
				defer os.Unsetenv("TRUST_PROXY")
			}

			cfg, err := Load()
			if err != nil {
				t.Fatalf("Load() error = %v", err)
			}

			if !tt.check(cfg) {
				t.Fatalf("expected %s=%s to set config field to true", tt.envKey, tt.envVal)
			}
		})
	}
}

func TestSplitAndTrim(t *testing.T) {
	tests := []struct {
		input string
		want  []string
	}{
		{"a,b,c", []string{"a", "b", "c"}},
		{" a , b , c ", []string{"a", "b", "c"}},
		{"single", []string{"single"}},
		{"a,,b", []string{"a", "b"}},
		{"", []string{}},
	}

	for _, tt := range tests {
		t.Run(tt.input, func(t *testing.T) {
			got := splitAndTrim(tt.input)
			if len(got) != len(tt.want) {
				t.Fatalf("splitAndTrim(%q) = %v, want %v", tt.input, got, tt.want)
			}
			for i := range got {
				if got[i] != tt.want[i] {
					t.Fatalf("splitAndTrim(%q)[%d] = %q, want %q", tt.input, i, got[i], tt.want[i])
				}
			}
		})
	}
}

func TestNoAnthropicConfigField(t *testing.T) {
	// Verify the top-level Anthropic field was removed from Config.
	// The canonical location is cfg.Providers.Anthropic.
	cfg := defaultConfig()
	if cfg.Providers.Anthropic.BaseURL == "" {
		t.Fatal("expected Providers.Anthropic.BaseURL to have a default value")
	}
}
