#include "LuaCharacterApi.hpp"
#include "LuaEntityApi.hpp"
#include "../systems/CharacterRegistry.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: push uint64_t vector as Lua table
// ---------------------------------------------------------------------------

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

// ---------------------------------------------------------------------------
// Character creation & management
// ---------------------------------------------------------------------------

static int luaCharacterCreate(lua_State *L)
{
	const char *firstName = luaL_checkstring(L, 1);
	const char *lastName = luaL_checkstring(L, 2);
	const char *templatePath = lua_tostring(L, 3);
	bool persistent = true;
	if (lua_gettop(L) >= 4)
		persistent = lua_toboolean(L, 4) != 0;

	uint64_t id = CharacterRegistry::getSingleton().createCharacter(
		firstName, lastName, templatePath ? templatePath : "",
		persistent);
	lua_pushinteger(L, static_cast<lua_Integer>(id));
	return 1;
}

static int luaCharacterDelete(lua_State *L)
{
	uint64_t id = static_cast<uint64_t>(luaL_checkinteger(L, 1));
	CharacterRegistry::getSingleton().deleteCharacter(id);
	return 0;
}

static int luaCharacterFind(lua_State *L)
{
	uint64_t id = static_cast<uint64_t>(luaL_checkinteger(L, 1));
	const CharacterRegistry::CharacterRecord *c =
		CharacterRegistry::getSingleton().findCharacter(id);
	if (!c) {
		lua_pushnil(L);
		return 1;
	}

	lua_newtable(L);
	lua_pushinteger(L, static_cast<lua_Integer>(c->id));
	lua_setfield(L, -2, "id");
	lua_pushstring(L, c->firstName.c_str());
	lua_setfield(L, -2, "firstName");
	lua_pushstring(L, c->lastName.c_str());
	lua_setfield(L, -2, "lastName");
	lua_pushstring(L, c->className.c_str());
	lua_setfield(L, -2, "className");
	lua_pushinteger(L, c->level);
	lua_setfield(L, -2, "level");
	lua_pushinteger(L, c->ageYears);
	lua_setfield(L, -2, "ageYears");
	lua_pushstring(L, c->inlineSex.c_str());
	lua_setfield(L, -2, "sex");
	lua_pushboolean(L, c->persistent ? 1 : 0);
	lua_setfield(L, -2, "persistent");
	lua_pushinteger(L,
			static_cast<lua_Integer>(c->pregnantByFatherId));
	lua_setfield(L, -2, "pregnantByFatherId");
	lua_pushnumber(L, c->pregnancyProgress);
	lua_setfield(L, -2, "pregnancyProgress");
	lua_pushnumber(L, c->pregnancyMaxProgress);
	lua_setfield(L, -2, "pregnancyMaxProgress");
	return 1;
}

static int luaCharacterGetAll(lua_State *L)
{
	const auto &chars =
		CharacterRegistry::getSingleton().getCharacters();
	lua_newtable(L);
	int idx = 1;
	for (const auto &pair : chars) {
		lua_pushinteger(L, static_cast<lua_Integer>(pair.first));
		lua_rawseti(L, -2, idx++);
	}
	return 1;
}

static int luaCharacterSetName(lua_State *L)
{
	uint64_t id = static_cast<uint64_t>(luaL_checkinteger(L, 1));
	const char *firstName = luaL_checkstring(L, 2);
	const char *lastName = luaL_checkstring(L, 3);

	CharacterRegistry::CharacterRecord *c =
		CharacterRegistry::getSingleton().findCharacter(id);
	if (c) {
		c->firstName = firstName;
		c->lastName = lastName;
	}
	return 0;
}

// ---------------------------------------------------------------------------
// Spawn / despawn
// ---------------------------------------------------------------------------

static int luaCharacterSpawn(lua_State *L)
{
	uint64_t id = static_cast<uint64_t>(luaL_checkinteger(L, 1));
	flecs::entity e =
		CharacterRegistry::getSingleton().spawnCharacter(id);
	if (!e.is_alive()) {
		lua_pushnil(L);
		return 1;
	}
	int luaId = g_luaEntityIdMap.addEntity(e);
	lua_pushinteger(L, luaId);
	return 1;
}

static int luaCharacterDespawn(lua_State *L)
{
	uint64_t id = static_cast<uint64_t>(luaL_checkinteger(L, 1));
	bool ok = CharacterRegistry::getSingleton().despawnCharacter(id);
	lua_pushboolean(L, ok ? 1 : 0);
	return 1;
}

static int luaCharacterIsSpawned(lua_State *L)
{
	uint64_t id = static_cast<uint64_t>(luaL_checkinteger(L, 1));
	flecs::entity e =
		CharacterRegistry::getSingleton().findSpawnedEntity(id);
	lua_pushboolean(L, e.is_alive() ? 1 : 0);
	return 1;
}

// ---------------------------------------------------------------------------
// Pregnancy
// ---------------------------------------------------------------------------

static int luaCharacterConceive(lua_State *L)
{
	uint64_t motherId =
		static_cast<uint64_t>(luaL_checkinteger(L, 1));
	uint64_t fatherId =
		static_cast<uint64_t>(luaL_checkinteger(L, 2));
	bool ok = CharacterRegistry::getSingleton().conceive(motherId,
							     fatherId);
	lua_pushboolean(L, ok ? 1 : 0);
	return 1;
}

static int luaCharacterAbortPregnancy(lua_State *L)
{
	uint64_t motherId =
		static_cast<uint64_t>(luaL_checkinteger(L, 1));
	CharacterRegistry::getSingleton().abortPregnancy(motherId);
	return 0;
}

static int luaCharacterIsPregnant(lua_State *L)
{
	uint64_t motherId =
		static_cast<uint64_t>(luaL_checkinteger(L, 1));
	bool pregnant = CharacterRegistry::getSingleton().isPregnant(
		motherId);
	lua_pushboolean(L, pregnant ? 1 : 0);
	return 1;
}

static int luaCharacterGetPregnancyProgress(lua_State *L)
{
	uint64_t motherId =
		static_cast<uint64_t>(luaL_checkinteger(L, 1));
	const CharacterRegistry::CharacterRecord *c =
		CharacterRegistry::getSingleton().findCharacter(motherId);
	if (!c || c->pregnantByFatherId == 0) {
		lua_pushnil(L);
		return 1;
	}
	lua_newtable(L);
	lua_pushnumber(L, c->pregnancyProgress);
	lua_setfield(L, -2, "progress");
	lua_pushnumber(L, c->pregnancyMaxProgress);
	lua_setfield(L, -2, "maxProgress");
	lua_pushnumber(L,
			c->pregnancyMaxProgress > 0.0f ?
				c->pregnancyProgress /
					c->pregnancyMaxProgress :
				0.0f);
	lua_setfield(L, -2, "ratio");
	return 1;
}

// ---------------------------------------------------------------------------
// Birth & lineage
// ---------------------------------------------------------------------------

static int luaCharacterCreateChild(lua_State *L)
{
	uint64_t parentA =
		static_cast<uint64_t>(luaL_checkinteger(L, 1));
	uint64_t parentB =
		static_cast<uint64_t>(luaL_checkinteger(L, 2));
	uint64_t childId =
		CharacterRegistry::getSingleton().createChild(parentA,
							      parentB);
	lua_pushinteger(L, static_cast<lua_Integer>(childId));
	return 1;
}

static int luaCharacterGetParents(lua_State *L)
{
	uint64_t childId =
		static_cast<uint64_t>(luaL_checkinteger(L, 1));
	auto parents = CharacterRegistry::getSingleton().getParents(
		childId);
	pushUint64Vector(L, parents);
	return 1;
}

static int luaCharacterGetChildren(lua_State *L)
{
	uint64_t parentId =
		static_cast<uint64_t>(luaL_checkinteger(L, 1));
	auto children = CharacterRegistry::getSingleton().getChildren(
		parentId);
	pushUint64Vector(L, children);
	return 1;
}

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

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

	lua_newtable(L); // ecs.character

	lua_pushcfunction(L, luaCharacterCreate);
	lua_setfield(L, -2, "create");

	lua_pushcfunction(L, luaCharacterDelete);
	lua_setfield(L, -2, "delete");

	lua_pushcfunction(L, luaCharacterFind);
	lua_setfield(L, -2, "find");

	lua_pushcfunction(L, luaCharacterGetAll);
	lua_setfield(L, -2, "get_all");

	lua_pushcfunction(L, luaCharacterSetName);
	lua_setfield(L, -2, "set_name");

	lua_pushcfunction(L, luaCharacterSpawn);
	lua_setfield(L, -2, "spawn");

	lua_pushcfunction(L, luaCharacterDespawn);
	lua_setfield(L, -2, "despawn");

	lua_pushcfunction(L, luaCharacterIsSpawned);
	lua_setfield(L, -2, "is_spawned");

	lua_pushcfunction(L, luaCharacterConceive);
	lua_setfield(L, -2, "conceive");

	lua_pushcfunction(L, luaCharacterAbortPregnancy);
	lua_setfield(L, -2, "abort_pregnancy");

	lua_pushcfunction(L, luaCharacterIsPregnant);
	lua_setfield(L, -2, "is_pregnant");

	lua_pushcfunction(L, luaCharacterGetPregnancyProgress);
	lua_setfield(L, -2, "get_pregnancy_progress");

	lua_pushcfunction(L, luaCharacterCreateChild);
	lua_setfield(L, -2, "create_child");

	lua_pushcfunction(L, luaCharacterGetParents);
	lua_setfield(L, -2, "get_parents");

	lua_pushcfunction(L, luaCharacterGetChildren);
	lua_setfield(L, -2, "get_children");

	lua_setfield(L, -2, "character"); // ecs.character = { ... }
	lua_setglobal(L, "ecs");
}

} // namespace editScene
