package renderer

import (
	"fmt"
	"strings"
	"testing"

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

func TestSVGPath_AddPolygon(t *testing.T) {
	path := &SVGPath{}
	points := []engine.Point{
		{X: 0, Y: 0},
		{X: 10, Y: 0},
		{X: 10, Y: 10},
		{X: 0, Y: 10},
	}

	path.AddPolygon(points)
	expected := "M0 0L10 0L10 10L0 10Z"
	if got := path.DataString(); got != expected {
		t.Errorf("AddPolygon() = %v, want %v", got, expected)
	}
}

func TestSVGPath_AddPolygonEmpty(t *testing.T) {
	path := &SVGPath{}
	path.AddPolygon([]engine.Point{})

	if got := path.DataString(); got != "" {
		t.Errorf("AddPolygon([]) = %v, want empty string", got)
	}
}

func TestSVGPath_AddCircle(t *testing.T) {
	path := &SVGPath{}
	topLeft := engine.Point{X: 25, Y: 25} // Top-left corner to get center at (50, 50)
	size := 50.0                          // Size 50 gives radius 25

	path.AddCircle(topLeft, size, false)

	// Should start at left side of circle and draw two arcs
	result := path.DataString()
	if !strings.HasPrefix(result, "M25 50") {
		t.Errorf("Circle should start at left side, got: %s", result)
	}
	if !strings.Contains(result, "a25,25 0 1,1") {
		t.Errorf("Circle should contain clockwise arc, got: %s", result)
	}
}

func TestSVGPath_AddCircleCounterClockwise(t *testing.T) {
	path := &SVGPath{}
	topLeft := engine.Point{X: 25, Y: 25} // Top-left corner to get center at (50, 50)
	size := 50.0                          // Size 50 gives radius 25

	path.AddCircle(topLeft, size, true)

	result := path.DataString()
	if !strings.Contains(result, "a25,25 0 1,0") {
		t.Errorf("Counter-clockwise circle should have sweep flag 0, got: %s", result)
	}
}

func TestSVGRenderer_NewSVGRenderer(t *testing.T) {
	renderer := NewSVGRenderer(100)

	if renderer.iconSize != 100 {
		t.Errorf("NewSVGRenderer(100).iconSize = %v, want 100", renderer.iconSize)
	}
	if renderer.pathsByColor == nil {
		t.Error("pathsByColor should be initialized")
	}
}

func TestSVGRenderer_BeginEndShape(t *testing.T) {
	renderer := NewSVGRenderer(100)

	renderer.BeginShape("#ff0000")
	if renderer.currentColor != "#ff0000" {
		t.Errorf("BeginShape should set currentColor, got %v", renderer.currentColor)
	}

	if _, exists := renderer.pathsByColor["#ff0000"]; !exists {
		t.Error("BeginShape should create path for color")
	}

	renderer.EndShape()
	// EndShape is a no-op for SVG, just verify it doesn't panic
}

func TestSVGRenderer_AddPolygon(t *testing.T) {
	renderer := NewSVGRenderer(100)
	renderer.BeginShape("#ff0000")

	points := []engine.Point{
		{X: 0, Y: 0},
		{X: 10, Y: 0},
		{X: 5, Y: 10},
	}

	renderer.AddPolygon(points)

	path := renderer.pathsByColor["#ff0000"]
	expected := "M0 0L10 0L5 10Z"
	if got := path.DataString(); got != expected {
		t.Errorf("AddPolygon() = %v, want %v", got, expected)
	}
}

func TestSVGRenderer_AddCircle(t *testing.T) {
	renderer := NewSVGRenderer(100)
	renderer.BeginShape("#00ff00")

	topLeft := engine.Point{X: 30, Y: 30} // Top-left corner to get center at (50, 50)
	size := 40.0                          // Size 40 gives radius 20

	renderer.AddCircle(topLeft, size, false)

	path := renderer.pathsByColor["#00ff00"]
	result := path.DataString()
	if !strings.HasPrefix(result, "M30 50") {
		t.Errorf("Circle should start at correct position, got: %s", result)
	}
}

func TestSVGRenderer_ToSVG(t *testing.T) {
	renderer := NewSVGRenderer(100)
	renderer.SetBackground("#ffffff", 1.0)

	renderer.BeginShape("#ff0000")
	points := []engine.Point{
		{X: 0, Y: 0},
		{X: 10, Y: 0},
		{X: 10, Y: 10},
	}
	renderer.AddPolygon(points)

	svg := renderer.ToSVG()

	// Check SVG structure
	if !strings.Contains(svg, `<svg xmlns="http://www.w3.org/2000/svg"`) {
		t.Error("SVG should contain proper xmlns")
	}
	if !strings.Contains(svg, `width="100" height="100"`) {
		t.Error("SVG should contain correct dimensions")
	}
	if !strings.Contains(svg, `viewBox="0 0 100 100"`) {
		t.Error("SVG should contain correct viewBox")
	}
	if !strings.Contains(svg, `<rect width="100%" height="100%" fill="#ffffff" opacity="1.00"/>`) {
		t.Error("SVG should contain background rect")
	}
	if !strings.Contains(svg, `<path fill="#ff0000" d="M0 0L10 0L10 10Z"/>`) {
		t.Error("SVG should contain path with correct data")
	}
	if !strings.HasSuffix(svg, "</svg>") {
		t.Error("SVG should end with closing tag")
	}
}

func TestSVGRenderer_ToSVGWithoutBackground(t *testing.T) {
	renderer := NewSVGRenderer(50)

	renderer.BeginShape("#0000ff")
	center := engine.Point{X: 25, Y: 25}
	renderer.AddCircle(center, 10, false)

	svg := renderer.ToSVG()

	// Should not contain background rect
	if strings.Contains(svg, "<rect") {
		t.Error("SVG without background should not contain rect")
	}
	// Should contain the circle path
	if !strings.Contains(svg, `fill="#0000ff"`) {
		t.Error("SVG should contain circle path")
	}
}

func TestSVGRenderer_BackgroundWithOpacity(t *testing.T) {
	renderer := NewSVGRenderer(100)
	renderer.SetBackground("#cccccc", 0.5)

	svg := renderer.ToSVG()

	if !strings.Contains(svg, `opacity="0.50"`) {
		t.Error("SVG should contain opacity attribute")
	}
}

func TestSvgValue(t *testing.T) {
	tests := []struct {
		input    float64
		expected string
	}{
		{0, "0"},
		{1.0, "1"},
		{1.5, "1.5"},
		{1.23456, "1.2"},
		{1.26, "1.3"},
		{10.0, "10"},
		{10.1, "10.1"},
	}

	for _, test := range tests {
		if got := svgValue(test.input); got != test.expected {
			t.Errorf("svgValue(%v) = %v, want %v", test.input, got, test.expected)
		}
	}
}

func TestSvgValueRounding(t *testing.T) {
	// Test cases to verify "round half up" behavior matches JavaScript implementation
	testCases := []struct {
		input    float64
		expected string
	}{
		{12.0, "12"},
		{12.2, "12.2"},
		{12.25, "12.3"}, // Key case that fails with math.Round (would be "12.2")
		{12.35, "12.4"}, // Another case to verify consistent behavior
		{12.45, "12.5"}, // Another key case
		{12.75, "12.8"},
		{-12.25, "-12.2"}, // Test negative rounding
		{-12.35, "-12.3"},
		{50.45, "50.5"}, // Real-world case from avatar generation
		{50.55, "50.6"},
	}

	for _, tc := range testCases {
		t.Run(fmt.Sprintf("Input_%f", tc.input), func(t *testing.T) {
			got := svgValue(tc.input)
			if got != tc.expected {
				t.Errorf("svgValue(%f) = %q; want %q", tc.input, got, tc.expected)
			}
		})
	}
}
