// Package cache 提供文件缓存分配热点追踪测试。
//
// 该文件追踪 FileCache.Set 的分配来源，验证池化优化效果。
//
// 测试场景：
//   - SetNew: 新建条目路径
//   - SetUpdate: 更新已存在条目路径
//   - SetEviction: 淘汰场景（缓存满载）
//
// 目标：池化后 ≤1 allocs/op
//
// 作者：xfy
package cache

import (
	"container/list"
	"fmt"
	"sync"
	"testing"
	"time"
)

// BenchmarkFileCacheSetAllocation_New 测试新建条目路径的分配。
//
// 热点分配来源：
//   - entry := c.entryPool.Get()  -> 池化后 0 allocs
//   - c.lruList.PushFront(entry)  -> list Element 分配
//   - c.entries[path] = entry     -> map 写入
func BenchmarkFileCacheSetAllocation_New(b *testing.B) {
	fc := NewFileCache(10000, 0, 1*time.Hour)

	data := []byte("test data content for benchmark")
	size := int64(len(data))

	b.ReportAllocs()
	b.ResetTimer()

	for i := 0; b.Loop(); i++ {
		path := fmt.Sprintf("/new/file%d.txt", i)
		fc.Set(path, data, size, time.Now(), "text/plain")
	}
}

// BenchmarkFileCacheSetAllocation_Update 测试更新已存在条目路径的分配。
//
// 更新路径理论上 0 allocs（不涉及新条目分配）。
func BenchmarkFileCacheSetAllocation_Update(b *testing.B) {
	fc := NewFileCache(10000, 0, 1*time.Hour)

	// 预填充缓存
	data := []byte("test data content for benchmark")
	size := int64(len(data))
	for i := range 1000 {
		path := fmt.Sprintf("/update/file%d.txt", i)
		fc.Set(path, data, size, time.Now(), "text/plain")
	}

	b.ReportAllocs()
	b.ResetTimer()

	for i := 0; b.Loop(); i++ {
		// 循环更新已有条目
		path := fmt.Sprintf("/update/file%d.txt", i%1000)
		fc.Set(path, data, size, time.Now(), "text/plain")
	}
}

// BenchmarkFileCacheSetAllocation_Eviction 测试淘汰场景的分配。
//
// 热点：
//   - 淘汰时 removeEntry -> entryPool.Put(entry)
//   - 新 Set 时 entryPool.Get() 复用
//   - 理论上 LRU Element 分配仍是热点
func BenchmarkFileCacheSetAllocation_Eviction(b *testing.B) {
	// 容量限制为 100，强制触发淘汰
	fc := NewFileCache(100, 0, 1*time.Hour)

	// 预填充到容量上限
	data := []byte("test data content for benchmark")
	size := int64(len(data))
	for i := range 100 {
		path := fmt.Sprintf("/evict/file%d.txt", i)
		fc.Set(path, data, size, time.Now(), "text/plain")
	}

	b.ReportAllocs()
	b.ResetTimer()

	for i := 0; b.Loop(); i++ {
		// 每个 Set 都触发淘汰
		path := fmt.Sprintf("/evict/new%d.txt", i)
		fc.Set(path, data, size, time.Now(), "text/plain")
	}
}

// BenchmarkFileCacheSetAllocation_EvictionWithPool 测试淘汰+池化复用的分配。
//
// 验证 entryPool 在淘汰场景的复用效果。
func BenchmarkFileCacheSetAllocation_EvictionWithPool(b *testing.B) {
	fc := NewFileCache(100, 0, 1*time.Hour)

	data := []byte("test data content for benchmark")
	size := int64(len(data))

	// 预填充
	for i := range 100 {
		path := fmt.Sprintf("/pool/file%d.txt", i)
		fc.Set(path, data, size, time.Now(), "text/plain")
	}

	b.ReportAllocs()
	b.ResetTimer()

	for i := 0; b.Loop(); i++ {
		path := fmt.Sprintf("/pool/new%d.txt", i)
		fc.Set(path, data, size, time.Now(), "text/plain")
	}
}

// BenchmarkFileCacheSetAllocation_MemoryLimit 测试内存限制淘汰的分配。
//
// 热点：evictIfNeeded 需遍历 LRU 链表淘汰多个条目。
func BenchmarkFileCacheSetAllocation_MemoryLimit(b *testing.B) {
	// 限制 1MB，每个条目 1KB，约 1000 条目
	fc := NewFileCache(0, 1024*1024, 1*time.Hour)

	data := make([]byte, 1024) // 1KB 每条目
	size := int64(len(data))

	// 预填充到接近上限
	for i := range 900 {
		path := fmt.Sprintf("/mem/file%d.txt", i)
		fc.Set(path, data, size, time.Now(), "text/plain")
	}

	b.ReportAllocs()
	b.ResetTimer()

	for i := 0; b.Loop(); i++ {
		path := fmt.Sprintf("/mem/new%d.txt", i)
		fc.Set(path, data, size, time.Now(), "text/plain")
	}
}

// BenchmarkFileCacheSetAllocation_Concurrent 测试并发 Set 的分配。
//
// 并发场景下锁竞争可能影响池化效果。
func BenchmarkFileCacheSetAllocation_Concurrent(b *testing.B) {
	fc := NewFileCache(10000, 0, 1*time.Hour)

	data := []byte("concurrent test data")
	size := int64(len(data))

	b.ReportAllocs()
	b.ResetTimer()

	b.RunParallel(func(pb *testing.PB) {
		i := 0
		for pb.Next() {
			path := fmt.Sprintf("/conc/file%d.txt", i)
			fc.Set(path, data, size, time.Now(), "text/plain")
			i++
		}
	})
}

// BenchmarkFileCacheSetAllocation_ConcurrentEviction 测试并发淘汰场景。
func BenchmarkFileCacheSetAllocation_ConcurrentEviction(b *testing.B) {
	fc := NewFileCache(100, 0, 1*time.Hour)

	data := []byte("eviction concurrent")
	size := int64(len(data))

	// 预填充
	for i := range 100 {
		path := fmt.Sprintf("/concevict/file%d.txt", i)
		fc.Set(path, data, size, time.Now(), "text/plain")
	}

	b.ReportAllocs()
	b.ResetTimer()

	b.RunParallel(func(pb *testing.PB) {
		i := 0
		for pb.Next() {
			path := fmt.Sprintf("/concevict/new%d.txt", i)
			fc.Set(path, data, size, time.Now(), "text/plain")
			i++
		}
	})
}

// BenchmarkFileCacheEntryPool_GetPut 测试 entryPool 直接操作。
//
// 验证 sync.Pool 的分配效果（应 0 allocs）。
func BenchmarkFileCacheEntryPool_GetPut(b *testing.B) {
	pool := &sync.Pool{
		New: func() any {
			return &FileEntry{}
		},
	}

	// 预填充池
	for range 100 {
		pool.Put(&FileEntry{})
	}

	b.ReportAllocs()
	b.ResetTimer()

	for b.Loop() {
		entry := pool.Get().(*FileEntry)
		// 模拟使用
		entry.Path = "/test"
		entry.Data = nil
		pool.Put(entry)
	}
}

// BenchmarkFileCacheLRUList_PushFront 测试 LRU 链表操作分配。
//
// 热点：list.PushFront 分配 Element（约 1 allocs）。
func BenchmarkFileCacheLRUList_PushFront(b *testing.B) {
	lruList := list.New()

	b.ReportAllocs()
	b.ResetTimer()

	for i := 0; b.Loop(); i++ {
		entry := &FileEntry{Path: fmt.Sprintf("/lru%d", i)}
		lruList.PushFront(entry)
		// 模拟淘汰移除
		if lruList.Len() > 100 {
			old := lruList.Back()
			lruList.Remove(old)
		}
	}
}
