package logger

import (
	"testing"
)

func TestRedactEmail(t *testing.T) {
	redactor := NewSensitiveFieldRedactor()

	tests := []struct {
		name     string
		input    string
		expected string
	}{
		{
			name:     "normal email",
			input:    "john.doe@example.com",
			expected: "jo***@example.com",
		},
		{
			name:     "short local part",
			input:    "ab@example.com",
			expected: "**@example.com",
		},
		{
			name:     "single character local part",
			input:    "a@example.com",
			expected: "**@example.com",
		},
		{
			name:     "empty email",
			input:    "",
			expected: "[empty]",
		},
		{
			name:     "invalid email",
			input:    "notanemail",
			expected: "[invalid-email]",
		},
		{
			name:     "long email",
			input:    "very.long.email.address@subdomain.example.com",
			expected: "ve***@subdomain.example.com",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := redactor.RedactEmail(tt.input)
			if result != tt.expected {
				t.Errorf("RedactEmail(%q) = %q, want %q", tt.input, result, tt.expected)
			}
		})
	}
}

func TestHashForLogging(t *testing.T) {
	redactor := NewSensitiveFieldRedactor()

	tests := []struct {
		name  string
		input string
	}{
		{
			name:  "email",
			input: "john.doe@example.com",
		},
		{
			name:  "tenant slug",
			input: "my-company",
		},
		{
			name:  "another email",
			input: "jane.smith@test.com",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			hash1 := redactor.HashForLogging(tt.input)
			hash2 := redactor.HashForLogging(tt.input)

			// Hash should be consistent
			if hash1 != hash2 {
				t.Errorf("HashForLogging is not consistent: %q != %q", hash1, hash2)
			}

			// Hash should be 16 characters (8 bytes in hex)
			if len(hash1) != 16 {
				t.Errorf("HashForLogging length = %d, want 16", len(hash1))
			}

			// Hash should not contain original value
			if hash1 == tt.input {
				t.Errorf("HashForLogging returned original value")
			}
		})
	}

	// Different inputs should produce different hashes
	hash1 := redactor.HashForLogging("john.doe@example.com")
	hash2 := redactor.HashForLogging("jane.smith@example.com")
	if hash1 == hash2 {
		t.Error("Different inputs produced same hash")
	}

	// Empty string
	emptyHash := redactor.HashForLogging("")
	if emptyHash != "[empty]" {
		t.Errorf("HashForLogging(\"\") = %q, want [empty]", emptyHash)
	}
}

func TestRedactTenantSlug(t *testing.T) {
	redactor := NewSensitiveFieldRedactor()

	tests := []struct {
		name     string
		input    string
		expected string
	}{
		{
			name:     "normal slug",
			input:    "my-company",
			expected: "my***",
		},
		{
			name:     "short slug",
			input:    "abc",
			expected: "***",
		},
		{
			name:     "very short slug",
			input:    "ab",
			expected: "***",
		},
		{
			name:     "empty slug",
			input:    "",
			expected: "[empty]",
		},
		{
			name:     "long slug",
			input:    "very-long-company-name",
			expected: "ve***",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := redactor.RedactTenantSlug(tt.input)
			if result != tt.expected {
				t.Errorf("RedactTenantSlug(%q) = %q, want %q", tt.input, result, tt.expected)
			}
		})
	}
}

func TestRedactAPIKey(t *testing.T) {
	redactor := NewSensitiveFieldRedactor()

	tests := []struct {
		name     string
		input    string
		expected string
	}{
		{
			name:     "live API key",
			input:    "live_sk_abc123def456ghi789",
			expected: "live_sk_***i789",
		},
		{
			name:     "test API key",
			input:    "test_sk_xyz789uvw456rst123",
			expected: "test_sk_***t123",
		},
		{
			name:     "short live key",
			input:    "live_sk_abc",
			expected: "live_sk_***",
		},
		{
			name:     "other format",
			input:    "sk_abc123def456",
			expected: "***f456",
		},
		{
			name:     "very short key",
			input:    "abc",
			expected: "***",
		},
		{
			name:     "empty key",
			input:    "",
			expected: "[empty]",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := redactor.RedactAPIKey(tt.input)
			if result != tt.expected {
				t.Errorf("RedactAPIKey(%q) = %q, want %q", tt.input, result, tt.expected)
			}
		})
	}
}

func TestRedactJWTToken(t *testing.T) {
	redactor := NewSensitiveFieldRedactor()

	tests := []struct {
		name     string
		input    string
		expected string
	}{
		{
			name:     "normal JWT",
			input:    "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U",
			expected: "eyJhbGci...P0THsR8U",
		},
		{
			name:     "short token",
			input:    "short",
			expected: "***",
		},
		{
			name:     "empty token",
			input:    "",
			expected: "[empty]",
		},
		{
			name:     "minimum length token",
			input:    "1234567890123456",
			expected: "12345678...90123456",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := redactor.RedactJWTToken(tt.input)
			if result != tt.expected {
				t.Errorf("RedactJWTToken(%q) = %q, want %q", tt.input, result, tt.expected)
			}
		})
	}
}

func TestRedactIPAddress(t *testing.T) {
	redactor := NewSensitiveFieldRedactor()

	tests := []struct {
		name     string
		input    string
		expected string
	}{
		{
			name:     "IPv4 address",
			input:    "192.168.1.100",
			expected: "192.168.*.*",
		},
		{
			name:     "IPv4 public",
			input:    "8.8.8.8",
			expected: "8.8.*.*",
		},
		{
			name:     "IPv6 address",
			input:    "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
			expected: "2001:0db8:85a3:0000:****",
		},
		{
			name:     "IPv6 shortened",
			input:    "2001:db8::1",
			expected: "2001:db8::1:****",
		},
		{
			name:     "empty IP",
			input:    "",
			expected: "[empty]",
		},
		{
			name:     "invalid IP",
			input:    "notanip",
			expected: "***",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := redactor.RedactIPAddress(tt.input)
			if result != tt.expected {
				t.Errorf("RedactIPAddress(%q) = %q, want %q", tt.input, result, tt.expected)
			}
		})
	}
}

func TestUserIdentifier(t *testing.T) {
	userID := "user_123"
	email := "john.doe@example.com"

	fields := UserIdentifier(userID, email)

	if len(fields) != 3 {
		t.Errorf("UserIdentifier returned %d fields, want 3", len(fields))
	}

	// Check that fields contain expected keys
	fieldKeys := make(map[string]bool)
	for _, field := range fields {
		fieldKeys[field.Key] = true
	}

	expectedKeys := []string{"user_id", "email_hash", "email_redacted"}
	for _, key := range expectedKeys {
		if !fieldKeys[key] {
			t.Errorf("UserIdentifier missing key: %s", key)
		}
	}
}

func TestTenantIdentifier(t *testing.T) {
	tenantID := "tenant_123"
	slug := "my-company"

	fields := TenantIdentifier(tenantID, slug)

	if len(fields) != 3 {
		t.Errorf("TenantIdentifier returned %d fields, want 3", len(fields))
	}

	// Check that fields contain expected keys
	fieldKeys := make(map[string]bool)
	for _, field := range fields {
		fieldKeys[field.Key] = true
	}

	expectedKeys := []string{"tenant_id", "tenant_slug_hash", "tenant_slug_redacted"}
	for _, key := range expectedKeys {
		if !fieldKeys[key] {
			t.Errorf("TenantIdentifier missing key: %s", key)
		}
	}
}
