package test

import (
	"runtime"
	"sync"
	"testing"

	"github.com/AlexsanderHamir/GenPool/pool"
)

// BenchmarkObject is a simple struct we'll use for benchmarking.
type BenchmarkObject struct {
	// user fields
	Name   string   // 16 bytes (pointer + length)
	Data   []byte   // 24 bytes (pointer + len + cap)
	Result int64    // 8 bytes - store computation result
	_      [16]byte // 16 bytes padding = 64 bytes total

	pool.Fields[BenchmarkObject]
}

func cpuIntensiveWorkload(obj *BenchmarkObject) {
	obj.Name = "cpu_test"

	// Heavier CPU work
	var result int64
	for i := range 10_000 {
		result += int64(i * i * i)
		result ^= int64(i << 3)
		if i%1000 == 0 {
			result = result*31 + int64(i)
		}
	}
	obj.Result = result

	// Minimal allocation - just set a small data payload
	if cap(obj.Data) < 100 {
		obj.Data = make([]byte, 0, 100)
	}
	obj.Data = obj.Data[:0]

	// Add some derived data
	for i := range 100 {
		obj.Data = append(obj.Data, byte(result>>uint(i%8)))
	}
}

// Helper functions for benchmarks.
func allocator() *BenchmarkObject {
	return &BenchmarkObject{Name: "test"}
}

func cleaner(obj *BenchmarkObject) {
	obj.Name = ""
	obj.Data = obj.Data[:0]
}

func BenchmarkGenPool(b *testing.B) {
	runtime.SetBlockProfileRate(1)
	cfg := pool.Config[BenchmarkObject, *BenchmarkObject]{
		Allocator: allocator,
		Cleaner:   cleaner,
	}

	p, err := pool.NewPoolWithConfig(cfg)
	if err != nil {
		b.Fatalf("error creating pool: %v", err)
	}

	defer p.Close()

	b.SetParallelism(1000)
	b.ResetTimer()
	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			obj := p.Get()
			p.Put(obj)
		}
	})
}

func BenchmarkSyncPool(b *testing.B) {
	runtime.SetBlockProfileRate(1)
	p := &sync.Pool{
		New: func() any {
			return allocator()
		},
	}

	b.SetParallelism(1000)
	b.ResetTimer()
	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			obj := p.Get().(*BenchmarkObject)

			obj.Name = ""
			obj.Data = obj.Data[:0]

			p.Put(obj)
		}
	})
}
