package jdenticon

import (
	"errors"
	"strings"
	"testing"
)

// TestIsValidHexColor tests the hex color validation helper function.
func TestIsValidHexColor(t *testing.T) {
	tests := []struct {
		name     string
		color    string
		expected bool
	}{
		// Valid cases
		{
			name:     "empty string (transparent)",
			color:    "",
			expected: true,
		},
		{
			name:     "valid 3-digit lowercase",
			color:    "#fff",
			expected: true,
		},
		{
			name:     "valid 3-digit uppercase",
			color:    "#FFF",
			expected: true,
		},
		{
			name:     "valid 3-digit mixed case",
			color:    "#Fa3",
			expected: true,
		},
		{
			name:     "valid 6-digit lowercase",
			color:    "#ffffff",
			expected: true,
		},
		{
			name:     "valid 6-digit uppercase",
			color:    "#FFFFFF",
			expected: true,
		},
		{
			name:     "valid 6-digit mixed case",
			color:    "#Ff00Aa",
			expected: true,
		},
		{
			name:     "valid with numbers",
			color:    "#123456",
			expected: true,
		},
		{
			name:     "valid 3-digit with numbers",
			color:    "#123",
			expected: true,
		},

		// Invalid cases
		{
			name:     "missing hash prefix",
			color:    "ffffff",
			expected: false,
		},
		{
			name:     "too short",
			color:    "#ff",
			expected: false,
		},
		{
			name:     "too long",
			color:    "#fffffff",
			expected: false,
		},
		{
			name:     "invalid hex characters",
			color:    "#gggggg",
			expected: false,
		},
		{
			name:     "invalid hex characters in 3-digit",
			color:    "#ggg",
			expected: false,
		},
		{
			name:     "just hash",
			color:    "#",
			expected: false,
		},
		{
			name:     "double hash",
			color:    "##ffffff",
			expected: false,
		},
		{
			name:     "color name",
			color:    "red",
			expected: false,
		},
		{
			name:     "color name with hash",
			color:    "#red",
			expected: false,
		},
		{
			name:     "4-digit hex",
			color:    "#1234",
			expected: false,
		},
		{
			name:     "5-digit hex",
			color:    "#12345",
			expected: false,
		},
		{
			name:     "7-digit hex",
			color:    "#1234567",
			expected: false,
		},
		{
			name:     "8-digit hex (RGBA)",
			color:    "#12345678",
			expected: false,
		},
		{
			name:     "with spaces",
			color:    "# ffffff",
			expected: false,
		},
		{
			name:     "with special characters",
			color:    "#ffffff!",
			expected: false,
		},
	}

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

// TestWithBackgroundColor tests the WithBackgroundColor config option.
func TestWithBackgroundColor(t *testing.T) {
	tests := []struct {
		name          string
		color         string
		expectError   bool
		expectedError string
	}{
		// Valid cases
		{
			name:        "empty string (transparent)",
			color:       "",
			expectError: false,
		},
		{
			name:        "valid 3-digit hex",
			color:       "#fff",
			expectError: false,
		},
		{
			name:        "valid 6-digit hex",
			color:       "#ffffff",
			expectError: false,
		},
		{
			name:        "valid mixed case",
			color:       "#FfAa00",
			expectError: false,
		},
		{
			name:        "valid with numbers",
			color:       "#123456",
			expectError: false,
		},

		// Invalid cases
		{
			name:          "missing hash",
			color:         "ffffff",
			expectError:   true,
			expectedError: "must be a valid hex color in #RGB or #RRGGBB format",
		},
		{
			name:          "invalid hex characters",
			color:         "#gggggg",
			expectError:   true,
			expectedError: "must be a valid hex color in #RGB or #RRGGBB format",
		},
		{
			name:          "too short",
			color:         "#ff",
			expectError:   true,
			expectedError: "must be a valid hex color in #RGB or #RRGGBB format",
		},
		{
			name:          "too long",
			color:         "#fffffff",
			expectError:   true,
			expectedError: "must be a valid hex color in #RGB or #RRGGBB format",
		},
		{
			name:          "color name",
			color:         "red",
			expectError:   true,
			expectedError: "must be a valid hex color in #RGB or #RRGGBB format",
		},
		{
			name:          "4-digit hex",
			color:         "#1234",
			expectError:   true,
			expectedError: "must be a valid hex color in #RGB or #RRGGBB format",
		},
		{
			name:          "8-digit hex (RGBA)",
			color:         "#12345678",
			expectError:   true,
			expectedError: "must be a valid hex color in #RGB or #RRGGBB format",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			option := WithBackgroundColor(tt.color)
			config := DefaultConfig()
			err := option(&config)

			if tt.expectError {
				if err == nil {
					t.Errorf("WithBackgroundColor(%q) expected error but got none", tt.color)
					return
				}

				// Check that it's the right error type
				var invalidInput *ErrInvalidInput
				if !errors.As(err, &invalidInput) {
					t.Errorf("WithBackgroundColor(%q) error type = %T, expected *ErrInvalidInput", tt.color, err)
					return
				}

				// Check error message contains expected text
				if tt.expectedError != "" {
					if !strings.Contains(err.Error(), tt.expectedError) {
						t.Errorf("WithBackgroundColor(%q) error = %q, expected to contain %q", tt.color, err.Error(), tt.expectedError)
					}
				}

				// Check field name
				if invalidInput.Field != "background_color" {
					t.Errorf("WithBackgroundColor(%q) error field = %q, expected %q", tt.color, invalidInput.Field, "background_color")
				}

				// Check value is captured
				if invalidInput.Value != tt.color {
					t.Errorf("WithBackgroundColor(%q) error value = %q, expected %q", tt.color, invalidInput.Value, tt.color)
				}
			} else {
				if err != nil {
					t.Errorf("WithBackgroundColor(%q) unexpected error: %v", tt.color, err)
					return
				}

				// Check that the color was set
				if config.BackgroundColor != tt.color {
					t.Errorf("WithBackgroundColor(%q) config.BackgroundColor = %q, expected %q", tt.color, config.BackgroundColor, tt.color)
				}
			}
		})
	}
}

// TestConfigValidateBackgroundColor tests that Config.Validate() validates background colors.
func TestConfigValidateBackgroundColor(t *testing.T) {
	tests := []struct {
		name          string
		color         string
		expectError   bool
		expectedError string
	}{
		{
			name:        "valid empty color",
			color:       "",
			expectError: false,
		},
		{
			name:        "valid 3-digit hex",
			color:       "#fff",
			expectError: false,
		},
		{
			name:        "valid 6-digit hex",
			color:       "#ffffff",
			expectError: false,
		},
		{
			name:          "invalid color",
			color:         "invalid-color",
			expectError:   true,
			expectedError: "must be a valid hex color in #RGB or #RRGGBB format",
		},
		{
			name:          "missing hash",
			color:         "ffffff",
			expectError:   true,
			expectedError: "must be a valid hex color in #RGB or #RRGGBB format",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			config := DefaultConfig()
			config.BackgroundColor = tt.color

			err := config.Validate()

			if tt.expectError {
				if err == nil {
					t.Errorf("Config.Validate() with BackgroundColor=%q expected error but got none", tt.color)
					return
				}

				// Check that it's the right error type
				var invalidInput *ErrInvalidInput
				if !errors.As(err, &invalidInput) {
					t.Errorf("Config.Validate() with BackgroundColor=%q error type = %T, expected *ErrInvalidInput", tt.color, err)
					return
				}

				// Check error message contains expected text
				if tt.expectedError != "" {
					if !strings.Contains(err.Error(), tt.expectedError) {
						t.Errorf("Config.Validate() with BackgroundColor=%q error = %q, expected to contain %q", tt.color, err.Error(), tt.expectedError)
					}
				}
			} else {
				if err != nil {
					t.Errorf("Config.Validate() with BackgroundColor=%q unexpected error: %v", tt.color, err)
				}
			}
		})
	}
}

// TestToEngineColorConfigBackgroundColor tests that toEngineColorConfig handles background colors.
// Since validation is handled by Config.Validate(), this test focuses on the successful conversion path.
func TestToEngineColorConfigBackgroundColor(t *testing.T) {
	tests := []struct {
		name          string
		color         string
		expectError   bool
		expectedError string
	}{
		{
			name:        "valid empty color",
			color:       "",
			expectError: false,
		},
		{
			name:        "valid 3-digit hex",
			color:       "#fff",
			expectError: false,
		},
		{
			name:        "valid 6-digit hex",
			color:       "#ffffff",
			expectError: false,
		},
		{
			name:          "invalid color caught by Validate()",
			color:         "invalid-color",
			expectError:   true,
			expectedError: "invalid configuration",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			config := DefaultConfig()
			// Directly set the background color to bypass WithBackgroundColor validation
			config.BackgroundColor = tt.color

			_, err := config.toEngineColorConfig()

			if tt.expectError {
				if err == nil {
					t.Errorf("toEngineColorConfig() with BackgroundColor=%q expected error but got none", tt.color)
					return
				}

				// Check error message contains expected text (from c.Validate() call)
				if tt.expectedError != "" {
					if !strings.Contains(err.Error(), tt.expectedError) {
						t.Errorf("toEngineColorConfig() with BackgroundColor=%q error = %q, expected to contain %q", tt.color, err.Error(), tt.expectedError)
					}
				}
			} else {
				if err != nil {
					t.Errorf("toEngineColorConfig() with BackgroundColor=%q unexpected error: %v", tt.color, err)
				}
			}
		})
	}
}

// TestConfigureFunctionWithBackgroundColor tests the Configure function with background color options.
func TestConfigureFunctionWithBackgroundColor(t *testing.T) {
	tests := []struct {
		name          string
		color         string
		expectError   bool
		expectedError string
	}{
		{
			name:        "valid color through Configure",
			color:       "#ffffff",
			expectError: false,
		},
		{
			name:          "invalid color through Configure",
			color:         "invalid",
			expectError:   true,
			expectedError: "configuration option failed",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			config, err := Configure(WithBackgroundColor(tt.color))

			if tt.expectError {
				if err == nil {
					t.Errorf("Configure(WithBackgroundColor(%q)) expected error but got none", tt.color)
					return
				}

				if tt.expectedError != "" {
					if !strings.Contains(err.Error(), tt.expectedError) {
						t.Errorf("Configure(WithBackgroundColor(%q)) error = %q, expected to contain %q", tt.color, err.Error(), tt.expectedError)
					}
				}
			} else {
				if err != nil {
					t.Errorf("Configure(WithBackgroundColor(%q)) unexpected error: %v", tt.color, err)
					return
				}

				if config.BackgroundColor != tt.color {
					t.Errorf("Configure(WithBackgroundColor(%q)) config.BackgroundColor = %q, expected %q", tt.color, config.BackgroundColor, tt.color)
				}
			}
		})
	}
}
