// Package lua 提供 Lua 脚本嵌入能力
package lua

import (
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestSharedDictGetSet(t *testing.T) {
	dict := NewSharedDict("test", 100)

	// Set
	ok, err := dict.Set("key1", "value1", 0)
	require.NoError(t, err)
	assert.True(t, ok)

	// Get
	value, expired, err := dict.Get("key1")
	require.NoError(t, err)
	assert.False(t, expired)
	assert.Equal(t, "value1", value)

	// 不存在的 key
	value, expired, err = dict.Get("notexist")
	require.NoError(t, err)
	assert.False(t, expired)
	assert.Equal(t, "", value)
}

func TestSharedDictAdd(t *testing.T) {
	dict := NewSharedDict("test", 100)

	// Add 新 key
	ok, err := dict.Add("key1", "value1", 0)
	require.NoError(t, err)
	assert.True(t, ok)

	// Add 已存在的 key
	ok, err = dict.Add("key1", "value2", 0)
	require.NoError(t, err)
	assert.False(t, ok) // 已存在，返回 false

	// 验证值未被修改
	value, _, _ := dict.Get("key1")
	assert.Equal(t, "value1", value)
}

func TestSharedDictIncr(t *testing.T) {
	dict := NewSharedDict("test", 100)

	// Incr 不存在的 key，从 0 开始
	newValue, err := dict.Incr("counter", 5)
	require.NoError(t, err)
	assert.Equal(t, 5, newValue)

	// Incr 已存在的 key
	newValue, err = dict.Incr("counter", 3)
	require.NoError(t, err)
	assert.Equal(t, 8, newValue)

	// 轮减
	newValue, err = dict.Incr("counter", -2)
	require.NoError(t, err)
	assert.Equal(t, 6, newValue)
}

func TestSharedDictDelete(t *testing.T) {
	dict := NewSharedDict("test", 100)

	dict.Set("key1", "value1", 0)
	dict.Delete("key1")

	value, _, _ := dict.Get("key1")
	assert.Equal(t, "", value)

	// 删除不存在的 key 不会报错
	dict.Delete("notexist")
}

func TestSharedDictTTL(t *testing.T) {
	dict := NewSharedDict("test", 100)

	// Set 带 TTL
	ok, err := dict.Set("key1", "value1", 100*time.Millisecond)
	require.NoError(t, err)
	assert.True(t, ok)

	// 立即获取应该成功
	value, expired, err := dict.Get("key1")
	require.NoError(t, err)
	assert.False(t, expired)
	assert.Equal(t, "value1", value)

	// 等待过期
	time.Sleep(150 * time.Millisecond)

	// 过期后获取
	value, expired, err = dict.Get("key1")
	require.NoError(t, err)
	assert.True(t, expired)
	assert.Equal(t, "", value)
}

func TestSharedDictLRUEviction(t *testing.T) {
	dict := NewSharedDict("test", 3) // 只有 3 个容量

	// 添加 3 个条目
	dict.Set("key1", "value1", 0)
	dict.Set("key2", "value2", 0)
	dict.Set("key3", "value3", 0)

	// 使用 key1，使其成为最近使用
	dict.Get("key1")

	// 添加第 4 个条目，应该淘汰 key2（最久未使用）
	ok, err := dict.Set("key4", "value4", 0)
	require.NoError(t, err)
	assert.True(t, ok)

	// key1 应该还在
	value, _, _ := dict.Get("key1")
	assert.Equal(t, "value1", value)

	// key2 应该被淘汰
	value, _, _ = dict.Get("key2")
	assert.Equal(t, "", value)
}

func TestSharedDictFlushAll(t *testing.T) {
	dict := NewSharedDict("test", 100)

	dict.Set("key1", "value1", 0)
	dict.Set("key2", "value2", 0)

	dict.FlushAll()

	assert.Equal(t, 0, dict.Size())
}

func TestSharedDictFlushExpired(t *testing.T) {
	dict := NewSharedDict("test", 100)

	dict.Set("key1", "value1", 100*time.Millisecond)
	dict.Set("key2", "value2", 100*time.Millisecond)
	dict.Set("key3", "value3", 0) // 不过期

	// 立即清除应该返回 0
	count := dict.FlushExpired()
	assert.Equal(t, 0, count)

	// 等待过期
	time.Sleep(150 * time.Millisecond)

	count = dict.FlushExpired()
	assert.Equal(t, 2, count)

	// key3 应该还在
	assert.Equal(t, 1, dict.Size())
	value, _, _ := dict.Get("key3")
	assert.Equal(t, "value3", value)
}

func TestSharedDictSize(t *testing.T) {
	dict := NewSharedDict("test", 100)

	assert.Equal(t, 0, dict.Size())
	assert.Equal(t, 100, dict.FreeSlots())

	dict.Set("key1", "value1", 0)
	assert.Equal(t, 1, dict.Size())
	assert.Equal(t, 99, dict.FreeSlots())
}

func TestSharedDictManager(t *testing.T) {
	manager := NewSharedDictManager()

	// 创建字典
	dict1 := manager.CreateDict("dict1", 100)
	require.NotNil(t, dict1)

	// 再次获取同一个字典
	dict1Again := manager.GetDict("dict1")
	assert.Equal(t, dict1, dict1Again)

	// 创建另一个字典
	dict2 := manager.CreateDict("dict2", 200)
	require.NotNil(t, dict2)

	// 获取不存在的字典
	notexist := manager.GetDict("notexist")
	assert.Nil(t, notexist)

	// 关闭
	manager.Close()
}

func TestSharedDictLuaAPI(t *testing.T) {
	engine, err := NewEngine(DefaultConfig())
	require.NoError(t, err)
	defer engine.Close()

	// 创建共享字典（通过 Lua API 测试，此处仅为初始化）
	_ = engine.CreateSharedDict("mydict", 100)

	// 测试 Lua 脚本
	L := engine.GetLStateForTest()

	// 手动注册 ngx.shared API（用于测试）
	ngx := L.NewTable()
	L.SetGlobal("ngx", ngx)
	RegisterSharedDictAPI(L, engine.SharedDictManager(), ngx)

	// 运行 Lua 脚本测试
	err = L.DoString(`
		local dict = ngx.shared.DICT("mydict")

		-- 测试 set/get
		dict:set("key1", "value1")
		local val, flags = dict:get("key1")
		assert(val == "value1")

		-- 测试 add
		local ok, err = dict:add("key2", "value2")
		assert(ok == true)

		-- 测试 add 已存在的 key
		ok, err = dict:add("key2", "value3")
		assert(ok == false)
		assert(err == "exists")

		-- 测试 incr
		local new_val, err = dict:incr("counter", 10)
		assert(new_val == 10)

		new_val, err = dict:incr("counter", 5)
		assert(new_val == 15)

		-- 测试 size
		local size = dict:size()
		assert(size >= 3)

		-- 测试 delete
		dict:delete("key1")
		local val, err = dict:get("key1")
		assert(val == nil)
	`)
	require.NoError(t, err)
}
