/**
 * @file item_lua_test.cpp
 * @brief Standalone test for the Lua Item / Inventory / Container APIs.
 *
 * Tests the ecs.items.*, ecs.inventory.*, and ecs.container.* functions
 * exposed via the ecs.* Lua API.
 *
 * Build with:
 *   g++ -std=c++17 -I. -I../.. -I../../lua/lua-5.4.8/src \
 *       item_lua_test.cpp \
 *       lua_test_stubs.cpp \
 *       ../../lua/lua-5.4.8/src/liblua.a \
 *       -o item_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 registerLuaItemApi(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: Register and find an item
// ---------------------------------------------------------------------------

static int testItemRegisterAndFind(lua_State *L)
{
	TEST("register and find item definition");

	bool ok = runLua(L,
			 "ecs.items.register('potion_health', {"
			 "  itemName = 'Health Potion',"
			 "  itemType = 'consumable',"
			 "  maxStackSize = 10,"
			 "  weight = 0.5,"
			 "  value = 25,"
			 "  useActionName = 'drink_potion',"
			 "  unique = false"
			 "}); "
			 "local def = ecs.items.find('potion_health');"
			 "assert(def ~= nil, 'definition should exist');"
			 "assert(def.itemName == 'Health Potion', 'wrong name');"
			 "assert(def.itemType == 'consumable', 'wrong type');"
			 "assert(def.maxStackSize == 10, 'wrong maxStackSize');"
			 "assert(def.weight == 0.5, 'wrong weight');"
			 "assert(def.value == 25, 'wrong value');"
			 "assert(def.useActionName == 'drink_potion', 'wrong action');"
			 "assert(def.unique == false, 'wrong unique')");
	if (!ok)
		FAIL("register/find assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 2: List registered items
// ---------------------------------------------------------------------------

static int testItemList(lua_State *L)
{
	TEST("list registered items");

	bool ok = runLua(L,
			 "ecs.items.register('sword_iron', { itemName = 'Iron Sword' });"
			 "local list = ecs.items.list();"
			 "assert(#list >= 2, 'expected at least 2 items');"
			 "local found = false;"
			 "for _, id in ipairs(list) do "
			 "  if id == 'sword_iron' then found = true end "
			 "end; "
			 "assert(found, 'sword_iron should be in list')");
	if (!ok)
		FAIL("list assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 3: is_unique returns correct value
// ---------------------------------------------------------------------------

static int testItemIsUnique(lua_State *L)
{
	TEST("is_unique returns correct value");

	bool ok = runLua(L,
			 "ecs.items.register('amulet_legendary', {"
			 "  itemName = 'Legendary Amulet',"
			 "  unique = true"
			 "}); "
			 "assert(ecs.items.is_unique('amulet_legendary') == true, "
			 "       'amulet should be unique'); "
			 "assert(ecs.items.is_unique('potion_health') == false, "
			 "       'potion should not be unique'); "
			 "assert(ecs.items.is_unique('nonexistent') == false, "
			 "       'nonexistent should return false')");
	if (!ok)
		FAIL("is_unique assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 4: Find nonexistent item returns nil
// ---------------------------------------------------------------------------

static int testItemFindMissing(lua_State *L)
{
	TEST("find nonexistent item returns nil");

	bool ok = runLua(L,
			 "local def = ecs.items.find('does_not_exist');"
			 "assert(def == nil, 'should be nil for missing item')");
	if (!ok)
		FAIL("find missing assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 5: Inventory add and count
// ---------------------------------------------------------------------------

static int testInventoryAddAndCount(lua_State *L)
{
	TEST("inventory add and count");

	bool ok = runLua(L,
			 "local id = 42; "
			 "local ok = ecs.inventory.add(id, 'potion_health', 3); "
			 "assert(ok == true, 'add should return true'); "
			 "local count = ecs.inventory.count(id, 'potion_health'); "
			 "assert(count == 3, 'expected count 3, got ' .. tostring(count))");
	if (!ok)
		FAIL("add/count assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 6: Inventory has item
// ---------------------------------------------------------------------------

static int testInventoryHas(lua_State *L)
{
	TEST("inventory has item");

	bool ok = runLua(L,
			 "local id = 42; "
			 "ecs.inventory.add(id, 'sword_iron', 1); "
			 "assert(ecs.inventory.has(id, 'sword_iron') == true, "
			 "       'should have sword_iron'); "
			 "assert(ecs.inventory.has(id, 'nonexistent') == false, "
			 "       'should not have nonexistent')");
	if (!ok)
		FAIL("has assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 7: Inventory remove
// ---------------------------------------------------------------------------

static int testInventoryRemove(lua_State *L)
{
	TEST("inventory remove");

	bool ok = runLua(L,
			 "local id = 42; "
			 "ecs.inventory.add(id, 'potion_health', 10); "
			 "local removed = ecs.inventory.remove(id, 'potion_health', 4); "
			 "assert(removed == 4, 'expected removed 4, got ' .. tostring(removed)); "
			 "local count = ecs.inventory.count(id, 'potion_health'); "
			 "assert(count == 9, 'expected 9 remaining (3+10-4), got ' .. tostring(count))");
	if (!ok)
		FAIL("remove assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 8: Inventory get_slots
// ---------------------------------------------------------------------------

static int testInventoryGetSlots(lua_State *L)
{
	TEST("inventory get_slots");

	bool ok = runLua(L,
			 "local id = 100; "
			 "ecs.inventory.add(id, 'gem_ruby', 5); "
			 "ecs.inventory.add(id, 'gem_sapphire', 3); "
			 "local slots = ecs.inventory.get_slots(id); "
			 "assert(#slots >= 2, 'expected at least 2 slots'); "
			 "local rubyFound = false; "
			 "for _, slot in ipairs(slots) do "
			 "  if slot.itemId == 'gem_ruby' then "
			 "    assert(slot.stackSize == 5, 'wrong ruby count'); "
			 "    rubyFound = true; "
			 "  end "
			 "end; "
			 "assert(rubyFound, 'ruby slot not found')");
	if (!ok)
		FAIL("get_slots assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 9: Inventory set_slots
// ---------------------------------------------------------------------------

static int testInventorySetSlots(lua_State *L)
{
	TEST("inventory set_slots");

	bool ok = runLua(L,
			 "local id = 200; "
			 "ecs.inventory.set_slots(id, {"
			 "  { itemId = 'arrow', stackSize = 99 },"
			 "  { itemId = 'bow', stackSize = 1 }"
			 "}); "
			 "local slots = ecs.inventory.get_slots(id); "
			 "assert(#slots == 2, 'expected 2 slots'); "
			 "assert(ecs.inventory.count(id, 'arrow') == 99, 'wrong arrow count'); "
			 "assert(ecs.inventory.count(id, 'bow') == 1, 'wrong bow count')");
	if (!ok)
		FAIL("set_slots assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 10: Container get/set/clear state
// ---------------------------------------------------------------------------

static int testContainerState(lua_State *L)
{
	TEST("container get/set/clear state");

	bool ok = runLua(L,
			 "ecs.container.set_state('chest_village', {"
			 "  { itemId = 'gold_coin', stackSize = 50 },"
			 "  { itemId = 'iron_dagger', stackSize = 1 }"
			 "}); "
			 "local state = ecs.container.get_state('chest_village'); "
			 "assert(#state == 2, 'expected 2 slots'); "
			 "local foundCoin = false; "
			 "for _, s in ipairs(state) do "
			 "  if s.itemId == 'gold_coin' then "
			 "    assert(s.stackSize == 50, 'wrong coin count'); "
			 "    foundCoin = true; "
			 "  end "
			 "end; "
			 "assert(foundCoin, 'gold_coin not found'); "
			 "ecs.container.clear_state('chest_village'); "
			 "local cleared = ecs.container.get_state('chest_village'); "
			 "assert(#cleared == 0, 'expected empty after clear')");
	if (!ok)
		FAIL("container state assertion failed");

	PASS();
	return 0;
}

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

int main()
{
	printf("Item / Inventory / Container 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 the item API
	editScene::registerLuaItemApi(L);

	// Run tests
	int failures = 0;
	failures += testItemRegisterAndFind(L);
	failures += testItemList(L);
	failures += testItemIsUnique(L);
	failures += testItemFindMissing(L);
	failures += testInventoryAddAndCount(L);
	failures += testInventoryHas(L);
	failures += testInventoryRemove(L);
	failures += testInventoryGetSlots(L);
	failures += testInventorySetSlots(L);
	failures += testContainerState(L);

	// Cleanup
	lua_close(L);

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

	return failures > 0 ? 1 : 0;
}
