package cache_test

import (
	"context"
	"sync"
	"sync/atomic"
	"testing"
	"time"

	"github.com/user-management-system/internal/cache"
)

// TestRedisCache_Disabled 测试禁用状态的RedisCache不报错
func TestRedisCache_Disabled(t *testing.T) {
	c := cache.NewRedisCache(false)
	ctx := context.Background()

	if err := c.Set(ctx, "key", "value", time.Minute); err != nil {
		t.Errorf("disabled cache Set should not error: %v", err)
	}
	val, err := c.Get(ctx, "key")
	if err != nil {
		t.Errorf("disabled cache Get should not error: %v", err)
	}
	if val != nil {
		t.Errorf("disabled cache Get should return nil, got: %v", val)
	}
	if err := c.Delete(ctx, "key"); err != nil {
		t.Errorf("disabled cache Delete should not error: %v", err)
	}
	exists, err := c.Exists(ctx, "key")
	if err != nil {
		t.Errorf("disabled cache Exists should not error: %v", err)
	}
	if exists {
		t.Error("disabled cache Exists should return false")
	}
	if err := c.Clear(ctx); err != nil {
		t.Errorf("disabled cache Clear should not error: %v", err)
	}
	if err := c.Close(); err != nil {
		t.Errorf("disabled cache Close should not error: %v", err)
	}
}

// TestL1Cache_SetGet 测试L1内存缓存的基本读写
func TestL1Cache_SetGet(t *testing.T) {
	l1 := cache.NewL1Cache()

	l1.Set("user:1", "alice", time.Minute)
	val, ok := l1.Get("user:1")
	if !ok {
		t.Fatal("L1 Get: expected hit")
	}
	if val != "alice" {
		t.Errorf("L1 Get value = %v, want alice", val)
	}
}

// TestL1Cache_Expiration 测试L1缓存过期
func TestL1Cache_Expiration(t *testing.T) {
	l1 := cache.NewL1Cache()

	l1.Set("expire:1", "v", 50*time.Millisecond)
	time.Sleep(100 * time.Millisecond)

	_, ok := l1.Get("expire:1")
	if ok {
		t.Error("L1 key should have expired")
	}
}

// TestL1Cache_Delete 测试L1缓存删除
func TestL1Cache_Delete(t *testing.T) {
	l1 := cache.NewL1Cache()

	l1.Set("del:1", "v", time.Minute)
	l1.Delete("del:1")

	_, ok := l1.Get("del:1")
	if ok {
		t.Error("L1 key should be deleted")
	}
}

// TestL1Cache_Clear 测试L1缓存清空
func TestL1Cache_Clear(t *testing.T) {
	l1 := cache.NewL1Cache()

	l1.Set("a", 1, time.Minute)
	l1.Set("b", 2, time.Minute)
	l1.Clear()

	_, ok1 := l1.Get("a")
	_, ok2 := l1.Get("b")
	if ok1 || ok2 {
		t.Error("L1 cache should be empty after Clear()")
	}
}

// TestL1Cache_Size 测试L1缓存大小统计
func TestL1Cache_Size(t *testing.T) {
	l1 := cache.NewL1Cache()

	l1.Set("s1", 1, time.Minute)
	l1.Set("s2", 2, time.Minute)
	l1.Set("s3", 3, time.Minute)

	if l1.Size() != 3 {
		t.Errorf("L1 Size = %d, want 3", l1.Size())
	}

	l1.Delete("s1")
	if l1.Size() != 2 {
		t.Errorf("L1 Size after Delete = %d, want 2", l1.Size())
	}
}

// TestL1Cache_Cleanup 测试L1过期键清理
func TestL1Cache_Cleanup(t *testing.T) {
	l1 := cache.NewL1Cache()

	l1.Set("exp", "v", 30*time.Millisecond)
	l1.Set("keep", "v", time.Minute)

	time.Sleep(60 * time.Millisecond)
	l1.Cleanup()

	if l1.Size() != 1 {
		t.Errorf("after Cleanup L1 Size = %d, want 1", l1.Size())
	}
}

// TestCacheManager_SetGet 测试CacheManager读写（仅L1）
func TestCacheManager_SetGet(t *testing.T) {
	l1 := cache.NewL1Cache()
	cm := cache.NewCacheManager(l1, nil)
	ctx := context.Background()

	if err := cm.Set(ctx, "k1", "v1", time.Minute, time.Minute); err != nil {
		t.Fatalf("CacheManager Set error: %v", err)
	}
	val, ok := cm.Get(ctx, "k1")
	if !ok {
		t.Fatal("CacheManager Get: expected hit")
	}
	if val != "v1" {
		t.Errorf("CacheManager Get value = %v, want v1", val)
	}
}

// TestCacheManager_Delete 测试CacheManager删除
func TestCacheManager_Delete(t *testing.T) {
	l1 := cache.NewL1Cache()
	cm := cache.NewCacheManager(l1, nil)
	ctx := context.Background()

	_ = cm.Set(ctx, "del:1", "v", time.Minute, time.Minute)
	if err := cm.Delete(ctx, "del:1"); err != nil {
		t.Fatalf("CacheManager Delete error: %v", err)
	}
	_, ok := cm.Get(ctx, "del:1")
	if ok {
		t.Error("CacheManager key should be deleted")
	}
}

// TestCacheManager_Exists 测试CacheManager存在性检查
func TestCacheManager_Exists(t *testing.T) {
	l1 := cache.NewL1Cache()
	cm := cache.NewCacheManager(l1, nil)
	ctx := context.Background()

	if cm.Exists(ctx, "notexist") {
		t.Error("CacheManager Exists should return false for missing key")
	}
	_ = cm.Set(ctx, "exist:1", "v", time.Minute, time.Minute)
	if !cm.Exists(ctx, "exist:1") {
		t.Error("CacheManager Exists should return true after Set")
	}
}

// TestCacheManager_Clear 测试CacheManager清空
func TestCacheManager_Clear(t *testing.T) {
	l1 := cache.NewL1Cache()
	cm := cache.NewCacheManager(l1, nil)
	ctx := context.Background()

	_ = cm.Set(ctx, "a", 1, time.Minute, time.Minute)
	_ = cm.Set(ctx, "b", 2, time.Minute, time.Minute)

	if err := cm.Clear(ctx); err != nil {
		t.Fatalf("CacheManager Clear error: %v", err)
	}
	if cm.Exists(ctx, "a") || cm.Exists(ctx, "b") {
		t.Error("CacheManager should be empty after Clear()")
	}
}

// TestCacheManager_Concurrent 测试CacheManager并发安全
func TestCacheManager_Concurrent(t *testing.T) {
	l1 := cache.NewL1Cache()
	cm := cache.NewCacheManager(l1, nil)
	ctx := context.Background()

	var wg sync.WaitGroup
	var hitCount int64

	// 预热
	_ = cm.Set(ctx, "concurrent:key", "v", time.Minute, time.Minute)

	// 并发读写
	for i := 0; i < 50; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			for j := 0; j < 20; j++ {
				if _, ok := cm.Get(ctx, "concurrent:key"); ok {
					atomic.AddInt64(&hitCount, 1)
				}
			}
		}()
	}
	wg.Wait()

	if hitCount == 0 {
		t.Error("concurrent cache reads should produce hits")
	}
}

// TestCacheManager_WithDisabledL2 测试CacheManager配合禁用L2
func TestCacheManager_WithDisabledL2(t *testing.T) {
	l1 := cache.NewL1Cache()
	l2 := cache.NewRedisCache(false) // disabled
	cm := cache.NewCacheManager(l1, l2)
	ctx := context.Background()

	if err := cm.Set(ctx, "k", "v", time.Minute, time.Minute); err != nil {
		t.Fatalf("Set with disabled L2 should not error: %v", err)
	}
	val, ok := cm.Get(ctx, "k")
	if !ok || val != "v" {
		t.Errorf("Get from L1 after Set = (%v, %v), want (v, true)", val, ok)
	}
}
