package websocket

import (
	"math"
	"net/http"
	"testing"
	"time"

	"github.com/rcourtman/pulse-go-rewrite/internal/alerts"
)

func TestIsValidPrivateOrigin(t *testing.T) {
	tests := []struct {
		name     string
		host     string
		expected bool
	}{
		// Localhost variations
		{"localhost", "localhost", true},
		{"ipv4 loopback", "127.0.0.1", true},
		{"ipv6 loopback", "::1", true},

		// Private IPv4 ranges
		{"10.x.x.x private", "10.0.0.1", true},
		{"10.x.x.x edge", "10.255.255.255", true},
		{"172.16.x.x private", "172.16.0.1", true},
		{"172.31.x.x private", "172.31.255.255", true},
		{"192.168.x.x private", "192.168.1.1", true},
		{"192.168.x.x edge", "192.168.255.255", true},

		// Local domain suffixes
		{"hostname.local", "myhost.local", true},
		{"hostname.lan", "myhost.lan", true},
		{"subdomain.hostname.local", "sub.myhost.local", true},
		{"too many subdomains .local", "a.b.c.d.local", false},

		// Public IPs (should reject)
		{"public IP 8.8.8.8", "8.8.8.8", false},
		{"public IP 1.1.1.1", "1.1.1.1", false},
		{"public IP 203.0.113.1", "203.0.113.1", false},

		// Public domains (should reject)
		{"example.com", "example.com", false},
		{"google.com", "google.com", false},
		{"malicious.attacker.com", "malicious.attacker.com", false},

		// Edge cases
		{"empty string", "", false},
		{"just dot", ".", false},
		{"numbers only", "12345", false},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			result := isValidPrivateOrigin(tc.host)
			if result != tc.expected {
				t.Errorf("isValidPrivateOrigin(%q) = %v, want %v", tc.host, result, tc.expected)
			}
		})
	}
}

func TestNormalizeForwardedProto(t *testing.T) {
	tests := []struct {
		name     string
		proto    string
		fallback string
		expected string
	}{
		// Empty proto returns fallback
		{"empty proto returns fallback", "", "http", "http"},
		{"empty proto returns https fallback", "", "https", "https"},

		// Standard HTTP schemes
		{"http passthrough", "http", "https", "http"},
		{"https passthrough", "https", "http", "https"},
		{"HTTP uppercase", "HTTP", "http", "http"},
		{"HTTPS uppercase", "HTTPS", "http", "https"},

		// WebSocket schemes normalized to HTTP
		{"ws becomes http", "ws", "https", "http"},
		{"wss becomes https", "wss", "http", "https"},
		{"WS uppercase", "WS", "https", "http"},
		{"WSS uppercase", "WSS", "http", "https"},

		// Comma-separated chains (take first)
		{"chain wss,https", "wss,https", "http", "https"},
		{"chain https,wss", "https,wss", "http", "https"},
		{"chain http,wss,https", "http,wss,https", "https", "http"},

		// Whitespace handling
		{"whitespace trimmed", "  https  ", "http", "https"},
		{"whitespace in chain", "  wss , https  ", "http", "https"},

		// Unknown protos pass through
		{"unknown proto", "ftp", "http", "ftp"},
		{"unknown empty after trim", "   ", "http", "http"},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			result := normalizeForwardedProto(tc.proto, tc.fallback)
			if result != tc.expected {
				t.Errorf("normalizeForwardedProto(%q, %q) = %q, want %q", tc.proto, tc.fallback, result, tc.expected)
			}
		})
	}
}

func TestSanitizeValue(t *testing.T) {
	tests := []struct {
		name     string
		input    interface{}
		expected interface{}
	}{
		// Normal values pass through
		{"normal float64", float64(42.5), float64(42.5)},
		{"zero float64", float64(0), float64(0)},
		{"negative float64", float64(-100.5), float64(-100.5)},
		{"string", "hello", "hello"},
		{"int via float64", float64(100), float64(100)},
		{"bool true", true, true},
		{"bool false", false, false},
		{"nil", nil, nil},

		// NaN becomes nil
		{"NaN float64", math.NaN(), nil},

		// Inf becomes nil
		{"positive Inf", math.Inf(1), nil},
		{"negative Inf", math.Inf(-1), nil},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			result := sanitizeValue(tc.input)

			// Special handling for NaN comparison
			if tc.expected == nil {
				if result != nil {
					t.Errorf("sanitizeValue() = %v, want nil", result)
				}
				return
			}

			if result != tc.expected {
				t.Errorf("sanitizeValue() = %v, want %v", result, tc.expected)
			}
		})
	}
}

func TestSanitizeValue_Map(t *testing.T) {
	input := map[string]interface{}{
		"normal":   float64(42.5),
		"nan":      math.NaN(),
		"inf":      math.Inf(1),
		"negInf":   math.Inf(-1),
		"string":   "test",
		"bool":     true,
		"nilValue": nil,
	}

	result := sanitizeValue(input)
	resultMap, ok := result.(map[string]interface{})
	if !ok {
		t.Fatalf("expected map[string]interface{}, got %T", result)
	}

	// Check normal value preserved
	if resultMap["normal"] != float64(42.5) {
		t.Errorf("normal value = %v, want 42.5", resultMap["normal"])
	}

	// Check NaN/Inf became nil
	if resultMap["nan"] != nil {
		t.Errorf("nan value = %v, want nil", resultMap["nan"])
	}
	if resultMap["inf"] != nil {
		t.Errorf("inf value = %v, want nil", resultMap["inf"])
	}
	if resultMap["negInf"] != nil {
		t.Errorf("negInf value = %v, want nil", resultMap["negInf"])
	}

	// Check other types preserved
	if resultMap["string"] != "test" {
		t.Errorf("string value = %v, want 'test'", resultMap["string"])
	}
	if resultMap["bool"] != true {
		t.Errorf("bool value = %v, want true", resultMap["bool"])
	}
}

func TestSanitizeValue_Slice(t *testing.T) {
	input := []interface{}{
		float64(1.0),
		math.NaN(),
		float64(2.0),
		math.Inf(1),
		"string",
	}

	result := sanitizeValue(input)
	resultSlice, ok := result.([]interface{})
	if !ok {
		t.Fatalf("expected []interface{}, got %T", result)
	}

	if len(resultSlice) != 5 {
		t.Fatalf("slice length = %d, want 5", len(resultSlice))
	}

	if resultSlice[0] != float64(1.0) {
		t.Errorf("resultSlice[0] = %v, want 1.0", resultSlice[0])
	}
	if resultSlice[1] != nil {
		t.Errorf("resultSlice[1] (NaN) = %v, want nil", resultSlice[1])
	}
	if resultSlice[2] != float64(2.0) {
		t.Errorf("resultSlice[2] = %v, want 2.0", resultSlice[2])
	}
	if resultSlice[3] != nil {
		t.Errorf("resultSlice[3] (Inf) = %v, want nil", resultSlice[3])
	}
	if resultSlice[4] != "string" {
		t.Errorf("resultSlice[4] = %v, want 'string'", resultSlice[4])
	}
}

func TestSanitizeValue_NestedMap(t *testing.T) {
	input := map[string]interface{}{
		"outer": map[string]interface{}{
			"inner": map[string]interface{}{
				"nan": math.NaN(),
				"ok":  float64(42),
			},
		},
	}

	result := sanitizeValue(input)
	resultMap := result.(map[string]interface{})
	outer := resultMap["outer"].(map[string]interface{})
	inner := outer["inner"].(map[string]interface{})

	if inner["nan"] != nil {
		t.Errorf("nested nan = %v, want nil", inner["nan"])
	}
	if inner["ok"] != float64(42) {
		t.Errorf("nested ok = %v, want 42", inner["ok"])
	}
}

func TestCloneMetadata(t *testing.T) {
	t.Run("nil input returns nil", func(t *testing.T) {
		result := cloneMetadata(nil)
		if result != nil {
			t.Errorf("cloneMetadata(nil) = %v, want nil", result)
		}
	})

	t.Run("empty map returns empty map", func(t *testing.T) {
		input := map[string]interface{}{}
		result := cloneMetadata(input)
		if result == nil {
			t.Fatal("cloneMetadata(empty) returned nil")
		}
		if len(result) != 0 {
			t.Errorf("cloneMetadata(empty) has length %d, want 0", len(result))
		}
	})

	t.Run("clones simple values", func(t *testing.T) {
		input := map[string]interface{}{
			"string": "hello",
			"int":    42,
			"float":  3.14,
			"bool":   true,
		}
		result := cloneMetadata(input)

		// Verify values copied
		if result["string"] != "hello" {
			t.Errorf("string = %v, want 'hello'", result["string"])
		}
		if result["int"] != 42 {
			t.Errorf("int = %v, want 42", result["int"])
		}
		if result["float"] != 3.14 {
			t.Errorf("float = %v, want 3.14", result["float"])
		}
		if result["bool"] != true {
			t.Errorf("bool = %v, want true", result["bool"])
		}

		// Verify it's a different map
		input["string"] = "modified"
		if result["string"] == "modified" {
			t.Error("cloned map should not be affected by original modifications")
		}
	})

	t.Run("deep clones nested maps", func(t *testing.T) {
		nested := map[string]interface{}{"key": "value"}
		input := map[string]interface{}{
			"nested": nested,
		}
		result := cloneMetadata(input)

		// Modify original nested map
		nested["key"] = "modified"

		// Cloned nested map should be unaffected
		resultNested := result["nested"].(map[string]interface{})
		if resultNested["key"] != "value" {
			t.Errorf("nested key = %v, want 'value' (should not be affected by original)", resultNested["key"])
		}
	})

	t.Run("deep clones slices", func(t *testing.T) {
		slice := []string{"a", "b", "c"}
		input := map[string]interface{}{
			"slice": slice,
		}
		result := cloneMetadata(input)

		// Modify original slice
		slice[0] = "modified"

		// Cloned slice should be unaffected
		resultSlice := result["slice"].([]string)
		if resultSlice[0] != "a" {
			t.Errorf("slice[0] = %v, want 'a' (should not be affected by original)", resultSlice[0])
		}
	})
}

func TestCloneMetadataValue(t *testing.T) {
	t.Run("clones map[string]interface{}", func(t *testing.T) {
		input := map[string]interface{}{"key": "value"}
		result := cloneMetadataValue(input)
		resultMap := result.(map[string]interface{})

		input["key"] = "modified"
		if resultMap["key"] != "value" {
			t.Error("cloned map should not be affected by original")
		}
	})

	t.Run("clones map[string]string", func(t *testing.T) {
		input := map[string]string{"key": "value"}
		result := cloneMetadataValue(input)

		// map[string]string gets converted to map[string]interface{}
		resultMap := result.(map[string]interface{})
		if resultMap["key"] != "value" {
			t.Errorf("key = %v, want 'value'", resultMap["key"])
		}
	})

	t.Run("clones []interface{}", func(t *testing.T) {
		input := []interface{}{"a", "b", "c"}
		result := cloneMetadataValue(input)
		resultSlice := result.([]interface{})

		input[0] = "modified"
		if resultSlice[0] != "a" {
			t.Error("cloned slice should not be affected by original")
		}
	})

	t.Run("clones []string", func(t *testing.T) {
		input := []string{"a", "b", "c"}
		result := cloneMetadataValue(input)
		resultSlice := result.([]string)

		input[0] = "modified"
		if resultSlice[0] != "a" {
			t.Error("cloned string slice should not be affected by original")
		}
	})

	t.Run("clones []int", func(t *testing.T) {
		input := []int{1, 2, 3}
		result := cloneMetadataValue(input)
		resultSlice := result.([]int)

		input[0] = 999
		if resultSlice[0] != 1 {
			t.Error("cloned int slice should not be affected by original")
		}
	})

	t.Run("clones []float64", func(t *testing.T) {
		input := []float64{1.1, 2.2, 3.3}
		result := cloneMetadataValue(input)
		resultSlice := result.([]float64)

		input[0] = 999.9
		if resultSlice[0] != 1.1 {
			t.Error("cloned float64 slice should not be affected by original")
		}
	})

	t.Run("primitives returned as-is", func(t *testing.T) {
		// Primitives are immutable, so returning as-is is fine
		if cloneMetadataValue("string") != "string" {
			t.Error("string should pass through")
		}
		if cloneMetadataValue(42) != 42 {
			t.Error("int should pass through")
		}
		if cloneMetadataValue(3.14) != 3.14 {
			t.Error("float should pass through")
		}
		if cloneMetadataValue(true) != true {
			t.Error("bool should pass through")
		}
		if cloneMetadataValue(nil) != nil {
			t.Error("nil should pass through")
		}
	})
}

func TestCloneAlert(t *testing.T) {
	t.Run("nil alert returns empty alert", func(t *testing.T) {
		result := cloneAlert(nil)
		if result.ID != "" {
			t.Error("cloneAlert(nil) should return empty alert")
		}
	})

	t.Run("clones basic fields", func(t *testing.T) {
		now := time.Now()
		original := &alerts.Alert{
			ID:         "alert-123",
			Type:       "cpu",
			Level:      alerts.AlertLevelWarning,
			ResourceID: "vm/100",
			Message:    "CPU high",
			Value:      85.5,
			StartTime:  now,
		}

		result := cloneAlert(original)

		if result.ID != "alert-123" {
			t.Errorf("ID = %v, want alert-123", result.ID)
		}
		if result.Type != "cpu" {
			t.Errorf("Type = %v, want cpu", result.Type)
		}
		if result.Level != alerts.AlertLevelWarning {
			t.Errorf("Level = %v, want AlertLevelWarning", result.Level)
		}
		if result.ResourceID != "vm/100" {
			t.Errorf("ResourceID = %v, want vm/100", result.ResourceID)
		}
		if result.Message != "CPU high" {
			t.Errorf("Message = %v, want 'CPU high'", result.Message)
		}
		if result.Value != 85.5 {
			t.Errorf("Value = %v, want 85.5", result.Value)
		}
	})

	t.Run("deep clones AckTime", func(t *testing.T) {
		ackTime := time.Now()
		original := &alerts.Alert{
			ID:      "alert-123",
			AckTime: &ackTime,
		}

		result := cloneAlert(original)

		// Verify AckTime is cloned
		if result.AckTime == nil {
			t.Fatal("AckTime should not be nil")
		}
		if result.AckTime == original.AckTime {
			t.Error("AckTime should be a different pointer")
		}
		if !result.AckTime.Equal(*original.AckTime) {
			t.Error("AckTime values should be equal")
		}
	})

	t.Run("deep clones EscalationTimes", func(t *testing.T) {
		original := &alerts.Alert{
			ID:              "alert-123",
			EscalationTimes: []time.Time{time.Now(), time.Now().Add(time.Hour)},
		}

		result := cloneAlert(original)

		if len(result.EscalationTimes) != 2 {
			t.Fatalf("EscalationTimes length = %d, want 2", len(result.EscalationTimes))
		}

		// Modify original
		original.EscalationTimes[0] = time.Now().Add(24 * time.Hour)

		// Clone should be unaffected
		if result.EscalationTimes[0].Equal(original.EscalationTimes[0]) {
			t.Error("cloned EscalationTimes should not be affected by original modifications")
		}
	})

	t.Run("deep clones Metadata", func(t *testing.T) {
		original := &alerts.Alert{
			ID: "alert-123",
			Metadata: map[string]interface{}{
				"key": "value",
				"nested": map[string]interface{}{
					"inner": "data",
				},
			},
		}

		result := cloneAlert(original)

		// Modify original metadata
		original.Metadata["key"] = "modified"

		// Clone should be unaffected
		if result.Metadata["key"] != "value" {
			t.Error("cloned Metadata should not be affected by original modifications")
		}
	})
}

func TestCloneAlertData(t *testing.T) {
	t.Run("handles *alerts.Alert", func(t *testing.T) {
		original := &alerts.Alert{ID: "alert-123", Message: "test"}
		result := cloneAlertData(original)

		cloned, ok := result.(alerts.Alert)
		if !ok {
			t.Fatalf("expected alerts.Alert, got %T", result)
		}
		if cloned.ID != "alert-123" {
			t.Errorf("ID = %v, want alert-123", cloned.ID)
		}
	})

	t.Run("handles alerts.Alert value", func(t *testing.T) {
		original := alerts.Alert{ID: "alert-456", Message: "test"}
		result := cloneAlertData(original)

		cloned, ok := result.(alerts.Alert)
		if !ok {
			t.Fatalf("expected alerts.Alert, got %T", result)
		}
		if cloned.ID != "alert-456" {
			t.Errorf("ID = %v, want alert-456", cloned.ID)
		}
	})

	t.Run("returns other types unchanged", func(t *testing.T) {
		// Strings, maps, etc. that aren't alerts should pass through
		result := cloneAlertData("not an alert")
		if result != "not an alert" {
			t.Errorf("non-alert data should pass through unchanged")
		}
	})
}

func TestNewHub(t *testing.T) {
	stateGetter := func() interface{} {
		return map[string]string{"status": "ok"}
	}

	hub := NewHub(stateGetter)

	if hub == nil {
		t.Fatal("NewHub returned nil")
	}

	if hub.clients == nil {
		t.Error("clients map should be initialized")
	}
	if hub.broadcast == nil {
		t.Error("broadcast channel should be initialized")
	}
	if hub.broadcastSeq == nil {
		t.Error("broadcastSeq channel should be initialized")
	}
	if hub.register == nil {
		t.Error("register channel should be initialized")
	}
	if hub.unregister == nil {
		t.Error("unregister channel should be initialized")
	}
	if hub.stopChan == nil {
		t.Error("stopChan should be initialized")
	}
	if hub.getState == nil {
		t.Error("getState should be set")
	}
	if len(hub.allowedOrigins) != 0 {
		t.Error("allowedOrigins should be empty by default")
	}
	if hub.coalesceWindow != 100*time.Millisecond {
		t.Errorf("coalesceWindow = %v, want 100ms", hub.coalesceWindow)
	}
}

func TestHub_SetAllowedOrigins(t *testing.T) {
	hub := NewHub(nil)

	origins := []string{"http://localhost:3000", "https://example.com"}
	hub.SetAllowedOrigins(origins)

	hub.mu.RLock()
	defer hub.mu.RUnlock()

	if len(hub.allowedOrigins) != 2 {
		t.Errorf("allowedOrigins length = %d, want 2", len(hub.allowedOrigins))
	}
	if hub.allowedOrigins[0] != "http://localhost:3000" {
		t.Errorf("allowedOrigins[0] = %v, want http://localhost:3000", hub.allowedOrigins[0])
	}
}

func TestHub_SetStateGetter(t *testing.T) {
	hub := NewHub(nil)

	if hub.getState != nil {
		t.Error("getState should be nil initially when passed nil")
	}

	newGetter := func() interface{} {
		return "new state"
	}
	hub.SetStateGetter(newGetter)

	hub.mu.RLock()
	defer hub.mu.RUnlock()

	if hub.getState == nil {
		t.Error("getState should be set after SetStateGetter")
	}
}

func TestHub_GetClientCount(t *testing.T) {
	hub := NewHub(nil)

	if hub.GetClientCount() != 0 {
		t.Error("client count should be 0 initially")
	}

	// Manually add clients (bypassing register channel for unit test)
	hub.mu.Lock()
	hub.clients[&Client{id: "client-1"}] = true
	hub.clients[&Client{id: "client-2"}] = true
	hub.mu.Unlock()

	if hub.GetClientCount() != 2 {
		t.Errorf("client count = %d, want 2", hub.GetClientCount())
	}
}

func TestMessage_Fields(t *testing.T) {
	msg := Message{
		Type:      "test",
		Data:      map[string]string{"key": "value"},
		Timestamp: "2024-01-01T00:00:00Z",
	}

	if msg.Type != "test" {
		t.Errorf("Type = %v, want test", msg.Type)
	}
	if msg.Timestamp != "2024-01-01T00:00:00Z" {
		t.Errorf("Timestamp = %v, want 2024-01-01T00:00:00Z", msg.Timestamp)
	}
}

func TestHub_CheckOrigin(t *testing.T) {
	tests := []struct {
		name           string
		origin         string
		host           string
		allowedOrigins []string
		forwardedProto string
		forwardedHost  string
		expected       bool
	}{
		// No origin header - always allowed for non-browser clients
		{
			name:     "no origin header",
			origin:   "",
			host:     "localhost:8080",
			expected: true,
		},

		// Same-origin requests
		{
			name:     "same origin http",
			origin:   "http://localhost:8080",
			host:     "localhost:8080",
			expected: true,
		},
		{
			name:           "same origin with forwarded proto https",
			origin:         "https://example.com",
			host:           "example.com",
			forwardedProto: "https",
			expected:       true,
		},
		{
			name:          "same origin with forwarded host",
			origin:        "http://proxy.example.com",
			host:          "backend:8080",
			forwardedHost: "proxy.example.com",
			expected:      true,
		},

		// Wildcard allowed origins
		{
			name:           "wildcard allows any origin",
			origin:         "https://evil.com",
			host:           "localhost:8080",
			allowedOrigins: []string{"*"},
			expected:       true,
		},

		// Explicit allowed origins
		{
			name:           "explicit allowed origin matches",
			origin:         "https://app.example.com",
			host:           "localhost:8080",
			allowedOrigins: []string{"https://app.example.com"},
			expected:       true,
		},
		{
			name:           "explicit allowed origin no match",
			origin:         "https://other.example.com",
			host:           "localhost:8080",
			allowedOrigins: []string{"https://app.example.com"},
			expected:       false,
		},
		{
			name:           "multiple allowed origins - match second",
			origin:         "https://second.example.com",
			host:           "localhost:8080",
			allowedOrigins: []string{"https://first.example.com", "https://second.example.com"},
			expected:       true,
		},

		// Private network fallback (no allowed origins configured)
		{
			name:     "private IP 192.168.x.x allowed when no origins configured",
			origin:   "http://192.168.1.100:3000",
			host:     "localhost:8080",
			expected: true,
		},
		{
			name:     "private IP 10.x.x.x allowed when no origins configured",
			origin:   "http://10.0.0.50:3000",
			host:     "localhost:8080",
			expected: true,
		},
		{
			name:     "localhost allowed when no origins configured",
			origin:   "http://localhost:3000",
			host:     "localhost:8080",
			expected: true,
		},
		{
			name:     "127.0.0.1 allowed when no origins configured",
			origin:   "http://127.0.0.1:3000",
			host:     "localhost:8080",
			expected: true,
		},
		{
			name:     ".local domain allowed when no origins configured",
			origin:   "http://myserver.local:3000",
			host:     "localhost:8080",
			expected: true,
		},
		{
			name:     ".lan domain allowed when no origins configured",
			origin:   "http://myserver.lan:3000",
			host:     "localhost:8080",
			expected: true,
		},
		{
			name:     "public IP rejected when no origins configured",
			origin:   "http://8.8.8.8:3000",
			host:     "localhost:8080",
			expected: false,
		},
		{
			name:     "public domain rejected when no origins configured",
			origin:   "https://evil.example.com",
			host:     "localhost:8080",
			expected: false,
		},

		// HTTPS origin stripping
		{
			name:     "https origin with private IP",
			origin:   "https://192.168.1.50:443",
			host:     "localhost:8080",
			expected: true,
		},

		// Forwarded proto normalization
		{
			name:           "wss forwarded proto normalized to https",
			origin:         "https://example.com",
			host:           "example.com",
			forwardedProto: "wss",
			expected:       true,
		},
		{
			name:           "ws forwarded proto normalized to http",
			origin:         "http://example.com",
			host:           "example.com",
			forwardedProto: "ws",
			expected:       true,
		},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			hub := NewHub(nil)
			if len(tc.allowedOrigins) > 0 {
				hub.SetAllowedOrigins(tc.allowedOrigins)
			}

			req := &http.Request{
				Host:   tc.host,
				Header: make(http.Header),
			}
			if tc.origin != "" {
				req.Header.Set("Origin", tc.origin)
			}
			if tc.forwardedProto != "" {
				req.Header.Set("X-Forwarded-Proto", tc.forwardedProto)
			}
			if tc.forwardedHost != "" {
				req.Header.Set("X-Forwarded-Host", tc.forwardedHost)
			}

			result := hub.checkOrigin(req)
			if result != tc.expected {
				t.Errorf("checkOrigin() = %v, want %v", result, tc.expected)
			}
		})
	}
}

func TestHub_CheckOrigin_XForwardedScheme(t *testing.T) {
	hub := NewHub(nil)

	req := &http.Request{
		Host:   "example.com",
		Header: make(http.Header),
	}
	req.Header.Set("Origin", "https://example.com")
	req.Header.Set("X-Forwarded-Scheme", "https")

	result := hub.checkOrigin(req)
	if !result {
		t.Error("checkOrigin should allow same-origin with X-Forwarded-Scheme")
	}
}
