/**
 * @file character_class_lua_test.cpp
 * @brief Standalone test for the Lua Character Class API.
 *
 * Tests the ecs.character_class.* functions exposed via the ecs.* Lua API.
 * Tests database queries (get_class_names, get_stat_names, get_skill_names,
 * get_need_names, get_class, get_stat_kind) and per-entity runtime API
 * (get_level, get_xp, add_xp, get_stat, get_skill, get_need,
 * get_available_points, set_need, get_pool_current, get_pool_max,
 * set_pool_current).
 *
 * Build with:
 *   g++ -std=c++17 -I. -I../.. -I../../lua/lua-5.4.8/src \
 *       character_class_lua_test.cpp \
 *       lua_test_stubs.cpp \
 *       ../../lua/lua-5.4.8/src/liblua.a \
 *       -o character_class_lua_test -lm
 *
 * Or via CMake (see CMakeLists.txt in this directory).
 */

#include <cstdio>
#include <cstring>
#include <cassert>
#include <string>
#include <vector>

// Ogre stub (provides Ogre::String, Ogre::Vector3, Ogre::LogManager)
#include "ogre_stub.h"

// Include Lua
extern "C" {
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
}

// Forward declare the registration function
namespace editScene
{
void registerLuaCharacterClassApi(lua_State *L);
void registerLuaEntityApi(lua_State *L);
void registerLuaComponentApi(lua_State *L);
}

// ---------------------------------------------------------------------------
// Test helpers
// ---------------------------------------------------------------------------

static int testCount = 0;
static int passCount = 0;

#define TEST(name)                                             \
	do {                                                   \
		testCount++;                                   \
		printf("  TEST %d: %s ... ", testCount, name); \
	} while (0)

#define PASS()                    \
	do {                      \
		passCount++;      \
		printf("PASS\n"); \
	} while (0)

#define FAIL(msg)                          \
	do {                               \
		printf("FAIL: %s\n", msg); \
		return 1;                  \
	} while (0)

// ---------------------------------------------------------------------------
// Helper: run a Lua string and check for errors
// ---------------------------------------------------------------------------

static bool runLua(lua_State *L, const char *code)
{
	if (luaL_dostring(L, code) != LUA_OK) {
		fprintf(stderr, "Lua error: %s\n", lua_tostring(L, -1));
		lua_pop(L, 1);
		return false;
	}
	return true;
}

// ---------------------------------------------------------------------------
// Test 1: get_class_names returns a table
// ---------------------------------------------------------------------------

static int testGetClassNames(lua_State *L)
{
	TEST("get_class_names returns a table");

	bool ok = runLua(L,
			 "local names = ecs.character_class.get_class_names();"
			 "assert(type(names) == 'table', "
			 "'get_class_names should return a table')");
	if (!ok)
		FAIL("get_class_names assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 2: get_stat_names returns a table
// ---------------------------------------------------------------------------

static int testGetStatNames(lua_State *L)
{
	TEST("get_stat_names returns a table");

	bool ok = runLua(L,
			 "local names = ecs.character_class.get_stat_names();"
			 "assert(type(names) == 'table', "
			 "'get_stat_names should return a table')");
	if (!ok)
		FAIL("get_stat_names assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 3: get_skill_names returns a table
// ---------------------------------------------------------------------------

static int testGetSkillNames(lua_State *L)
{
	TEST("get_skill_names returns a table");

	bool ok = runLua(L,
			 "local names = ecs.character_class.get_skill_names();"
			 "assert(type(names) == 'table', "
			 "'get_skill_names should return a table')");
	if (!ok)
		FAIL("get_skill_names assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 4: get_need_names returns a table
// ---------------------------------------------------------------------------

static int testGetNeedNames(lua_State *L)
{
	TEST("get_need_names returns a table");

	bool ok = runLua(L,
			 "local names = ecs.character_class.get_need_names();"
			 "assert(type(names) == 'table', "
			 "'get_need_names should return a table')");
	if (!ok)
		FAIL("get_need_names assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 5: get_class returns nil for unknown class
// ---------------------------------------------------------------------------

static int testGetClassUnknown(lua_State *L)
{
	TEST("get_class returns nil for unknown class");

	bool ok = runLua(
		L, "local cls = ecs.character_class.get_class('nonexistent');"
		   "assert(cls == nil, "
		   "'get_class should return nil for unknown class')");
	if (!ok)
		FAIL("get_class unknown assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 6: get_stat_kind returns 'unknown' for unknown stat
// ---------------------------------------------------------------------------

static int testGetStatKindUnknown(lua_State *L)
{
	TEST("get_stat_kind returns 'unknown' for unknown stat");

	bool ok = runLua(
		L,
		"local kind = ecs.character_class.get_stat_kind('nonexistent');"
		"assert(kind == 'unknown', "
		"'get_stat_kind should return unknown for unknown stat')");
	if (!ok)
		FAIL("get_stat_kind unknown assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 7: get_stat_kind returns 'unknown' for nil
// ---------------------------------------------------------------------------

static int testGetStatKindNil(lua_State *L)
{
	TEST("get_stat_kind returns 'unknown' for nil");

	bool ok = runLua(L,
			 "local kind = ecs.character_class.get_stat_kind(nil);"
			 "assert(kind == 'unknown', "
			 "'get_stat_kind should return unknown for nil')");
	if (!ok)
		FAIL("get_stat_kind nil assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 8: get_level returns 0 for non-existent entity
// ---------------------------------------------------------------------------

static int testGetLevelNoEntity(lua_State *L)
{
	TEST("get_level returns 0 for non-existent entity");

	bool ok = runLua(
		L, "local level = ecs.character_class.get_level(99999);"
		   "assert(level == 0, "
		   "'get_level should return 0 for non-existent entity')");
	if (!ok)
		FAIL("get_level no entity assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 9: get_xp returns 0 for non-existent entity
// ---------------------------------------------------------------------------

static int testGetXPNoEntity(lua_State *L)
{
	TEST("get_xp returns 0 for non-existent entity");

	bool ok = runLua(L,
			 "local xp = ecs.character_class.get_xp(99999);"
			 "assert(xp == 0, "
			 "'get_xp should return 0 for non-existent entity')");
	if (!ok)
		FAIL("get_xp no entity assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 10: add_xp returns false for non-existent entity
// ---------------------------------------------------------------------------

static int testAddXPNoEntity(lua_State *L)
{
	TEST("add_xp returns false for non-existent entity");

	bool ok = runLua(
		L, "local ok = ecs.character_class.add_xp(99999, 100);"
		   "assert(ok == false, "
		   "'add_xp should return false for non-existent entity')");
	if (!ok)
		FAIL("add_xp no entity assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 11: get_stat returns 0 for non-existent entity
// ---------------------------------------------------------------------------

static int testGetStatNoEntity(lua_State *L)
{
	TEST("get_stat returns 0 for non-existent entity");

	bool ok = runLua(
		L,
		"local val = ecs.character_class.get_stat(99999, 'strength');"
		"assert(val == 0, "
		"'get_stat should return 0 for non-existent entity')");
	if (!ok)
		FAIL("get_stat no entity assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 12: get_skill returns 0 for non-existent entity
// ---------------------------------------------------------------------------

static int testGetSkillNoEntity(lua_State *L)
{
	TEST("get_skill returns 0 for non-existent entity");

	bool ok = runLua(
		L,
		"local val = ecs.character_class.get_skill(99999, 'swordsmanship');"
		"assert(val == 0, "
		"'get_skill should return 0 for non-existent entity')");
	if (!ok)
		FAIL("get_skill no entity assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 13: get_need returns 0 for non-existent entity
// ---------------------------------------------------------------------------

static int testGetNeedNoEntity(lua_State *L)
{
	TEST("get_need returns 0 for non-existent entity");

	bool ok = runLua(
		L, "local val = ecs.character_class.get_need(99999, 'hunger');"
		   "assert(val == 0, "
		   "'get_need should return 0 for non-existent entity')");
	if (!ok)
		FAIL("get_need no entity assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 14: get_available_points returns 0 for non-existent entity
// ---------------------------------------------------------------------------

static int testGetAvailablePointsNoEntity(lua_State *L)
{
	TEST("get_available_points returns 0 for non-existent entity");

	bool ok = runLua(
		L,
		"local pts = ecs.character_class.get_available_points(99999);"
		"assert(pts == 0, "
		"'get_available_points should return 0 for non-existent entity')");
	if (!ok)
		FAIL("get_available_points no entity assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 15: set_need does not crash for non-existent entity
// ---------------------------------------------------------------------------

static int testSetNeedNoEntity(lua_State *L)
{
	TEST("set_need does not crash for non-existent entity");

	bool ok =
		runLua(L, "ecs.character_class.set_need(99999, 'hunger', 50);");
	if (!ok)
		FAIL("set_need no entity assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 16: get_pool_current returns 0 for non-existent entity
// ---------------------------------------------------------------------------

static int testGetPoolCurrentNoEntity(lua_State *L)
{
	TEST("get_pool_current returns 0 for non-existent entity");

	bool ok = runLua(
		L,
		"local val = ecs.character_class.get_pool_current(99999, 'health');"
		"assert(val == 0, "
		"'get_pool_current should return 0 for non-existent entity')");
	if (!ok)
		FAIL("get_pool_current no entity assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 17: get_pool_max returns 0 for non-existent entity
// ---------------------------------------------------------------------------

static int testGetPoolMaxNoEntity(lua_State *L)
{
	TEST("get_pool_max returns 0 for non-existent entity");

	bool ok = runLua(
		L,
		"local val = ecs.character_class.get_pool_max(99999, 'health');"
		"assert(val == 0, "
		"'get_pool_max should return 0 for non-existent entity')");
	if (!ok)
		FAIL("get_pool_max no entity assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 18: set_pool_current returns false for non-existent entity
// ---------------------------------------------------------------------------

static int testSetPoolCurrentNoEntity(lua_State *L)
{
	TEST("set_pool_current returns false for non-existent entity");

	bool ok = runLua(
		L,
		"local ok = ecs.character_class.set_pool_current(99999, 'health', 50);"
		"assert(ok == false, "
		"'set_pool_current should return false for non-existent entity')");
	if (!ok)
		FAIL("set_pool_current no entity assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 19: All functions exist and are callable
// ---------------------------------------------------------------------------

static int testAllFunctionsExist(lua_State *L)
{
	TEST("all functions exist and are callable");

	bool ok = runLua(
		L,
		"assert(type(ecs.character_class.get_class_names) == 'function', "
		"'get_class_names should be a function');"
		"assert(type(ecs.character_class.get_stat_names) == 'function', "
		"'get_stat_names should be a function');"
		"assert(type(ecs.character_class.get_skill_names) == 'function', "
		"'get_skill_names should be a function');"
		"assert(type(ecs.character_class.get_need_names) == 'function', "
		"'get_need_names should be a function');"
		"assert(type(ecs.character_class.get_class) == 'function', "
		"'get_class should be a function');"
		"assert(type(ecs.character_class.get_stat_kind) == 'function', "
		"'get_stat_kind should be a function');"
		"assert(type(ecs.character_class.get_level) == 'function', "
		"'get_level should be a function');"
		"assert(type(ecs.character_class.get_xp) == 'function', "
		"'get_xp should be a function');"
		"assert(type(ecs.character_class.add_xp) == 'function', "
		"'add_xp should be a function');"
		"assert(type(ecs.character_class.get_stat) == 'function', "
		"'get_stat should be a function');"
		"assert(type(ecs.character_class.get_skill) == 'function', "
		"'get_skill should be a function');"
		"assert(type(ecs.character_class.get_need) == 'function', "
		"'get_need should be a function');"
		"assert(type(ecs.character_class.get_available_points) == 'function', "
		"'get_available_points should be a function');"
		"assert(type(ecs.character_class.set_need) == 'function', "
		"'set_need should be a function');"
		"assert(type(ecs.character_class.get_pool_current) == 'function', "
		"'get_pool_current should be a function');"
		"assert(type(ecs.character_class.get_pool_max) == 'function', "
		"'get_pool_max should be a function');"
		"assert(type(ecs.character_class.set_pool_current) == 'function', "
		"'set_pool_current should be a function')");
	if (!ok)
		FAIL("all functions exist assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 20: get_stat returns 0 for nil stat name
// ---------------------------------------------------------------------------

static int testGetStatNilName(lua_State *L)
{
	TEST("get_stat returns 0 for nil stat name");

	bool ok = runLua(L,
			 "local val = ecs.character_class.get_stat(99999, nil);"
			 "assert(val == 0, "
			 "'get_stat should return 0 for nil stat name')");
	if (!ok)
		FAIL("get_stat nil name assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 21: get_skill returns 0 for nil skill name
// ---------------------------------------------------------------------------

static int testGetSkillNilName(lua_State *L)
{
	TEST("get_skill returns 0 for nil skill name");

	bool ok = runLua(
		L, "local val = ecs.character_class.get_skill(99999, nil);"
		   "assert(val == 0, "
		   "'get_skill should return 0 for nil skill name')");
	if (!ok)
		FAIL("get_skill nil name assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 22: get_need returns 0 for nil need name
// ---------------------------------------------------------------------------

static int testGetNeedNilName(lua_State *L)
{
	TEST("get_need returns 0 for nil need name");

	bool ok = runLua(L,
			 "local val = ecs.character_class.get_need(99999, nil);"
			 "assert(val == 0, "
			 "'get_need should return 0 for nil need name')");
	if (!ok)
		FAIL("get_need nil name assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 23: get_pool_current returns 0 for nil pool name
// ---------------------------------------------------------------------------

static int testGetPoolCurrentNilName(lua_State *L)
{
	TEST("get_pool_current returns 0 for nil pool name");

	bool ok = runLua(
		L,
		"local val = ecs.character_class.get_pool_current(99999, nil);"
		"assert(val == 0, "
		"'get_pool_current should return 0 for nil pool name')");
	if (!ok)
		FAIL("get_pool_current nil name assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 24: get_pool_max returns 0 for nil pool name
// ---------------------------------------------------------------------------

static int testGetPoolMaxNilName(lua_State *L)
{
	TEST("get_pool_max returns 0 for nil pool name");

	bool ok = runLua(
		L, "local val = ecs.character_class.get_pool_max(99999, nil);"
		   "assert(val == 0, "
		   "'get_pool_max should return 0 for nil pool name')");
	if (!ok)
		FAIL("get_pool_max nil name assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 25: set_pool_current returns false for nil pool name
// ---------------------------------------------------------------------------

static int testSetPoolCurrentNilName(lua_State *L)
{
	TEST("set_pool_current returns false for nil pool name");

	bool ok = runLua(
		L,
		"local ok = ecs.character_class.set_pool_current(99999, nil, 50);"
		"assert(ok == false, "
		"'set_pool_current should return false for nil pool name')");
	if (!ok)
		FAIL("set_pool_current nil name assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Main
// ---------------------------------------------------------------------------

int main()
{
	printf("Character Class Lua API Tests\n");
	printf("==============================\n\n");

	// Create Lua state
	lua_State *L = luaL_newstate();
	if (!L) {
		fprintf(stderr, "Failed to create Lua state\n");
		return 1;
	}
	luaL_openlibs(L);

	// Register required APIs
	editScene::registerLuaEntityApi(L);
	editScene::registerLuaComponentApi(L);
	editScene::registerLuaCharacterClassApi(L);

	// Run tests
	int failures = 0;
	failures += testGetClassNames(L);
	failures += testGetStatNames(L);
	failures += testGetSkillNames(L);
	failures += testGetNeedNames(L);
	failures += testGetClassUnknown(L);
	failures += testGetStatKindUnknown(L);
	failures += testGetStatKindNil(L);
	failures += testGetLevelNoEntity(L);
	failures += testGetXPNoEntity(L);
	failures += testAddXPNoEntity(L);
	failures += testGetStatNoEntity(L);
	failures += testGetSkillNoEntity(L);
	failures += testGetNeedNoEntity(L);
	failures += testGetAvailablePointsNoEntity(L);
	failures += testSetNeedNoEntity(L);
	failures += testGetPoolCurrentNoEntity(L);
	failures += testGetPoolMaxNoEntity(L);
	failures += testSetPoolCurrentNoEntity(L);
	failures += testAllFunctionsExist(L);
	failures += testGetStatNilName(L);
	failures += testGetSkillNilName(L);
	failures += testGetNeedNilName(L);
	failures += testGetPoolCurrentNilName(L);
	failures += testGetPoolMaxNilName(L);
	failures += testSetPoolCurrentNilName(L);

	// Cleanup
	lua_close(L);

	printf("\nResults: %d/%d passed, %d failed\n", passCount, testCount,
	       failures);

	return failures > 0 ? 1 : 0;
}
