package renderer

import (
	"strings"
	"testing"

	"gitea.dockr.co/kev/go-jdenticon/internal/engine"
)

// TestSVGRenderer_SecurityValidation tests defense-in-depth color validation
// This test addresses SEC-06 from the security report by verifying that
// the SVG renderer properly validates color inputs and prevents injection attacks.
func TestSVGRenderer_SecurityValidation(t *testing.T) {
	tests := []struct {
		name        string
		color       string
		expectInSVG bool
		description string
	}{
		{
			name:        "valid_hex_color_3_digit",
			color:       "#f00",
			expectInSVG: true,
			description: "Valid 3-digit hex color should be rendered",
		},
		{
			name:        "valid_hex_color_6_digit",
			color:       "#ff0000",
			expectInSVG: true,
			description: "Valid 6-digit hex color should be rendered",
		},
		{
			name:        "valid_hex_color_8_digit",
			color:       "#ff0000ff",
			expectInSVG: true,
			description: "Valid 8-digit hex color with alpha should be rendered",
		},
		{
			name:        "injection_attempt_script",
			color:       "\"><script>alert('xss')</script><path fill=\"#000",
			expectInSVG: false,
			description: "Script injection attempt should be blocked",
		},
		{
			name:        "injection_attempt_svg_element",
			color:       "#f00\"/><use href=\"#malicious\"/><path fill=\"#000",
			expectInSVG: false,
			description: "SVG element injection attempt should be blocked",
		},
		{
			name:        "malformed_hex_no_hash",
			color:       "ff0000",
			expectInSVG: false,
			description: "Hex color without # should be rejected",
		},
		{
			name:        "valid_hex_color_4_digit_rgba",
			color:       "#ff00",
			expectInSVG: true,
			description: "Valid 4-digit RGBA hex color should be rendered",
		},
		{
			name:        "malformed_hex_invalid_length_5",
			color:       "#ff000",
			expectInSVG: false,
			description: "Invalid 5-character hex color should be rejected",
		},
		{
			name:        "malformed_hex_invalid_chars",
			color:       "#gggggg",
			expectInSVG: false,
			description: "Invalid hex characters should be rejected",
		},
		{
			name:        "empty_color",
			color:       "",
			expectInSVG: false,
			description: "Empty color string should be rejected",
		},
		{
			name:        "xml_entity_injection",
			color:       "#ff0000&lt;script&gt;",
			expectInSVG: false,
			description: "XML entity injection attempt should be blocked",
		},
		{
			name:        "path_data_injection",
			color:       "#f00\" d=\"M0 0L100 100Z\"/><script>alert('xss')</script><path fill=\"#000",
			expectInSVG: false,
			description: "Path data injection attempt should be blocked",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			renderer := NewSVGRenderer(100)

			// Test BeginShape validation
			renderer.BeginShape(tt.color)

			// Add some path data to ensure the color would be rendered if valid
			points := []engine.Point{
				{X: 10, Y: 10},
				{X: 50, Y: 10},
				{X: 50, Y: 50},
				{X: 10, Y: 50},
			}
			renderer.AddPolygon(points)
			renderer.EndShape()

			// Generate SVG output
			svgOutput := renderer.ToSVG()

			if tt.expectInSVG {
				// Verify valid colors are present in the output
				if !strings.Contains(svgOutput, `fill="`+tt.color+`"`) {
					t.Errorf("Expected valid color %s to be present in SVG output, but it was not found.\nSVG: %s", tt.color, svgOutput)
				}

				// Ensure the path element is present for valid colors
				if !strings.Contains(svgOutput, "<path") {
					t.Errorf("Expected path element to be present for valid color %s, but it was not found", tt.color)
				}
			} else {
				// Verify invalid/malicious colors are NOT present in the output
				// Special handling for empty string since it's always "contained" in any string
				if tt.color != "" && strings.Contains(svgOutput, tt.color) {
					t.Errorf("Expected invalid/malicious color %s to be rejected, but it was found in SVG output.\nSVG: %s", tt.color, svgOutput)
				}

				// For invalid colors, no path should be rendered with that color
				if strings.Contains(svgOutput, `fill="`+tt.color+`"`) {
					t.Errorf("Expected invalid color %s to be rejected from fill attribute, but it was found", tt.color)
				}
			}

			// Verify the SVG is still well-formed XML
			if !strings.HasPrefix(svgOutput, "<svg") {
				t.Errorf("SVG output should start with <svg tag")
			}
			if !strings.HasSuffix(svgOutput, "</svg>") {
				t.Errorf("SVG output should end with </svg> tag")
			}
		})
	}
}

// TestSVGRenderer_BackgroundColorValidation tests background color validation
func TestSVGRenderer_BackgroundColorValidation(t *testing.T) {
	tests := []struct {
		name        string
		bgColor     string
		opacity     float64
		expectInSVG bool
		description string
	}{
		{
			name:        "valid_background_color",
			bgColor:     "#ffffff",
			opacity:     1.0,
			expectInSVG: true,
			description: "Valid background color should be rendered",
		},
		{
			name:        "invalid_background_injection",
			bgColor:     "#fff\"/><script>alert('bg')</script><rect fill=\"#000",
			opacity:     1.0,
			expectInSVG: false,
			description: "Background color injection should be blocked",
		},
		{
			name:        "malformed_background_color",
			bgColor:     "not-a-color",
			opacity:     1.0,
			expectInSVG: false,
			description: "Invalid background color format should be rejected",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			renderer := NewSVGRenderer(100)
			renderer.SetBackground(tt.bgColor, tt.opacity)

			svgOutput := renderer.ToSVG()

			if tt.expectInSVG {
				// Verify valid background colors are present
				if !strings.Contains(svgOutput, `<rect`) {
					t.Errorf("Expected background rectangle to be present for valid color %s", tt.bgColor)
				}
				if !strings.Contains(svgOutput, `fill="`+tt.bgColor+`"`) {
					t.Errorf("Expected valid background color %s to be present in SVG", tt.bgColor)
				}
			} else {
				// Verify invalid background colors are rejected
				if strings.Contains(svgOutput, tt.bgColor) {
					t.Errorf("Expected invalid background color %s to be rejected, but found in: %s", tt.bgColor, svgOutput)
				}
			}
		})
	}
}

// TestSVGRenderer_MultipleInvalidColors tests behavior with multiple invalid colors
func TestSVGRenderer_MultipleInvalidColors(t *testing.T) {
	renderer := NewSVGRenderer(100)

	maliciousColors := []string{
		"\"><script>alert(1)</script><path fill=\"#000",
		"#invalid-color",
		"javascript:alert('xss')",
		"#ff0000\"/><use href=\"#malicious\"/>",
	}

	// Try to add shapes with all malicious colors
	for _, color := range maliciousColors {
		renderer.BeginShape(color)
		points := []engine.Point{{X: 0, Y: 0}, {X: 50, Y: 50}}
		renderer.AddPolygon(points)
		renderer.EndShape()
	}

	svgOutput := renderer.ToSVG()

	// Verify none of the malicious colors appear in the output
	for _, color := range maliciousColors {
		if strings.Contains(svgOutput, color) {
			t.Errorf("Malicious color %s should not appear in SVG output, but was found: %s", color, svgOutput)
		}
	}

	// Verify the SVG is still valid and doesn't contain path elements for rejected colors
	pathCount := strings.Count(svgOutput, "<path")
	if pathCount > 0 {
		t.Errorf("Expected no path elements for invalid colors, but found %d", pathCount)
	}

	// Ensure SVG structure is intact
	if !strings.Contains(svgOutput, `<svg xmlns="http://www.w3.org/2000/svg"`) {
		t.Errorf("SVG should still have proper structure even with all invalid colors")
	}
}

// TestSVGRenderer_ValidAndInvalidColorMix tests mixed valid/invalid colors
func TestSVGRenderer_ValidAndInvalidColorMix(t *testing.T) {
	renderer := NewSVGRenderer(100)

	// Add valid color
	renderer.BeginShape("#ff0000")
	renderer.AddPolygon([]engine.Point{{X: 0, Y: 0}, {X: 25, Y: 25}})
	renderer.EndShape()

	// Add invalid color
	renderer.BeginShape("\"><script>alert('xss')</script><path fill=\"#000")
	renderer.AddPolygon([]engine.Point{{X: 25, Y: 25}, {X: 50, Y: 50}})
	renderer.EndShape()

	// Add another valid color
	renderer.BeginShape("#00ff00")
	renderer.AddPolygon([]engine.Point{{X: 50, Y: 50}, {X: 75, Y: 75}})
	renderer.EndShape()

	svgOutput := renderer.ToSVG()

	// Valid colors should be present
	if !strings.Contains(svgOutput, `fill="#ff0000"`) {
		t.Errorf("Valid color #ff0000 should be present in output")
	}
	if !strings.Contains(svgOutput, `fill="#00ff00"`) {
		t.Errorf("Valid color #00ff00 should be present in output")
	}

	// Invalid color should be rejected
	if strings.Contains(svgOutput, "script") {
		t.Errorf("Invalid color with script injection should be rejected")
	}

	// Should have exactly 2 path elements (for the 2 valid colors)
	pathCount := strings.Count(svgOutput, "<path")
	if pathCount != 2 {
		t.Errorf("Expected exactly 2 path elements for valid colors, got %d", pathCount)
	}
}
