package engine

import (
	"context"
	"fmt"
	"testing"

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

var benchmarkHashes = []string{
	"7c4a8d09ca3762af61e59520943dc26494f8941b", // test-hash
	"b36d9b6a07d0b5bfb7e0e77a7f8d1e5e6f7a8b9c", // example1@gmail.com
	"a9d8e7f6c5b4a3d2e1f0e9d8c7b6a5d4e3f2a1b0", // example2@yahoo.com
	"1234567890abcdef1234567890abcdef12345678",
	"fedcba0987654321fedcba0987654321fedcba09",
	"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
	"0000000000000000000000000000000000000000",
	"ffffffffffffffffffffffffffffffffffffffffffff",
}

var benchmarkSizesFloat = []float64{
	16.0, 32.0, 64.0, 128.0, 256.0, 512.0,
}

// Benchmark core generator creation
func BenchmarkNewGeneratorWithConfig(b *testing.B) {
	config := GeneratorConfig{
		ColorConfig: DefaultColorConfig(),
		CacheSize:   1000,
	}

	b.ResetTimer()
	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		generator, err := NewGeneratorWithConfig(config)
		if err != nil {
			b.Fatalf("NewGeneratorWithConfig failed: %v", err)
		}
		_ = generator
	}
}

// Benchmark icon generation without cache (per size)
func BenchmarkGenerateWithoutCachePerSize(b *testing.B) {
	config := GeneratorConfig{
		ColorConfig: DefaultColorConfig(),
		CacheSize:   1000,
	}
	generator, err := NewGeneratorWithConfig(config)
	if err != nil {
		b.Fatalf("NewGeneratorWithConfig failed: %v", err)
	}

	for _, size := range benchmarkSizesFloat {
		b.Run(fmt.Sprintf("size-%.0f", size), func(b *testing.B) {
			b.ReportAllocs()

			for i := 0; i < b.N; i++ {
				hash := benchmarkHashes[i%len(benchmarkHashes)]
				_, err := generator.GenerateWithoutCache(context.Background(), hash, size)
				if err != nil {
					b.Fatalf("GenerateWithoutCache failed: %v", err)
				}
			}
		})
	}
}

// Benchmark icon generation with cache (different from generator_test.go)
func BenchmarkGenerateWithCacheHeavy(b *testing.B) {
	config := GeneratorConfig{
		ColorConfig: DefaultColorConfig(),
		CacheSize:   100,
	}
	generator, err := NewGeneratorWithConfig(config)
	if err != nil {
		b.Fatalf("NewGeneratorWithConfig failed: %v", err)
	}

	b.ResetTimer()
	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		// Use limited set of hashes to test cache hits
		hash := benchmarkHashes[i%3] // Only use first 3 hashes
		size := 64.0
		_, err := generator.Generate(context.Background(), hash, size)
		if err != nil {
			b.Fatalf("Generate failed: %v", err)
		}
	}
}

// Benchmark hash parsing functions
func BenchmarkParseHex(b *testing.B) {
	hash := "7c4a8d09ca3762af61e59520943dc26494f8941b"

	b.Run("offset2_len1", func(b *testing.B) {
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			_, _ = util.ParseHex(hash, 2, 1)
		}
	})

	b.Run("offset4_len1", func(b *testing.B) {
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			_, _ = util.ParseHex(hash, 4, 1)
		}
	})

	b.Run("offset1_len1", func(b *testing.B) {
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			_, _ = util.ParseHex(hash, 1, 1)
		}
	})

	b.Run("offset8_len3", func(b *testing.B) {
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			_, _ = util.ParseHex(hash, 8, 3)
		}
	})
}

// Benchmark hue extraction
func BenchmarkExtractHue(b *testing.B) {
	config := GeneratorConfig{
		ColorConfig: DefaultColorConfig(),
		CacheSize:   1,
	}
	generator, err := NewGeneratorWithConfig(config)
	if err != nil {
		b.Fatalf("NewGeneratorWithConfig failed: %v", err)
	}

	b.ResetTimer()
	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		hash := benchmarkHashes[i%len(benchmarkHashes)]
		_, _ = generator.extractHue(hash)
	}
}

// Benchmark shape selection
func BenchmarkShapeSelection(b *testing.B) {
	hash := "7c4a8d09ca3762af61e59520943dc26494f8941b"

	b.ResetTimer()
	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		// Simulate shape selection process using util.ParseHex
		sideShapeIndex, _ := util.ParseHex(hash, hashPosSideShape, 1)
		cornerShapeIndex, _ := util.ParseHex(hash, hashPosCornerShape, 1)
		centerShapeIndex, _ := util.ParseHex(hash, hashPosCenterShape, 1)

		// Use modulo with arbitrary shape counts (simulating actual shape arrays)
		sideShapeIndex = sideShapeIndex % 16 // Assume 16 outer shapes
		cornerShapeIndex = cornerShapeIndex % 16
		centerShapeIndex = centerShapeIndex % 8 // Assume 8 center shapes

		_, _, _ = sideShapeIndex, cornerShapeIndex, centerShapeIndex
	}
}

// Benchmark color theme generation
func BenchmarkGenerateColorTheme(b *testing.B) {
	config := DefaultColorConfig()
	generator, err := NewGeneratorWithConfig(GeneratorConfig{
		ColorConfig: config,
		CacheSize:   1,
	})
	if err != nil {
		b.Fatalf("NewGeneratorWithConfig failed: %v", err)
	}

	b.ResetTimer()
	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		hash := benchmarkHashes[i%len(benchmarkHashes)]
		hue, _ := generator.extractHue(hash)
		_ = GenerateColorTheme(hue, config)
	}
}

// Benchmark position computation
func BenchmarkComputePositions(b *testing.B) {
	b.ResetTimer()
	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		// Test both side and corner positions
		_ = getSidePositions()
		_ = getCornerPositions()
	}
}

// Benchmark transform applications
func BenchmarkTransformApplication(b *testing.B) {
	transform := Transform{
		x:        1.0,
		y:        2.0,
		size:     64.0,
		rotation: 1,
	}

	b.Run("center_point", func(b *testing.B) {
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			_ = transform.TransformIconPoint(0.5, 0.5, 0, 0)
		}
	})

	b.Run("corner_point", func(b *testing.B) {
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			_ = transform.TransformIconPoint(1.0, 1.0, 0, 0)
		}
	})

	b.Run("origin_point", func(b *testing.B) {
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			_ = transform.TransformIconPoint(0.0, 0.0, 0, 0)
		}
	})
}

// Benchmark icon size calculations
func BenchmarkIconSizeCalculations(b *testing.B) {
	sizes := benchmarkSizesFloat
	padding := 0.1

	b.ResetTimer()
	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		size := sizes[i%len(sizes)]
		// Simulate size calculations from generator
		paddingPixels := size * padding * paddingMultiple
		iconSize := size - paddingPixels
		cellSize := iconSize / gridSize

		_, _, _ = paddingPixels, iconSize, cellSize
	}
}

// Benchmark cache key generation
func BenchmarkCacheKeyGeneration(b *testing.B) {
	b.ResetTimer()
	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		hash := benchmarkHashes[i%len(benchmarkHashes)]
		size := benchmarkSizesFloat[i%len(benchmarkSizesFloat)]
		_ = benchmarkCacheKey(hash, size)
	}
}

// Helper function to simulate cache key generation
func benchmarkCacheKey(hash string, size float64) string {
	return hash + ":" + fmt.Sprintf("%.0f", size)
}

// Benchmark full icon generation pipeline
func BenchmarkFullGenerationPipeline(b *testing.B) {
	config := GeneratorConfig{
		ColorConfig: DefaultColorConfig(),
		CacheSize:   1, // Minimal cache to avoid cache hits
	}
	generator, err := NewGeneratorWithConfig(config)
	if err != nil {
		b.Fatalf("NewGeneratorWithConfig failed: %v", err)
	}

	b.ResetTimer()
	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		hash := benchmarkHashes[i%len(benchmarkHashes)]
		size := 64.0

		// This tests the full pipeline: hash parsing, color generation,
		// shape selection, positioning, and rendering preparation
		_, err := generator.GenerateWithoutCache(context.Background(), hash, size)
		if err != nil {
			b.Fatalf("GenerateWithoutCache failed: %v", err)
		}
	}
}

// Benchmark different grid sizes (theoretical)
func BenchmarkGridSizeCalculations(b *testing.B) {
	sizes := benchmarkSizesFloat

	b.ResetTimer()
	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		size := sizes[i%len(sizes)]
		padding := 0.1

		// Test calculations for different theoretical grid sizes
		for gridSizeTest := 3; gridSizeTest <= 6; gridSizeTest++ {
			paddingPixels := size * padding * paddingMultiple
			iconSize := size - paddingPixels
			cellSize := iconSize / float64(gridSizeTest)
			_ = cellSize
		}
	}
}

// Benchmark color conflict resolution
func BenchmarkColorConflictResolution(b *testing.B) {
	config := DefaultColorConfig()
	generator, err := NewGeneratorWithConfig(GeneratorConfig{
		ColorConfig: config,
		CacheSize:   1,
	})
	if err != nil {
		b.Fatalf("NewGeneratorWithConfig failed: %v", err)
	}

	b.ResetTimer()
	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		hash := benchmarkHashes[i%len(benchmarkHashes)]
		hue, _ := generator.extractHue(hash)
		colorTheme := GenerateColorTheme(hue, config)

		// Simulate color conflict resolution
		for j := 0; j < 5; j++ {
			colorHash, _ := util.ParseHex(hash, hashPosColorStart+j%3, 1)
			selectedColor := colorTheme[colorHash%len(colorTheme)]
			_ = selectedColor
		}
	}
}

// Helper function to get side positions (matching generator logic)
func getSidePositions() [][]int {
	return [][]int{{1, 0}, {2, 0}, {2, 3}, {1, 3}, {0, 1}, {3, 1}, {3, 2}, {0, 2}}
}

// Helper function to get corner positions (matching generator logic)
func getCornerPositions() [][]int {
	return [][]int{{0, 0}, {3, 0}, {3, 3}, {0, 3}}
}

// Benchmark concurrent icon generation for high-traffic scenarios
func BenchmarkGenerateWithoutCacheParallel(b *testing.B) {
	config := GeneratorConfig{
		ColorConfig: DefaultColorConfig(),
		CacheSize:   1, // Minimal cache to avoid cache effects
	}
	generator, err := NewGeneratorWithConfig(config)
	if err != nil {
		b.Fatalf("NewGeneratorWithConfig failed: %v", err)
	}

	for _, size := range []float64{64.0, 128.0, 256.0} {
		b.Run(fmt.Sprintf("size-%.0f", size), func(b *testing.B) {
			b.ReportAllocs()

			b.RunParallel(func(pb *testing.PB) {
				i := 0
				for pb.Next() {
					hash := benchmarkHashes[i%len(benchmarkHashes)]
					_, err := generator.GenerateWithoutCache(context.Background(), hash, size)
					if err != nil {
						b.Errorf("GenerateWithoutCache failed: %v", err)
					}
					i++
				}
			})
		})
	}
}

// Benchmark concurrent cached generation
func BenchmarkGenerateWithCacheParallel(b *testing.B) {
	config := GeneratorConfig{
		ColorConfig: DefaultColorConfig(),
		CacheSize:   100, // Shared cache for concurrent access
	}
	generator, err := NewGeneratorWithConfig(config)
	if err != nil {
		b.Fatalf("NewGeneratorWithConfig failed: %v", err)
	}

	b.ReportAllocs()

	b.RunParallel(func(pb *testing.PB) {
		i := 0
		for pb.Next() {
			// Use limited set of hashes to test cache hits under concurrency
			hash := benchmarkHashes[i%3] // Only use first 3 hashes
			size := 64.0
			_, err := generator.Generate(context.Background(), hash, size)
			if err != nil {
				b.Errorf("Generate failed: %v", err)
			}
			i++
		}
	})
}
