package renderer

import (
	"strconv"
	"strings"
	"testing"

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

// ============================================================================
// RENDERER MICRO-BENCHMARKS FOR MEMORY ALLOCATION ANALYSIS
// ============================================================================

var (
	// Test data for renderer benchmarks
	benchTestPoints = []engine.Point{
		{X: 0.0, Y: 0.0},
		{X: 10.5, Y: 0.0},
		{X: 10.5, Y: 10.5},
		{X: 0.0, Y: 10.5},
	}
	benchTestColors = []string{
		"#ff0000", "#00ff00", "#0000ff", "#ffff00", "#ff00ff",
	}
	benchTestSizes = []int{32, 64, 128, 256}
)

// ============================================================================
// SVG STRING BUILDING MICRO-BENCHMARKS
// ============================================================================

// BenchmarkSVGStringBuilding tests different string building patterns in SVG generation
func BenchmarkSVGStringBuilding(b *testing.B) {
	points := benchTestPoints

	b.Run("svgValue_formatting", func(b *testing.B) {
		values := []float64{0.0, 10.5, 15.75, 100.0, 256.5}
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			value := values[i%len(values)]
			_ = svgValue(value)
		}
	})

	b.Run("strconv_FormatFloat", func(b *testing.B) {
		value := 10.5
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			_ = strconv.FormatFloat(value, 'f', 1, 64)
		}
	})

	b.Run("strconv_Itoa", func(b *testing.B) {
		value := 10
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			_ = strconv.Itoa(value)
		}
	})

	b.Run("polygon_path_building", func(b *testing.B) {
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			var buf strings.Builder
			buf.Grow(50) // Estimate capacity

			// Simulate polygon path building
			buf.WriteString("M")
			buf.WriteString(svgValue(points[0].X))
			buf.WriteString(" ")
			buf.WriteString(svgValue(points[0].Y))

			for j := 1; j < len(points); j++ {
				buf.WriteString("L")
				buf.WriteString(svgValue(points[j].X))
				buf.WriteString(" ")
				buf.WriteString(svgValue(points[j].Y))
			}
			buf.WriteString("Z")
			_ = buf.String()
		}
	})
}

// BenchmarkSVGPathOperations tests SVGPath struct operations
func BenchmarkSVGPathOperations(b *testing.B) {
	points := benchTestPoints

	b.Run("SVGPath_AddPolygon", func(b *testing.B) {
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			path := &SVGPath{}
			path.AddPolygon(points)
		}
	})

	b.Run("SVGPath_AddCircle", func(b *testing.B) {
		topLeft := engine.Point{X: 5.0, Y: 5.0}
		size := 10.0
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			path := &SVGPath{}
			path.AddCircle(topLeft, size, false)
		}
	})

	b.Run("SVGPath_DataString", func(b *testing.B) {
		path := &SVGPath{}
		path.AddPolygon(points)
		b.ResetTimer()
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			_ = path.DataString()
		}
	})
}

// BenchmarkStringBuilderPooling tests the efficiency of string builder pooling
func BenchmarkStringBuilderPooling(b *testing.B) {
	points := benchTestPoints

	b.Run("direct_builder", func(b *testing.B) {
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			// Use direct string builder (pool eliminated for direct writing)
			var buf strings.Builder

			// Build polygon path
			buf.WriteString("M")
			buf.WriteString(svgValue(points[0].X))
			buf.WriteString(" ")
			buf.WriteString(svgValue(points[0].Y))
			for j := 1; j < len(points); j++ {
				buf.WriteString("L")
				buf.WriteString(svgValue(points[j].X))
				buf.WriteString(" ")
				buf.WriteString(svgValue(points[j].Y))
			}
			buf.WriteString("Z")
			_ = buf.String()
		}
	})

	b.Run("without_pool", func(b *testing.B) {
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			// Create new buffer each time
			var buf strings.Builder
			buf.Grow(50)

			// Build polygon path
			buf.WriteString("M")
			buf.WriteString(svgValue(points[0].X))
			buf.WriteString(" ")
			buf.WriteString(svgValue(points[0].Y))
			for j := 1; j < len(points); j++ {
				buf.WriteString("L")
				buf.WriteString(svgValue(points[j].X))
				buf.WriteString(" ")
				buf.WriteString(svgValue(points[j].Y))
			}
			buf.WriteString("Z")
			_ = buf.String()
		}
	})

	b.Run("reused_builder", func(b *testing.B) {
		var buf strings.Builder
		buf.Grow(100) // Pre-allocate larger buffer
		b.ResetTimer()
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			buf.Reset()

			// Build polygon path
			buf.WriteString("M")
			buf.WriteString(svgValue(points[0].X))
			buf.WriteString(" ")
			buf.WriteString(svgValue(points[0].Y))
			for j := 1; j < len(points); j++ {
				buf.WriteString("L")
				buf.WriteString(svgValue(points[j].X))
				buf.WriteString(" ")
				buf.WriteString(svgValue(points[j].Y))
			}
			buf.WriteString("Z")
			_ = buf.String()
		}
	})
}

// ============================================================================
// SVG RENDERER MICRO-BENCHMARKS
// ============================================================================

// BenchmarkSVGRendererOperations tests SVG renderer creation and operations
func BenchmarkSVGRendererOperations(b *testing.B) {
	sizes := benchTestSizes
	colors := benchTestColors

	b.Run("NewSVGRenderer", func(b *testing.B) {
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			size := sizes[i%len(sizes)]
			_ = NewSVGRenderer(size)
		}
	})

	b.Run("BeginShape", func(b *testing.B) {
		renderer := NewSVGRenderer(64)
		b.ResetTimer()
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			color := colors[i%len(colors)]
			renderer.BeginShape(color)
		}
	})

	b.Run("AddPolygon", func(b *testing.B) {
		renderer := NewSVGRenderer(64)
		renderer.BeginShape(colors[0])
		b.ResetTimer()
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			renderer.AddPolygon(benchTestPoints)
		}
	})

	b.Run("AddCircle", func(b *testing.B) {
		renderer := NewSVGRenderer(64)
		renderer.BeginShape(colors[0])
		topLeft := engine.Point{X: 5.0, Y: 5.0}
		b.ResetTimer()
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			renderer.AddCircle(topLeft, 10.0, false)
		}
	})
}

// BenchmarkSVGGeneration tests full SVG generation with different scenarios
func BenchmarkSVGGeneration(b *testing.B) {
	colors := benchTestColors[:3] // Use fewer colors for cleaner tests

	b.Run("empty_renderer", func(b *testing.B) {
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			renderer := NewSVGRenderer(64)
			_ = renderer.ToSVG()
		}
	})

	b.Run("single_shape", func(b *testing.B) {
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			renderer := NewSVGRenderer(64)
			renderer.BeginShape(colors[0])
			renderer.AddPolygon(benchTestPoints)
			renderer.EndShape()
			_ = renderer.ToSVG()
		}
	})

	b.Run("multiple_shapes", func(b *testing.B) {
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			renderer := NewSVGRenderer(64)
			for j, color := range colors {
				renderer.BeginShape(color)
				// Offset points for each shape
				offsetPoints := make([]engine.Point, len(benchTestPoints))
				for k, point := range benchTestPoints {
					offsetPoints[k] = engine.Point{
						X: point.X + float64(j*12),
						Y: point.Y + float64(j*12),
					}
				}
				renderer.AddPolygon(offsetPoints)
				renderer.EndShape()
			}
			_ = renderer.ToSVG()
		}
	})

	b.Run("with_background", func(b *testing.B) {
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			renderer := NewSVGRenderer(64)
			renderer.SetBackground("#ffffff", 1.0)
			renderer.BeginShape(colors[0])
			renderer.AddPolygon(benchTestPoints)
			renderer.EndShape()
			_ = renderer.ToSVG()
		}
	})
}

// BenchmarkSVGSizeEstimation tests SVG capacity estimation
func BenchmarkSVGSizeEstimation(b *testing.B) {
	colors := benchTestColors

	b.Run("capacity_estimation", func(b *testing.B) {
		renderer := NewSVGRenderer(64)
		for _, color := range colors {
			renderer.BeginShape(color)
			renderer.AddPolygon(benchTestPoints)
			renderer.EndShape()
		}

		b.ResetTimer()
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			// Simulate capacity estimation logic from ToSVG
			capacity := svgBaseOverheadBytes
			capacity += svgBackgroundRectBytes // Assume background

			// Estimate path data size
			for _, color := range renderer.colorOrder {
				path := renderer.pathsByColor[color]
				if path != nil {
					capacity += svgPathOverheadBytes + path.data.Len()
				}
			}
			_ = capacity
		}
	})

	b.Run("strings_builder_with_estimation", func(b *testing.B) {
		renderer := NewSVGRenderer(64)
		for _, color := range colors {
			renderer.BeginShape(color)
			renderer.AddPolygon(benchTestPoints)
			renderer.EndShape()
		}

		b.ResetTimer()
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			// Test strings.Builder with capacity estimation
			capacity := svgBaseOverheadBytes + svgBackgroundRectBytes
			for _, color := range renderer.colorOrder {
				path := renderer.pathsByColor[color]
				if path != nil {
					capacity += svgPathOverheadBytes + path.data.Len()
				}
			}

			var svg strings.Builder
			svg.Grow(capacity)
			svg.WriteString(`<svg xmlns="http://www.w3.org/2000/svg">`)
			svg.WriteString("</svg>")
			_ = svg.String()
		}
	})
}

// ============================================================================
// MAP OPERATIONS MICRO-BENCHMARKS
// ============================================================================

// BenchmarkMapOperations tests map operations used in renderer
func BenchmarkMapOperations(b *testing.B) {
	colors := benchTestColors

	b.Run("map_creation", func(b *testing.B) {
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			m := make(map[string]*SVGPath)
			_ = m
		}
	})

	b.Run("map_insertion", func(b *testing.B) {
		m := make(map[string]*SVGPath)
		b.ResetTimer()
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			color := colors[i%len(colors)]
			m[color] = &SVGPath{}
		}
	})

	b.Run("map_lookup", func(b *testing.B) {
		m := make(map[string]*SVGPath)
		for _, color := range colors {
			m[color] = &SVGPath{}
		}
		b.ResetTimer()
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			color := colors[i%len(colors)]
			_ = m[color]
		}
	})

	b.Run("map_existence_check", func(b *testing.B) {
		m := make(map[string]*SVGPath)
		for _, color := range colors {
			m[color] = &SVGPath{}
		}
		b.ResetTimer()
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			color := colors[i%len(colors)]
			_, exists := m[color]
			_ = exists
		}
	})
}

// ============================================================================
// SLICE OPERATIONS MICRO-BENCHMARKS
// ============================================================================

// BenchmarkSliceOperations tests slice operations for color ordering
func BenchmarkSliceOperations(b *testing.B) {
	colors := benchTestColors

	b.Run("slice_append", func(b *testing.B) {
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			var colorOrder []string
			//lint:ignore S1011 Intentionally benchmarking individual appends vs batch
			//nolint:gosimple // Intentionally benchmarking individual appends vs batch
			for _, color := range colors {
				colorOrder = append(colorOrder, color)
			}
			_ = colorOrder
		}
	})

	b.Run("slice_with_capacity", func(b *testing.B) {
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			colorOrder := make([]string, 0, len(colors))
			//lint:ignore S1011 Intentionally benchmarking individual appends with pre-allocation
			//nolint:gosimple // Intentionally benchmarking individual appends with pre-allocation
			for _, color := range colors {
				colorOrder = append(colorOrder, color)
			}
			_ = colorOrder
		}
	})

	b.Run("slice_iteration", func(b *testing.B) {
		colorOrder := make([]string, len(colors))
		copy(colorOrder, colors)
		b.ResetTimer()
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			for _, color := range colorOrder {
				_ = color
			}
		}
	})
}

// ============================================================================
// COORDINATE TRANSFORMATION MICRO-BENCHMARKS
// ============================================================================

// BenchmarkCoordinateTransforms tests coordinate transformation patterns
func BenchmarkCoordinateTransforms(b *testing.B) {
	points := benchTestPoints

	b.Run("point_creation", func(b *testing.B) {
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			_ = engine.Point{X: 10.5, Y: 20.5}
		}
	})

	b.Run("point_slice_creation", func(b *testing.B) {
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			pointsCopy := make([]engine.Point, len(points))
			copy(pointsCopy, points)
			_ = pointsCopy
		}
	})

	b.Run("point_transformation", func(b *testing.B) {
		transform := func(x, y float64) (float64, float64) {
			return x * 2.0, y * 2.0
		}
		b.ResetTimer()
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			transformedPoints := make([]engine.Point, len(points))
			for j, point := range points {
				newX, newY := transform(point.X, point.Y)
				transformedPoints[j] = engine.Point{X: newX, Y: newY}
			}
			_ = transformedPoints
		}
	})
}

// ============================================================================
// MEMORY ALLOCATION PATTERN COMPARISONS
// ============================================================================

// BenchmarkAllocationPatterns compares different allocation patterns used in rendering
func BenchmarkAllocationPatterns(b *testing.B) {
	b.Run("string_concatenation", func(b *testing.B) {
		base := "test"
		suffix := "value"
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			_ = base + suffix
		}
	})

	b.Run("sprintf_formatting", func(b *testing.B) {
		value := 10.5
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			_ = strconv.FormatFloat(value, 'f', 1, 64)
		}
	})

	b.Run("builder_small_capacity", func(b *testing.B) {
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			var buf strings.Builder
			buf.Grow(10)
			buf.WriteString("test")
			buf.WriteString("value")
			_ = buf.String()
		}
	})

	b.Run("builder_large_capacity", func(b *testing.B) {
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			var buf strings.Builder
			buf.Grow(100)
			buf.WriteString("test")
			buf.WriteString("value")
			_ = buf.String()
		}
	})
}
