// Package lua 提供 Scheduler 模式测试。
//
// 该文件测试 Lua Scheduler 模式的功能：
//   - setupSchedulerNgxAPI
//   - setSchedulerMode / IsSchedulerMode
//   - SchedulerUnsafeLocationAPI
//   - SchedulerUnsafeVarAPI
//   - SchedulerUnsafeCtxAPI
//
// 作者：xfy
package lua

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	glua "github.com/yuin/gopher-lua"
)

// TestSchedulerModeFlag 测试 Scheduler 模式标志。
func TestSchedulerModeFlag(t *testing.T) {
	L := glua.NewState()
	defer L.Close()

	// 初始状态不是 scheduler 模式
	if IsSchedulerMode(L) {
		t.Error("Initial state should not be scheduler mode")
	}

	// 设置 scheduler 模式
	setSchedulerMode(L, true)
	if !IsSchedulerMode(L) {
		t.Error("Should be in scheduler mode after setting")
	}

	// 关闭 scheduler 模式
	setSchedulerMode(L, false)
	if IsSchedulerMode(L) {
		t.Error("Should not be in scheduler mode after unsetting")
	}
}

// TestSetupSchedulerNgxAPI 测试 setupSchedulerNgxAPI 创建安全的 ngx API。
func TestSetupSchedulerNgxAPI(t *testing.T) {
	engine, err := NewEngine(DefaultConfig())
	require.NoError(t, err)
	defer engine.Close()

	L := glua.NewState()
	defer L.Close()

	// 调用 setupSchedulerNgxAPI
	setupSchedulerNgxAPI(L, engine)

	// 验证 ngx 表存在
	ngx := L.GetGlobal("ngx")
	if ngx == glua.LNil {
		t.Fatal("ngx table should exist")
	}

	ngxTable, ok := ngx.(*glua.LTable)
	if !ok {
		t.Fatal("ngx should be a table")
	}

	// 验证 scheduler 模式已设置
	if !IsSchedulerMode(L) {
		t.Error("Should be in scheduler mode after setupSchedulerNgxAPI")
	}

	// 验证 ngx.shared 存在
	shared := ngxTable.RawGetString("shared")
	if shared == glua.LNil {
		t.Error("ngx.shared should exist")
	}

	// 验证 ngx.log 存在
	log := ngxTable.RawGetString("log")
	if log == glua.LNil {
		t.Error("ngx.log should exist")
	}

	// 验证 ngx.timer 存在
	timer := ngxTable.RawGetString("timer")
	if timer == glua.LNil {
		t.Error("ngx.timer should exist")
	}
}

// TestSchedulerUnsafeLocationAPI 测试 Scheduler 模式下 ngx.location 不可用。
func TestSchedulerUnsafeLocationAPI(t *testing.T) {
	L := glua.NewState()
	defer L.Close()

	ngx := L.NewTable()
	L.SetGlobal("ngx", ngx)

	// 注册不安全的 location API
	RegisterSchedulerUnsafeLocationAPI(L, ngx)

	// 验证 ngx.location 存在
	location := ngx.RawGetString("location")
	if location == glua.LNil {
		t.Fatal("ngx.location should exist")
	}

	// 尝试调用 ngx.location.capture 应该返回错误
	err := L.DoString(`
		local ok, err = pcall(function()
			ngx.location.capture("/test")
		end)
		if ok then
			error("ngx.location.capture should fail in scheduler mode")
		end
	`)
	assert.NoError(t, err)
}

// TestSchedulerUnsafeVarAPI 测试 Scheduler 模式下 ngx.var 不可用。
func TestSchedulerUnsafeVarAPI(t *testing.T) {
	L := glua.NewState()
	defer L.Close()

	ngx := L.NewTable()
	L.SetGlobal("ngx", ngx)

	// 注册不安全的 var API
	RegisterSchedulerUnsafeVarAPI(L, ngx)

	// 验证 ngx.var 存在
	v := ngx.RawGetString("var")
	if v == glua.LNil {
		t.Fatal("ngx.var should exist")
	}

	// 尝试读取 ngx.var 应该返回错误
	err := L.DoString(`
		local ok, err = pcall(function()
			local x = ngx.var.some_var
		end)
		if ok then
			error("reading ngx.var should fail in scheduler mode")
		end
	`)
	assert.NoError(t, err)

	// 尝试写入 ngx.var 应该返回错误
	err = L.DoString(`
		local ok, err = pcall(function()
			ngx.var.some_var = "value"
		end)
		if ok then
			error("writing ngx.var should fail in scheduler mode")
		end
	`)
	assert.NoError(t, err)
}

// TestSchedulerUnsafeCtxAPI 测试 Scheduler 模式下 ngx.ctx 不可用。
func TestSchedulerUnsafeCtxAPISeparate(t *testing.T) {
	L := glua.NewState()
	defer L.Close()

	ngx := L.NewTable()
	L.SetGlobal("ngx", ngx)

	// 注册不安全的 ctx API
	RegisterSchedulerUnsafeCtxAPI(L, ngx)

	// 验证 ngx.ctx 存在
	ctx := ngx.RawGetString("ctx")
	if ctx == glua.LNil {
		t.Fatal("ngx.ctx should exist")
	}

	// 尝试读取 ngx.ctx 应该返回错误
	err := L.DoString(`
		local ok, err = pcall(function()
			local x = ngx.ctx.some_key
		end)
		if ok then
			error("reading ngx.ctx should fail in scheduler mode")
		end
	`)
	assert.NoError(t, err)

	// 尝试写入 ngx.ctx 应该返回错误
	err = L.DoString(`
		local ok, err = pcall(function()
			ngx.ctx.some_key = "value"
		end)
		if ok then
			error("writing ngx.ctx should fail in scheduler mode")
		end
	`)
	assert.NoError(t, err)
}

// TestSchedulerUnsafeLogAPI 测试 Scheduler 模式下 ngx.log 可用。
func TestSchedulerUnsafeLogAPI(t *testing.T) {
	L := glua.NewState()
	defer L.Close()

	ngx := L.NewTable()
	L.SetGlobal("ngx", ngx)

	// 注册 Scheduler 日志 API
	RegisterSchedulerLogAPI(L, ngx)

	// 验证 ngx.log 存在
	log := ngx.RawGetString("log")
	if log == glua.LNil {
		t.Fatal("ngx.log should exist")
	}

	// ngx.log 应该可以调用（不会报错）
	err := L.DoString(`
		ngx.log(ngx.ERR, "test log message")
	`)
	assert.NoError(t, err)
}

// TestSchedulerUnsafeReqAPI 测试 Scheduler 模式下 ngx.req 不可用。
func TestSchedulerUnsafeReqAPISeparate(t *testing.T) {
	L := glua.NewState()
	defer L.Close()

	ngx := L.NewTable()
	L.SetGlobal("ngx", ngx)

	// 注册不安全的 req API
	RegisterSchedulerUnsafeReqAPI(L, ngx)

	// 验证 ngx["ngx.req"] 存在（RegisterUnsafeAPI 使用完整名称作为键）
	req := ngx.RawGetString("ngx.req")
	if req == glua.LNil {
		t.Fatal("ngx[\"ngx.req\"] should exist")
	}

	// 尝试调用 ngx.req.get_method 应该返回错误
	err := L.DoString(`
		local ok, err = pcall(function()
			ngx["ngx.req"].get_method()
		end)
		if ok then
			error("ngx.req.get_method should fail in scheduler mode")
		end
	`)
	assert.NoError(t, err)
}

// TestSchedulerUnsafeRespAPI 测试 Scheduler 模式下 ngx.resp 不可用。
func TestSchedulerUnsafeRespAPISeparate(t *testing.T) {
	L := glua.NewState()
	defer L.Close()

	ngx := L.NewTable()
	L.SetGlobal("ngx", ngx)

	// 注册不安全的 resp API
	RegisterSchedulerUnsafeRespAPI(L, ngx)

	// 验证 ngx["ngx.resp"] 存在（RegisterUnsafeAPI 使用完整名称作为键）
	resp := ngx.RawGetString("ngx.resp")
	if resp == glua.LNil {
		t.Fatal("ngx[\"ngx.resp\"] should exist")
	}

	// 尝试调用 ngx.resp.get_headers 应该返回错误
	err := L.DoString(`
		local ok, err = pcall(function()
			ngx["ngx.resp"].get_headers()
		end)
		if ok then
			error("ngx.resp.get_headers should fail in scheduler mode")
		end
	`)
	assert.NoError(t, err)
}

// TestSchedulerSharedDictAvailable 测试 Scheduler 模式下 ngx.shared 可用。
func TestSchedulerSharedDictAvailable(t *testing.T) {
	engine, err := NewEngine(DefaultConfig())
	require.NoError(t, err)
	defer engine.Close()

	// 创建共享字典
	engine.CreateSharedDict("test_dict", 100)

	L := glua.NewState()
	defer L.Close()

	// 调用 setupSchedulerNgxAPI
	setupSchedulerNgxAPI(L, engine)

	// 验证 ngx.shared.DICT 存在
	err = L.DoString(`
		if ngx.shared == nil then
			error("ngx.shared should exist")
		end
		local dict = ngx.shared.DICT("test_dict")
		if dict == nil then
			error("ngx.shared.DICT('test_dict') should return dict")
		end
	`)
	assert.NoError(t, err)
}

// TestSchedulerTimerAvailable 测试 Scheduler 模式下 ngx.timer 可用。
func TestSchedulerTimerAvailable(t *testing.T) {
	engine, err := NewEngine(DefaultConfig())
	require.NoError(t, err)
	defer engine.Close()

	L := glua.NewState()
	defer L.Close()

	// 调用 setupSchedulerNgxAPI
	setupSchedulerNgxAPI(L, engine)

	// 验证 ngx.timer 存在
	err = L.DoString(`
		if ngx.timer == nil then
			error("ngx.timer should exist")
		end
		if ngx.timer.at == nil then
			error("ngx.timer.at should exist")
		end
	`)
	assert.NoError(t, err)
}

// TestSchedulerModeIsolation 测试 Scheduler 模式与普通模式隔离。
func TestSchedulerModeIsolation(t *testing.T) {
	// 创建两个 LState，一个在 scheduler 模式，一个不在
	engine, err := NewEngine(DefaultConfig())
	require.NoError(t, err)
	defer engine.Close()

	// 普通 LState
	normalL := glua.NewState()
	defer normalL.Close()

	// Scheduler LState
	schedulerL := glua.NewState()
	defer schedulerL.Close()

	setupSchedulerNgxAPI(schedulerL, engine)

	// 验证普通 LState 不是 scheduler 模式
	if IsSchedulerMode(normalL) {
		t.Error("Normal LState should not be in scheduler mode")
	}

	// 验证 Scheduler LState 是 scheduler 模式
	if !IsSchedulerMode(schedulerL) {
		t.Error("Scheduler LState should be in scheduler mode")
	}
}

// TestSchedulerErrorMessages 测试 Scheduler 模式下错误消息格式。
func TestSchedulerErrorMessages(t *testing.T) {
	L := glua.NewState()
	defer L.Close()

	ngx := L.NewTable()
	L.SetGlobal("ngx", ngx)

	RegisterSchedulerUnsafeLocationAPI(L, ngx)
	RegisterSchedulerUnsafeVarAPI(L, ngx)
	RegisterSchedulerUnsafeCtxAPI(L, ngx)

	// 测试 location 错误消息
	err := L.DoString(`
		local ok, err = pcall(function()
			ngx.location.capture("/test")
		end)
		if not string.find(err, "not available") then
			error("Error message should mention 'not available': " .. tostring(err))
		end
	`)
	assert.NoError(t, err)

	// 测试 var 错误消息
	err = L.DoString(`
		local ok, err = pcall(function()
			local x = ngx.var.test
		end)
		if not string.find(err, "not available") then
			error("Error message should mention 'not available': " .. tostring(err))
		end
	`)
	assert.NoError(t, err)

	// 测试 ctx 错误消息
	err = L.DoString(`
		local ok, err = pcall(function()
			local x = ngx.ctx.test
		end)
		if not string.find(err, "not available") then
			error("Error message should mention 'not available': " .. tostring(err))
		end
	`)
	assert.NoError(t, err)
}

// TestSchedulerMultipleUnsafeCalls 测试多次调用不安全 API。
func TestSchedulerMultipleUnsafeCalls(t *testing.T) {
	L := glua.NewState()
	defer L.Close()

	ngx := L.NewTable()
	L.SetGlobal("ngx", ngx)

	RegisterSchedulerUnsafeVarAPI(L, ngx)

	// 多次调用都应该失败
	err := L.DoString(`
		for i = 1, 5 do
			local ok, err = pcall(function()
				ngx.var["key" .. i] = "value" .. i
			end)
			if ok then
				error("Call " .. i .. " should have failed")
			end
		end
	`)
	assert.NoError(t, err)
}

// TestSchedulerNilValueHandling 测试 Scheduler 模式下处理 nil 值。
func TestSchedulerNilValueHandling(t *testing.T) {
	L := glua.NewState()
	defer L.Close()

	ngx := L.NewTable()
	L.SetGlobal("ngx", ngx)

	RegisterSchedulerUnsafeVarAPI(L, ngx)

	// 尝试读取 nil 键
	err := L.DoString(`
		local ok, err = pcall(function()
			local x = ngx.var[nil]
		end)
		if ok then
			error("Reading nil key should fail")
		end
	`)
	assert.NoError(t, err)
}

// TestSchedulerConcurrentAccess 测试并发访问 Scheduler API。
func TestSchedulerConcurrentAccess(t *testing.T) {
	engine, err := NewEngine(DefaultConfig())
	require.NoError(t, err)
	defer engine.Close()

	// 创建多个 LState 并发访问
	done := make(chan bool, 10)

	for range 10 {
		go func() {
			L := glua.NewState()
			defer L.Close()

			setupSchedulerNgxAPI(L, engine)

			// 验证 scheduler 模式
			if !IsSchedulerMode(L) {
				t.Error("Should be in scheduler mode")
			}

			done <- true
		}()
	}

	// 等待所有 goroutine 完成
	for range 10 {
		<-done
	}
}
