#include "LuaCharacterClassApi.hpp"
#include "../systems/CharacterRegistry.hpp"
#include "../components/CharacterClassDatabase.hpp"
#include "../components/CharacterIdentity.hpp"
#include <OgreLogManager.h>

namespace editScene
{

// ---------------------------------------------------------------------------
// Helper: get the Flecs world from the Lua registry
// ---------------------------------------------------------------------------

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;
}

// ---------------------------------------------------------------------------
// Helper: get character record from entity ID
// ---------------------------------------------------------------------------

static CharacterRegistry::CharacterRecord *getCharacterRecord(
	lua_State *L, int entityArgIdx)
{
	int entityId = static_cast<int>(lua_tointeger(L, entityArgIdx));
	flecs::entity e = getWorld(L).entity(entityId);
	if (!e.is_alive() || !e.has<CharacterIdentityComponent>())
		return nullptr;
	auto &ci = e.get<CharacterIdentityComponent>();
	return CharacterRegistry::getSingleton().findCharacter(ci.registryId);
}

// ---------------------------------------------------------------------------
// Helper: push string-vector as Lua table
// ---------------------------------------------------------------------------

static void pushStringVector(lua_State *L,
			     const std::vector<Ogre::String> &vec)
{
	lua_newtable(L);
	for (size_t i = 0; i < vec.size(); i++) {
		lua_pushstring(L, vec[i].c_str());
		lua_rawseti(L, -2, static_cast<int>(i + 1));
	}
}

// ---------------------------------------------------------------------------
// Database queries
// ---------------------------------------------------------------------------

static int luaGetClassNames(lua_State *L)
{
	pushStringVector(L,
			 CharacterClassDatabase::getSingleton()
				 .getClassNames());
	return 1;
}

static int luaGetStatNames(lua_State *L)
{
	pushStringVector(L,
			 CharacterClassDatabase::getSingleton()
				 .getStatNames());
	return 1;
}

static int luaGetSkillNames(lua_State *L)
{
	pushStringVector(L,
			 CharacterClassDatabase::getSingleton()
				 .getSkillNames());
	return 1;
}

static int luaGetNeedNames(lua_State *L)
{
	pushStringVector(L,
			 CharacterClassDatabase::getSingleton()
				 .getNeedNames());
	return 1;
}

static int luaGetClass(lua_State *L)
{
	const char *name = lua_tostring(L, 1);
	if (!name)
		return 0;

	const auto *cls = CharacterClassDatabase::getSingleton().findClass(
		name);
	if (!cls)
		return 0;

	lua_newtable(L);
	lua_pushstring(L, cls->name.c_str());
	lua_setfield(L, -2, "name");
	lua_pushstring(L, cls->description.c_str());
	lua_setfield(L, -2, "description");
	pushStringVector(L, cls->primaryStats);
	lua_setfield(L, -2, "primary_stats");
	return 1;
}

static int luaGetStatKind(lua_State *L)
{
	const char *name = lua_tostring(L, 1);
	if (!name) {
		lua_pushstring(L, "unknown");
		return 1;
	}
	const auto *def = CharacterClassDatabase::getSingleton().findStat(name);
	if (!def) {
		lua_pushstring(L, "unknown");
		return 1;
	}
	lua_pushstring(L, def->kind == CharacterClassDatabase::StatKind::ResourcePool ?
				   "resource_pool" : "attribute");
	return 1;
}

// ---------------------------------------------------------------------------
// Per-entity runtime API (via CharacterRegistry)
// ---------------------------------------------------------------------------

static int luaGetLevel(lua_State *L)
{
	auto *rec = getCharacterRecord(L, 1);
	lua_pushinteger(L, rec ? rec->level : 0);
	return 1;
}

static int luaGetXP(lua_State *L)
{
	auto *rec = getCharacterRecord(L, 1);
	lua_pushinteger(L, rec ? (lua_Integer)rec->currentXP : 0);
	return 1;
}

static int luaAddXP(lua_State *L)
{
	auto *rec = getCharacterRecord(L, 1);
	int64_t amount = static_cast<int64_t>(lua_tointeger(L, 2));
	if (!rec) {
		lua_pushboolean(L, 0);
		return 1;
	}
	rec->currentXP += amount;
	lua_pushboolean(L, 1);
	return 1;
}

static int luaGetStat(lua_State *L)
{
	auto *rec = getCharacterRecord(L, 1);
	const char *statName = lua_tostring(L, 2);
	if (!rec || !statName) {
		lua_pushinteger(L, 0);
		return 1;
	}

	const auto *def = CharacterClassDatabase::getSingleton().findStat(statName);
	if (def && def->kind ==
		    CharacterClassDatabase::StatKind::ResourcePool) {
		int maxVal = rec->stats.count(statName) ? rec->stats[statName] : 0;
		if (maxVal <= 0) {
			lua_pushinteger(L, 0);
			return 1;
		}
		auto it = rec->currentPools.find(statName);
		if (it == rec->currentPools.end()) {
			lua_pushinteger(L, maxVal);
			return 1;
		}
		int val = it->second;
		if (val < 0)
			val = 0;
		if (val > maxVal)
			val = maxVal;
		lua_pushinteger(L, val);
		return 1;
	}

	lua_pushinteger(L, rec->stats.count(statName) ? rec->stats[statName] : 0);
	return 1;
}

static int luaGetSkill(lua_State *L)
{
	auto *rec = getCharacterRecord(L, 1);
	const char *skillName = lua_tostring(L, 2);
	if (!rec || !skillName) {
		lua_pushinteger(L, 0);
		return 1;
	}
	int val = rec->skills.count(skillName) ? rec->skills[skillName] : 0;
	const auto *def = CharacterClassDatabase::getSingleton().findSkill(skillName);
	if (def) {
		if (val < 0)
			val = 0;
		if (val > def->maxValue)
			val = def->maxValue;
	}
	lua_pushinteger(L, val);
	return 1;
}

static int luaGetNeed(lua_State *L)
{
	auto *rec = getCharacterRecord(L, 1);
	const char *needName = lua_tostring(L, 2);
	if (!rec || !needName) {
		lua_pushinteger(L, 0);
		return 1;
	}
	int val = rec->needs.count(needName) ? rec->needs[needName] : 0;
	const auto *def = CharacterClassDatabase::getSingleton().findNeed(needName);
	if (def) {
		if (val < 0)
			val = 0;
		if (val > def->maxValue)
			val = def->maxValue;
	}
	lua_pushinteger(L, val);
	return 1;
}

static int luaGetAvailablePoints(lua_State *L)
{
	auto *rec = getCharacterRecord(L, 1);
	lua_pushinteger(L, rec ? (lua_Integer)rec->availablePoints : 0);
	return 1;
}

static int luaSetNeed(lua_State *L)
{
	auto *rec = getCharacterRecord(L, 1);
	const char *needName = lua_tostring(L, 2);
	int value = static_cast<int>(lua_tointeger(L, 3));
	if (!rec || !needName)
		return 0;
	rec->needs[needName] = value;
	return 0;
}

static int luaGetPoolCurrent(lua_State *L)
{
	auto *rec = getCharacterRecord(L, 1);
	const char *poolName = lua_tostring(L, 2);
	if (!rec || !poolName) {
		lua_pushinteger(L, 0);
		return 1;
	}
	int maxVal = rec->stats.count(poolName) ? rec->stats[poolName] : 0;
	if (maxVal <= 0) {
		lua_pushinteger(L, 0);
		return 1;
	}
	auto it = rec->currentPools.find(poolName);
	if (it == rec->currentPools.end()) {
		lua_pushinteger(L, maxVal);
		return 1;
	}
	int val = it->second;
	if (val < 0)
		val = 0;
	if (val > maxVal)
		val = maxVal;
	lua_pushinteger(L, val);
	return 1;
}

static int luaGetPoolMax(lua_State *L)
{
	auto *rec = getCharacterRecord(L, 1);
	const char *poolName = lua_tostring(L, 2);
	if (!rec || !poolName) {
		lua_pushinteger(L, 0);
		return 1;
	}
	const auto *def = CharacterClassDatabase::getSingleton().findStat(poolName);
	if (!def || def->kind != CharacterClassDatabase::StatKind::ResourcePool) {
		lua_pushinteger(L, 0);
		return 1;
	}
	int val = rec->stats.count(poolName) ? rec->stats[poolName] : 0;
	if (val < def->minValue)
		val = def->minValue;
	if (val > def->maxValue)
		val = def->maxValue;
	lua_pushinteger(L, val);
	return 1;
}

static int luaSetPoolCurrent(lua_State *L)
{
	auto *rec = getCharacterRecord(L, 1);
	const char *poolName = lua_tostring(L, 2);
	int value = static_cast<int>(lua_tointeger(L, 3));
	if (!rec || !poolName) {
		lua_pushboolean(L, 0);
		return 1;
	}
	int maxVal = rec->stats.count(poolName) ? rec->stats[poolName] : 0;
	const auto *def = CharacterClassDatabase::getSingleton().findStat(poolName);
	if (def) {
		if (maxVal < def->minValue)
			maxVal = def->minValue;
		if (maxVal > def->maxValue)
			maxVal = def->maxValue;
	}
	if (maxVal <= 0) {
		lua_pushboolean(L, 0);
		return 1;
	}
	if (value < 0)
		value = 0;
	if (value > maxVal)
		value = maxVal;
	rec->currentPools[poolName] = value;
	lua_pushboolean(L, 1);
	return 1;
}

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

void registerLuaCharacterClassApi(lua_State *L)
{
	lua_getglobal(L, "ecs");
	if (lua_isnil(L, -1)) {
		lua_pop(L, 1);
		lua_newtable(L);
	}

	lua_newtable(L);

	lua_pushcfunction(L, luaGetClassNames);
	lua_setfield(L, -2, "get_class_names");

	lua_pushcfunction(L, luaGetStatNames);
	lua_setfield(L, -2, "get_stat_names");

	lua_pushcfunction(L, luaGetSkillNames);
	lua_setfield(L, -2, "get_skill_names");

	lua_pushcfunction(L, luaGetNeedNames);
	lua_setfield(L, -2, "get_need_names");

	lua_pushcfunction(L, luaGetClass);
	lua_setfield(L, -2, "get_class");

	lua_pushcfunction(L, luaGetStatKind);
	lua_setfield(L, -2, "get_stat_kind");

	lua_pushcfunction(L, luaGetLevel);
	lua_setfield(L, -2, "get_level");

	lua_pushcfunction(L, luaGetXP);
	lua_setfield(L, -2, "get_xp");

	lua_pushcfunction(L, luaAddXP);
	lua_setfield(L, -2, "add_xp");

	lua_pushcfunction(L, luaGetStat);
	lua_setfield(L, -2, "get_stat");

	lua_pushcfunction(L, luaGetSkill);
	lua_setfield(L, -2, "get_skill");

	lua_pushcfunction(L, luaGetNeed);
	lua_setfield(L, -2, "get_need");

	lua_pushcfunction(L, luaGetAvailablePoints);
	lua_setfield(L, -2, "get_available_points");

	lua_pushcfunction(L, luaSetNeed);
	lua_setfield(L, -2, "set_need");

	lua_pushcfunction(L, luaGetPoolCurrent);
	lua_setfield(L, -2, "get_pool_current");

	lua_pushcfunction(L, luaGetPoolMax);
	lua_setfield(L, -2, "get_pool_max");

	lua_pushcfunction(L, luaSetPoolCurrent);
	lua_setfield(L, -2, "set_pool_current");

	lua_setfield(L, -2, "character_class");

	lua_setglobal(L, "ecs");
}

} // namespace editScene
