#include "LuaItemApi.hpp"
#include "../systems/ItemRegistry.hpp"
#include "../systems/ContainerStateRegistry.hpp"
#include "../systems/ItemSystem.hpp"
#include "../components/Item.hpp"
#include "../components/Inventory.hpp"
#include <OgreLogManager.h>
#include <flecs.h>

static flecs::world getWorld(lua_State *L)
{
	lua_getfield(L, LUA_REGISTRYINDEX, "EditSceneFlecsWorld");
	OgreAssert(lua_islightuserdata(L, -1), "Flecs world not registered");
	flecs::world *world =
		static_cast<flecs::world *>(lua_touserdata(L, -1));
	lua_pop(L, 1);
	return *world;
}

namespace editScene
{

// ---------------------------------------------------------------------------
// ecs.items API
// ---------------------------------------------------------------------------

static int luaItemRegister(lua_State *L)
{
	if (lua_gettop(L) < 2 || !lua_isstring(L, 1) || !lua_istable(L, 2))
		return 0;

	std::string itemId = lua_tostring(L, 1);
	ItemRegistry::ItemDefinition def;
	def.itemId = itemId;

	if (lua_getfield(L, 2, "itemName"), lua_isstring(L, -1))
		def.itemName = lua_tostring(L, -1);
	lua_pop(L, 1);
	if (lua_getfield(L, 2, "itemType"), lua_isstring(L, -1))
		def.itemType = lua_tostring(L, -1);
	lua_pop(L, 1);
	if (lua_getfield(L, 2, "maxStackSize"), lua_isnumber(L, -1))
		def.maxStackSize = (int)lua_tointeger(L, -1);
	lua_pop(L, 1);
	if (lua_getfield(L, 2, "weight"), lua_isnumber(L, -1))
		def.weight = (float)lua_tonumber(L, -1);
	lua_pop(L, 1);
	if (lua_getfield(L, 2, "value"), lua_isnumber(L, -1))
		def.value = (int)lua_tointeger(L, -1);
	lua_pop(L, 1);
	if (lua_getfield(L, 2, "useActionName"), lua_isstring(L, -1))
		def.useActionName = lua_tostring(L, -1);
	lua_pop(L, 1);
	if (lua_getfield(L, 2, "unique"), lua_isboolean(L, -1))
		def.unique = lua_toboolean(L, -1) != 0;
	lua_pop(L, 1);

	ItemRegistry::getSingleton().registerItem(def);
	return 0;
}

static int luaItemFind(lua_State *L)
{
	if (lua_gettop(L) < 1 || !lua_isstring(L, 1))
		return 0;

	std::string itemId = lua_tostring(L, 1);
	auto *def = ItemRegistry::getSingleton().findDefinition(itemId);
	if (!def)
		return 0;

	lua_newtable(L);
	lua_pushstring(L, def->itemId.c_str());
	lua_setfield(L, -2, "itemId");
	lua_pushstring(L, def->itemName.c_str());
	lua_setfield(L, -2, "itemName");
	lua_pushstring(L, def->itemType.c_str());
	lua_setfield(L, -2, "itemType");
	lua_pushinteger(L, def->maxStackSize);
	lua_setfield(L, -2, "maxStackSize");
	lua_pushnumber(L, def->weight);
	lua_setfield(L, -2, "weight");
	lua_pushinteger(L, def->value);
	lua_setfield(L, -2, "value");
	lua_pushstring(L, def->useActionName.c_str());
	lua_setfield(L, -2, "useActionName");
	lua_pushboolean(L, def->unique ? 1 : 0);
	lua_setfield(L, -2, "unique");
	return 1;
}

static int luaItemList(lua_State *L)
{
	lua_newtable(L);
	int idx = 1;
	for (const auto &pair : ItemRegistry::getSingleton().getDefinitions()) {
		lua_pushstring(L, pair.second.itemId.c_str());
		lua_rawseti(L, -2, idx++);
	}
	return 1;
}

static int luaItemIsUnique(lua_State *L)
{
	if (lua_gettop(L) < 1 || !lua_isstring(L, 1))
		return 0;
	lua_pushboolean(L,
			ItemRegistry::getSingleton().isUnique(lua_tostring(L, 1)) ?
				1 :
				0);
	return 1;
}

// ---------------------------------------------------------------------------
// ecs.inventory API
// ---------------------------------------------------------------------------

static flecs::entity luaCheckEntity(lua_State *L, int idx)
{
	if (!lua_isnumber(L, idx))
		return flecs::entity::null();
	flecs::world world = getWorld(L);
	return world.entity((flecs::entity_t)lua_tointeger(L, idx));
}

static int luaInventoryAdd(lua_State *L)
{
	if (lua_gettop(L) < 3 || !lua_isnumber(L, 1) || !lua_isstring(L, 2) ||
	    !lua_isnumber(L, 3))
		return 0;

	flecs::entity e = luaCheckEntity(L, 1);
	std::string itemId = lua_tostring(L, 2);
	int count = (int)lua_tointeger(L, 3);

	// Find ItemSystem via ECS world query
	bool added = false;
	if (e.is_alive() && e.has<InventoryComponent>()) {
		auto &inv = e.get_mut<InventoryComponent>();
		int maxStack = ItemRegistry::getSingleton().getMaxStackSize(itemId);
		// Simple add logic (no ItemSystem pointer available in Lua)
		while (count > 0) {
			bool stacked = false;
			for (auto &slot : inv.slots) {
				if (!slot.isEmpty() && slot.itemId == itemId &&
				    slot.stackSize < maxStack) {
					int space = maxStack - slot.stackSize;
					int add = std::min(space, count);
					slot.stackSize += add;
					count -= add;
					stacked = true;
					if (count <= 0)
						break;
				}
			}
			if (!stacked || count > 0) {
				int slotIdx = inv.findEmptySlot();
				if (slotIdx < 0)
					break;
				while ((int)inv.slots.size() <= slotIdx)
					inv.slots.emplace_back();
				auto &slot = inv.slots[slotIdx];
				slot.itemId = itemId;
				int add = std::min(count, maxStack);
				slot.stackSize = add;
				count -= add;
			}
		}
		inv.recalculateWeight();
		added = true;
	}
	lua_pushboolean(L, added ? 1 : 0);
	return 1;
}

static int luaInventoryRemove(lua_State *L)
{
	if (lua_gettop(L) < 3 || !lua_isnumber(L, 1) || !lua_isstring(L, 2) ||
	    !lua_isnumber(L, 3))
		return 0;

	flecs::entity e = luaCheckEntity(L, 1);
	std::string itemId = lua_tostring(L, 2);
	int count = (int)lua_tointeger(L, 3);
	int removed = 0;

	if (e.is_alive() && e.has<InventoryComponent>()) {
		auto &inv = e.get_mut<InventoryComponent>();
		for (int i = (int)inv.slots.size() - 1;
		     i >= 0 && count > 0; i--) {
			auto &slot = inv.slots[i];
			if (slot.isEmpty() || slot.itemId != itemId)
				continue;
			int rem = std::min(count, slot.stackSize);
			slot.stackSize -= rem;
			count -= rem;
			removed += rem;
			if (slot.stackSize <= 0)
				slot.clear();
		}
		inv.recalculateWeight();
	}
	lua_pushinteger(L, removed);
	return 1;
}

static int luaInventoryHas(lua_State *L)
{
	if (lua_gettop(L) < 2 || !lua_isnumber(L, 1) || !lua_isstring(L, 2))
		return 0;

	flecs::entity e = luaCheckEntity(L, 1);
	std::string itemId = lua_tostring(L, 2);
	bool has = false;
	if (e.is_alive() && e.has<InventoryComponent>())
		has = e.get<InventoryComponent>().hasItem(itemId);
	lua_pushboolean(L, has ? 1 : 0);
	return 1;
}

static int luaInventoryCount(lua_State *L)
{
	if (lua_gettop(L) < 2 || !lua_isnumber(L, 1) || !lua_isstring(L, 2))
		return 0;

	flecs::entity e = luaCheckEntity(L, 1);
	std::string itemId = lua_tostring(L, 2);
	int count = 0;
	if (e.is_alive() && e.has<InventoryComponent>())
		count = e.get<InventoryComponent>().countItem(itemId);
	lua_pushinteger(L, count);
	return 1;
}

static int luaInventoryGetSlots(lua_State *L)
{
	if (lua_gettop(L) < 1 || !lua_isnumber(L, 1))
		return 0;

	flecs::entity e = luaCheckEntity(L, 1);
	lua_newtable(L);
	if (e.is_alive() && e.has<InventoryComponent>()) {
		auto &inv = e.get<InventoryComponent>();
		int idx = 1;
		for (const auto &slot : inv.slots) {
			if (slot.isEmpty())
				continue;
			lua_newtable(L);
			lua_pushstring(L, slot.itemId.c_str());
			lua_setfield(L, -2, "itemId");
			lua_pushinteger(L, slot.stackSize);
			lua_setfield(L, -2, "stackSize");
			lua_rawseti(L, -2, idx++);
		}
	}
	return 1;
}

static int luaInventorySetSlots(lua_State *L)
{
	if (lua_gettop(L) < 2 || !lua_isnumber(L, 1) || !lua_istable(L, 2))
		return 0;

	flecs::entity e = luaCheckEntity(L, 1);
	if (!e.is_alive() || !e.has<InventoryComponent>())
		return 0;

	auto &inv = e.get_mut<InventoryComponent>();
	inv.slots.clear();
	int len = (int)lua_rawlen(L, 2);
	for (int i = 1; i <= len; i++) {
		lua_rawgeti(L, 2, i);
		if (lua_istable(L, -1)) {
			InventorySlot slot;
			if (lua_getfield(L, -1, "itemId"), lua_isstring(L, -1))
				slot.itemId = lua_tostring(L, -1);
			lua_pop(L, 1);
			if (lua_getfield(L, -1, "stackSize"), lua_isnumber(L, -1))
				slot.stackSize = (int)lua_tointeger(L, -1);
			lua_pop(L, 1);
			if (!slot.isEmpty())
				inv.slots.push_back(slot);
		}
		lua_pop(L, 1);
	}
	inv.recalculateWeight();
	return 0;
}

// ---------------------------------------------------------------------------
// ecs.container API
// ---------------------------------------------------------------------------

static int luaContainerGetState(lua_State *L)
{
	if (lua_gettop(L) < 1 || !lua_isstring(L, 1))
		return 0;
	std::string containerId = lua_tostring(L, 1);
	auto slots = ContainerStateRegistry::getInstance().getState(
		containerId);
	lua_newtable(L);
	int idx = 1;
	for (const auto &slot : slots) {
		lua_newtable(L);
		lua_pushstring(L, slot.itemId.c_str());
		lua_setfield(L, -2, "itemId");
		lua_pushinteger(L, slot.stackSize);
		lua_setfield(L, -2, "stackSize");
		lua_rawseti(L, -2, idx++);
	}
	return 1;
}

static int luaContainerSetState(lua_State *L)
{
	if (lua_gettop(L) < 2 || !lua_isstring(L, 1) || !lua_istable(L, 2))
		return 0;
	std::string containerId = lua_tostring(L, 1);
	std::vector<ContainerStateRegistry::ContainerSlot> slots;
	int len = (int)lua_rawlen(L, 2);
	for (int i = 1; i <= len; i++) {
		lua_rawgeti(L, 2, i);
		if (lua_istable(L, -1)) {
			ContainerStateRegistry::ContainerSlot slot;
			if (lua_getfield(L, -1, "itemId"), lua_isstring(L, -1))
				slot.itemId = lua_tostring(L, -1);
			lua_pop(L, 1);
			if (lua_getfield(L, -1, "stackSize"), lua_isnumber(L, -1))
				slot.stackSize = (int)lua_tointeger(L, -1);
			lua_pop(L, 1);
			slots.push_back(slot);
		}
		lua_pop(L, 1);
	}
	ContainerStateRegistry::getInstance().loadState(containerId, slots);
	return 0;
}

static int luaContainerClearState(lua_State *L)
{
	if (lua_gettop(L) < 1 || !lua_isstring(L, 1))
		return 0;
	ContainerStateRegistry::getInstance().clearState(lua_tostring(L, 1));
	return 0;
}

// ---------------------------------------------------------------------------
// Registration
// ---------------------------------------------------------------------------

void registerLuaItemApi(lua_State *L)
{
	// ecs.items
	lua_newtable(L);
	lua_pushcfunction(L, luaItemRegister);
	lua_setfield(L, -2, "register");
	lua_pushcfunction(L, luaItemFind);
	lua_setfield(L, -2, "find");
	lua_pushcfunction(L, luaItemList);
	lua_setfield(L, -2, "list");
	lua_pushcfunction(L, luaItemIsUnique);
	lua_setfield(L, -2, "is_unique");
	lua_setfield(L, -2, "items");

	// ecs.inventory
	lua_newtable(L);
	lua_pushcfunction(L, luaInventoryAdd);
	lua_setfield(L, -2, "add");
	lua_pushcfunction(L, luaInventoryRemove);
	lua_setfield(L, -2, "remove");
	lua_pushcfunction(L, luaInventoryHas);
	lua_setfield(L, -2, "has");
	lua_pushcfunction(L, luaInventoryCount);
	lua_setfield(L, -2, "count");
	lua_pushcfunction(L, luaInventoryGetSlots);
	lua_setfield(L, -2, "get_slots");
	lua_pushcfunction(L, luaInventorySetSlots);
	lua_setfield(L, -2, "set_slots");
	lua_setfield(L, -2, "inventory");

	// ecs.container
	lua_newtable(L);
	lua_pushcfunction(L, luaContainerGetState);
	lua_setfield(L, -2, "get_state");
	lua_pushcfunction(L, luaContainerSetState);
	lua_setfield(L, -2, "set_state");
	lua_pushcfunction(L, luaContainerClearState);
	lua_setfield(L, -2, "clear_state");
	lua_setfield(L, -2, "container");
}

} // namespace editScene
