package engine

import (
	"testing"
)

// TestCorrectedHSLToRGB_EmptyCorrectors tests the defensive bounds checking
// for the correctors array in CorrectedHSLToRGB
func TestCorrectedHSLToRGB_EmptyCorrectors(t *testing.T) {
	// Save original correctors
	originalCorrectors := correctors
	defer func() { correctors = originalCorrectors }()

	// Temporarily modify the unexported variable for this test
	correctors = []float64{}

	// Call the function and assert that it returns the expected error
	//nolint:dogsled // We only care about the error in this test
	_, _, _, err := CorrectedHSLToRGB(0.5, 0.5, 0.5)
	if err == nil {
		t.Fatal("expected an error for empty correctors, got nil")
	}

	// Check if error message contains expected content
	expectedMsg := "color correctors table is empty"
	if !contains(err.Error(), expectedMsg) {
		t.Errorf("expected error message to contain %q, got %q", expectedMsg, err.Error())
	}

	t.Logf("Got expected error: %v", err)
}

// TestHSLToRGB_FloatingPointValidation tests that HSLToRGB validates
// floating point results and catches NaN/Inf values
func TestHSLToRGB_FloatingPointValidation(t *testing.T) {
	// Test normal cases first
	testCases := []struct {
		name        string
		h, s, l     float64
		expectError bool
	}{
		{"normal_color", 0.5, 0.5, 0.5, false},
		{"pure_red", 0.0, 1.0, 0.5, false},
		{"white", 0.0, 0.0, 1.0, false},
		{"black", 0.0, 0.0, 0.0, false},
		{"grayscale", 0.0, 0.0, 0.5, false},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			r, g, b, err := HSLToRGB(tc.h, tc.s, tc.l)

			if tc.expectError {
				if err == nil {
					t.Errorf("expected error for %s, got none", tc.name)
				}
			} else {
				if err != nil {
					t.Errorf("unexpected error for %s: %v", tc.name, err)
				}
				// RGB values are uint8, so they're guaranteed to be in 0-255 range
				_ = r
				_ = g
				_ = b
			}
		})
	}
}

// TestColor_ToRGB_ErrorHandling tests that Color.ToRGB properly handles
// errors from the underlying conversion functions
func TestColor_ToRGB_ErrorHandling(t *testing.T) {
	// Test with normal values
	color := NewColorHSL(0.5, 0.5, 0.5)
	r, g, b, err := color.ToRGB()
	if err != nil {
		t.Errorf("ToRGB failed for normal color: %v", err)
	}
	// RGB values are uint8, so they're guaranteed to be in 0-255 range
	_ = r
	_ = g
	_ = b

	// Test corrected color conversion
	correctedColor := NewColorCorrectedHSL(0.5, 0.5, 0.5)
	r2, g2, b2, err2 := correctedColor.ToRGB()
	if err2 != nil {
		t.Errorf("ToRGB failed for corrected color: %v", err2)
	}
	// RGB values are uint8, so they're guaranteed to be in 0-255 range
	_ = r2
	_ = g2
	_ = b2
}

// TestColor_String_ErrorFallback tests that Color.String() properly handles
// conversion errors by falling back to black
func TestColor_String_ErrorFallback(t *testing.T) {
	// Save original correctors
	originalCorrectors := correctors
	defer func() { correctors = originalCorrectors }()

	// Create a corrected color that will fail conversion
	color := NewColorCorrectedHSL(0.5, 0.5, 0.5)

	// Temporarily break correctors to force an error
	correctors = []float64{}

	// String() should not panic and should return a fallback color
	result := color.String()

	// Should return black (#000000) as fallback
	expected := "#000000"
	if result != expected {
		t.Errorf("expected fallback color %s, got %s", expected, result)
	}

	t.Logf("String() properly fell back to: %s", result)
}

// TestColor_Equals_ErrorHandling tests that Color.Equals properly handles
// conversion errors by returning false
func TestColor_Equals_ErrorHandling(t *testing.T) {
	// Save original correctors
	originalCorrectors := correctors
	defer func() { correctors = originalCorrectors }()

	color1 := NewColorCorrectedHSL(0.5, 0.5, 0.5)
	color2 := NewColorCorrectedHSL(0.5, 0.5, 0.5)

	// First test normal comparison
	if !color1.Equals(color2) {
		t.Error("identical colors should be equal")
	}

	// Now break correctors to force conversion errors
	correctors = []float64{}

	// Colors with conversion errors should not be equal
	if color1.Equals(color2) {
		t.Error("colors with conversion errors should not be equal")
	}

	t.Log("Equals properly handled conversion errors")
}

// TestGenerateColorTheme_Robustness tests that GenerateColorTheme always
// returns exactly 5 colors and handles edge cases
func TestGenerateColorTheme_Robustness(t *testing.T) {
	config := DefaultColorConfig()

	testCases := []struct {
		name string
		hue  float64
	}{
		{"zero_hue", 0.0},
		{"mid_hue", 0.5},
		{"max_hue", 1.0},
		{"negative_hue", -0.5}, // Should be handled by hue normalization
		{"large_hue", 2.5},     // Should be handled by hue normalization
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			colors := GenerateColorTheme(tc.hue, config)

			// Must always return exactly 5 colors
			if len(colors) != 5 {
				t.Errorf("GenerateColorTheme returned %d colors, expected 5", len(colors))
			}

			// Each color should be valid (convertible to RGB)
			for i, color := range colors {
				_, _, _, err := color.ToRGB()
				if err != nil {
					t.Errorf("color %d in theme failed RGB conversion: %v", i, err)
				}
			}
		})
	}
}

// Helper function to check if a string contains a substring
func contains(s, substr string) bool {
	return len(s) >= len(substr) &&
		(s == substr || (len(s) > len(substr) &&
			(s[:len(substr)] == substr ||
				s[len(s)-len(substr):] == substr ||
				func() bool {
					for i := 0; i <= len(s)-len(substr); i++ {
						if s[i:i+len(substr)] == substr {
							return true
						}
					}
					return false
				}())))
}
