package renderer

import (
	"testing"

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

var benchmarkSizes = []int{
	16, 32, 64, 128, 256, 512,
}

var benchmarkColors = []string{
	"#ff0000", "#00ff00", "#0000ff", "#ffff00", "#ff00ff", "#00ffff",
	"#800000", "#008000", "#000080", "#808000", "#800080", "#008080",
	"#c0c0c0", "#808080", "#000000", "#ffffff",
}

var benchmarkPoints = [][]engine.Point{
	// Triangle
	{{X: 0, Y: 0}, {X: 1, Y: 0}, {X: 0.5, Y: 1}},
	// Square
	{{X: 0, Y: 0}, {X: 1, Y: 0}, {X: 1, Y: 1}, {X: 0, Y: 1}},
	// Pentagon
	{{X: 0.5, Y: 0}, {X: 1, Y: 0.4}, {X: 0.8, Y: 1}, {X: 0.2, Y: 1}, {X: 0, Y: 0.4}},
	// Hexagon
	{{X: 0.25, Y: 0}, {X: 0.75, Y: 0}, {X: 1, Y: 0.5}, {X: 0.75, Y: 1}, {X: 0.25, Y: 1}, {X: 0, Y: 0.5}},
}

// Benchmark SVG renderer creation
func BenchmarkNewSVGRenderer(b *testing.B) {
	b.ResetTimer()
	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		size := benchmarkSizes[i%len(benchmarkSizes)]
		_ = NewSVGRenderer(size)
	}
}

// Benchmark SVG shape rendering
func BenchmarkSVGAddPolygon(b *testing.B) {
	renderer := NewSVGRenderer(256)

	b.ResetTimer()
	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		color := benchmarkColors[i%len(benchmarkColors)]
		points := benchmarkPoints[i%len(benchmarkPoints)]

		renderer.BeginShape(color)
		renderer.AddPolygon(points)
		renderer.EndShape()
	}
}

// Benchmark SVG circle rendering
func BenchmarkSVGAddCircle(b *testing.B) {
	renderer := NewSVGRenderer(256)

	b.ResetTimer()
	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		color := benchmarkColors[i%len(benchmarkColors)]
		topLeft := engine.Point{X: 0.25, Y: 0.25}
		size := 0.5

		renderer.BeginShape(color)
		renderer.AddCircle(topLeft, size, false)
		renderer.EndShape()
	}
}

// Benchmark SVG background setting
func BenchmarkSVGSetBackground(b *testing.B) {
	renderer := NewSVGRenderer(256)

	b.ResetTimer()
	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		color := benchmarkColors[i%len(benchmarkColors)]
		opacity := 0.8
		renderer.SetBackground(color, opacity)
	}
}

// Benchmark complete SVG generation
func BenchmarkSVGToSVG(b *testing.B) {
	b.ResetTimer()
	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		size := benchmarkSizes[i%len(benchmarkSizes)]
		renderer := NewSVGRenderer(size)

		// Add some shapes
		renderer.SetBackground("#f0f0f0", 1.0)

		for j := 0; j < 3; j++ {
			color := benchmarkColors[j%len(benchmarkColors)]
			points := benchmarkPoints[j%len(benchmarkPoints)]

			renderer.BeginShape(color)
			renderer.AddPolygon(points)
			renderer.EndShape()
		}

		_ = renderer.ToSVG()
	}
}

// Benchmark PNG renderer creation
func BenchmarkNewPNGRenderer(b *testing.B) {
	b.ResetTimer()
	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		size := benchmarkSizes[i%len(benchmarkSizes)]
		_ = NewPNGRenderer(size)
	}
}

// Benchmark PNG shape rendering
func BenchmarkPNGAddPolygon(b *testing.B) {
	renderer := NewPNGRenderer(256)

	b.ResetTimer()
	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		color := benchmarkColors[i%len(benchmarkColors)]
		points := benchmarkPoints[i%len(benchmarkPoints)]

		renderer.BeginShape(color)
		renderer.AddPolygon(points)
		renderer.EndShape()
	}
}

// Benchmark PNG circle rendering
func BenchmarkPNGAddCircle(b *testing.B) {
	renderer := NewPNGRenderer(256)

	b.ResetTimer()
	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		color := benchmarkColors[i%len(benchmarkColors)]
		topLeft := engine.Point{X: 0.25, Y: 0.25}
		size := 0.5

		renderer.BeginShape(color)
		renderer.AddCircle(topLeft, size, false)
		renderer.EndShape()
	}
}

// Benchmark PNG background setting
func BenchmarkPNGSetBackground(b *testing.B) {
	renderer := NewPNGRenderer(256)

	b.ResetTimer()
	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		color := benchmarkColors[i%len(benchmarkColors)]
		opacity := 0.8
		renderer.SetBackground(color, opacity)
	}
}

// Benchmark complete PNG generation
func BenchmarkPNGToPNG(b *testing.B) {
	b.ResetTimer()
	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		size := benchmarkSizes[i%len(benchmarkSizes)]
		renderer := NewPNGRenderer(size)

		// Add some shapes
		renderer.SetBackground("#f0f0f0", 1.0)

		for j := 0; j < 3; j++ {
			color := benchmarkColors[j%len(benchmarkColors)]
			points := benchmarkPoints[j%len(benchmarkPoints)]

			renderer.BeginShape(color)
			renderer.AddPolygon(points)
			renderer.EndShape()
		}

		_, err := renderer.ToPNG()
		if err != nil {
			b.Fatalf("ToPNG failed: %v", err)
		}
	}
}

// Benchmark PNG generation with different output sizes
func BenchmarkPNGToPNGWithSize(b *testing.B) {
	renderer := NewPNGRenderer(128)

	// Add some test shapes
	renderer.SetBackground("#ffffff", 1.0)
	renderer.BeginShape("#ff0000")
	renderer.AddPolygon(benchmarkPoints[0])
	renderer.EndShape()

	b.ResetTimer()
	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		outputSize := benchmarkSizes[i%len(benchmarkSizes)]
		_, err := renderer.ToPNGWithSize(outputSize)
		if err != nil {
			b.Fatalf("ToPNGWithSize failed: %v", err)
		}
	}
}

// Benchmark complex shape rendering (many polygons)
func BenchmarkComplexSVGRendering(b *testing.B) {
	b.ResetTimer()
	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		renderer := NewSVGRenderer(256)
		renderer.SetBackground("#f8f8f8", 1.0)

		// Render many shapes to simulate complex icon
		for j := 0; j < 12; j++ {
			color := benchmarkColors[j%len(benchmarkColors)]
			points := benchmarkPoints[j%len(benchmarkPoints)]

			renderer.BeginShape(color)
			renderer.AddPolygon(points)
			renderer.EndShape()
		}

		_ = renderer.ToSVG()
	}
}

// Benchmark complex shape rendering (many polygons) for PNG
func BenchmarkComplexPNGRendering(b *testing.B) {
	b.ResetTimer()
	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		renderer := NewPNGRenderer(256)
		renderer.SetBackground("#f8f8f8", 1.0)

		// Render many shapes to simulate complex icon
		for j := 0; j < 12; j++ {
			color := benchmarkColors[j%len(benchmarkColors)]
			points := benchmarkPoints[j%len(benchmarkPoints)]

			renderer.BeginShape(color)
			renderer.AddPolygon(points)
			renderer.EndShape()
		}

		_, err := renderer.ToPNG()
		if err != nil {
			b.Fatalf("ToPNG failed: %v", err)
		}
	}
}

// Benchmark SVG vs PNG rendering comparison
func BenchmarkSVGvsPNG64px(b *testing.B) {
	// Shared test data
	testShapes := []struct {
		color  string
		points []engine.Point
	}{
		{"#ff0000", benchmarkPoints[0]},
		{"#00ff00", benchmarkPoints[1]},
		{"#0000ff", benchmarkPoints[2]},
	}

	b.Run("SVG", func(b *testing.B) {
		b.ResetTimer()
		b.ReportAllocs()

		for i := 0; i < b.N; i++ {
			renderer := NewSVGRenderer(64)
			renderer.SetBackground("#ffffff", 1.0)

			for _, shape := range testShapes {
				renderer.BeginShape(shape.color)
				renderer.AddPolygon(shape.points)
				renderer.EndShape()
			}

			_ = renderer.ToSVG()
		}
	})

	b.Run("PNG", func(b *testing.B) {
		b.ResetTimer()
		b.ReportAllocs()

		for i := 0; i < b.N; i++ {
			renderer := NewPNGRenderer(64)
			renderer.SetBackground("#ffffff", 1.0)

			for _, shape := range testShapes {
				renderer.BeginShape(shape.color)
				renderer.AddPolygon(shape.points)
				renderer.EndShape()
			}

			_, err := renderer.ToPNG()
			if err != nil {
				b.Fatalf("ToPNG failed: %v", err)
			}
		}
	})
}

// Benchmark memory allocation patterns
func BenchmarkRendererMemoryPatterns(b *testing.B) {
	b.Run("SVGMemory", func(b *testing.B) {
		b.ResetTimer()
		b.ReportAllocs()

		for i := 0; i < b.N; i++ {
			renderer := NewSVGRenderer(128)

			// Allocate many small shapes to test memory patterns
			for j := 0; j < 20; j++ {
				renderer.BeginShape("#808080")
				renderer.AddPolygon(benchmarkPoints[j%len(benchmarkPoints)])
				renderer.EndShape()
			}
		}
	})

	b.Run("PNGMemory", func(b *testing.B) {
		b.ResetTimer()
		b.ReportAllocs()

		for i := 0; i < b.N; i++ {
			renderer := NewPNGRenderer(128)

			// Allocate many small shapes to test memory patterns
			for j := 0; j < 20; j++ {
				renderer.BeginShape("#808080")
				renderer.AddPolygon(benchmarkPoints[j%len(benchmarkPoints)])
				renderer.EndShape()
			}
		}
	})
}

// Benchmark concurrent rendering scenarios
func BenchmarkRendererParallel(b *testing.B) {
	b.Run("SVGParallel", func(b *testing.B) {
		b.ReportAllocs()

		b.RunParallel(func(pb *testing.PB) {
			i := 0
			for pb.Next() {
				size := benchmarkSizes[i%len(benchmarkSizes)]
				renderer := NewSVGRenderer(size)
				renderer.SetBackground("#ffffff", 1.0)

				for j := 0; j < 3; j++ {
					color := benchmarkColors[j%len(benchmarkColors)]
					points := benchmarkPoints[j%len(benchmarkPoints)]

					renderer.BeginShape(color)
					renderer.AddPolygon(points)
					renderer.EndShape()
				}

				_ = renderer.ToSVG()
				i++
			}
		})
	})

	b.Run("PNGParallel", func(b *testing.B) {
		b.ReportAllocs()

		b.RunParallel(func(pb *testing.PB) {
			i := 0
			for pb.Next() {
				size := benchmarkSizes[i%len(benchmarkSizes)]
				renderer := NewPNGRenderer(size)
				renderer.SetBackground("#ffffff", 1.0)

				for j := 0; j < 3; j++ {
					color := benchmarkColors[j%len(benchmarkColors)]
					points := benchmarkPoints[j%len(benchmarkPoints)]

					renderer.BeginShape(color)
					renderer.AddPolygon(points)
					renderer.EndShape()
				}

				_, err := renderer.ToPNG()
				if err != nil {
					b.Errorf("ToPNG failed: %v", err)
				}
				i++
			}
		})
	})
}

// Benchmark shape rendering with different complexities
func BenchmarkShapeComplexity(b *testing.B) {
	renderer := NewSVGRenderer(256)

	b.Run("Triangle", func(b *testing.B) {
		b.ReportAllocs()
		trianglePoints := benchmarkPoints[0] // Triangle

		for i := 0; i < b.N; i++ {
			renderer.BeginShape("#ff0000")
			renderer.AddPolygon(trianglePoints)
			renderer.EndShape()
		}
	})

	b.Run("Square", func(b *testing.B) {
		b.ReportAllocs()
		squarePoints := benchmarkPoints[1] // Square

		for i := 0; i < b.N; i++ {
			renderer.BeginShape("#00ff00")
			renderer.AddPolygon(squarePoints)
			renderer.EndShape()
		}
	})

	b.Run("Pentagon", func(b *testing.B) {
		b.ReportAllocs()
		pentagonPoints := benchmarkPoints[2] // Pentagon

		for i := 0; i < b.N; i++ {
			renderer.BeginShape("#0000ff")
			renderer.AddPolygon(pentagonPoints)
			renderer.EndShape()
		}
	})

	b.Run("Hexagon", func(b *testing.B) {
		b.ReportAllocs()
		hexagonPoints := benchmarkPoints[3] // Hexagon

		for i := 0; i < b.N; i++ {
			renderer.BeginShape("#ffff00")
			renderer.AddPolygon(hexagonPoints)
			renderer.EndShape()
		}
	})
}
