/**
 * @file character_lua_test.cpp
 * @brief Standalone test for the Lua Character API.
 *
 * Tests runtime character creation, pregnancy, birth, lineage,
 * spawn/despawn, and birth-event tracking via the ecs.character.*
 * Lua API.
 *
 * Examples included in test comments show how to use each API
 * from Lua game scripts.
 *
 * Build with:
 *   g++ -std=c++17 -I. -I../.. -I../../lua/lua-5.4.8/src \
 *       character_lua_test.cpp \
 *       ../lua/LuaEntityApi.cpp \
 *       ../lua/LuaEventApi.cpp \
 *       ../lua/LuaCharacterApi.cpp \
 *       ../../lua/lua-5.4.8/src/liblua.a \
 *       -o character_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>
}

// Flecs stub for standalone testing
namespace flecs
{

struct entity {
	uint64_t m_id = 0;
	bool m_valid = false;

	entity()
		: m_id(0)
		, m_valid(false)
	{
	}
	explicit entity(uint64_t id)
		: m_id(id)
		, m_valid(true)
	{
	}

	uint64_t id() const
	{
		return m_id;
	}
	bool is_valid() const
	{
		return m_valid;
	}
	bool is_alive() const
	{
		return m_valid;
	}
	const char *name() const
	{
		return "";
	}
	void set_name(const char *)
	{
	}
	void destruct()
	{
		m_valid = false;
	}

	entity parent() const
	{
		return entity();
	}

	template <typename Func> void children(Func) const
	{
	}

	template <typename T> void add()
	{
	}

	template <typename T> bool has() const
	{
		return false;
	}

	template <typename T> const T *get() const
	{
		return nullptr;
	}

	template <typename T> void set(const T &)
	{
	}

	bool operator==(const entity &other) const
	{
		return m_id == other.m_id;
	}
};

using entity_t = uint64_t;

struct world {
	entity make_entity()
	{
		return entity(nextId++);
	}
	entity lookup(const char *)
	{
		return entity();
	}

	static world &get()
	{
		static world w;
		return w;
	}

private:
	uint64_t nextId = 1000;
};

} // namespace flecs

// Forward declare registration functions
namespace editScene
{
void registerLuaEntityApi(lua_State *L);
void registerLuaEventApi(lua_State *L);
void registerLuaCharacterApi(lua_State *L);
void clearStubCharacters();
}

// ---------------------------------------------------------------------------
// 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)

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: Create persistent and runtime characters
// ---------------------------------------------------------------------------

static int testCreateCharacters(lua_State *L)
{
	TEST("create persistent and runtime characters");

	/*
	 * Example: Create a persistent roster character
	 *   local id = ecs.character.create("Alice", "Smith", "", true)
	 *
	 * Example: Create a runtime-only (temporary) character
	 *   local id = ecs.character.create("Bandit", "", "", false)
	 */

	bool ok = runLua(
		L,
		"local p = ecs.character.create('Alice', 'Smith', '', true);"
		"assert(type(p) == 'number', 'persistent id should be number');"
		"assert(p > 0, 'persistent id should be positive');"
		"local r = ecs.character.create('Bandit', '', '', false);"
		"assert(type(r) == 'number', 'runtime id should be number');"
		"assert(r > p, 'runtime id should be larger than persistent');"
		"local c = ecs.character.find(p);"
		"assert(c ~= nil, 'should find persistent char');"
		"assert(c.firstName == 'Alice', 'name mismatch');"
		"assert(c.persistent == true, 'should be persistent');"
		"local c2 = ecs.character.find(r);"
		"assert(c2.persistent == false, 'should be runtime');");
	if (!ok)
		FAIL("create characters assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 2: Character listing and name changes
// ---------------------------------------------------------------------------

static int testCharacterListingAndNames(lua_State *L)
{
	TEST("list characters and change names");

	/*
	 * Example: List all characters
	 *   local all = ecs.character.get_all()
	 *   for i, id in ipairs(all) do ... end
	 *
	 * Example: Rename a character
	 *   ecs.character.set_name(id, "NewFirst", "NewLast")
	 */

	bool ok = runLua(
		L,
		"ecs.character.create('Bob', 'Jones', '', true);"
		"ecs.character.create('Carol', 'Dane', '', true);"
		"local all = ecs.character.get_all();"
		"assert(#all >= 2, 'should have at least 2 chars');"
		"local first = all[1];"
		"ecs.character.set_name(first, 'Robert', 'Jones-Junior');"
		"local c = ecs.character.find(first);"
		"assert(c.firstName == 'Robert', 'first name not updated');"
		"assert(c.lastName == 'Jones-Junior', 'last name not updated');");
	if (!ok)
		FAIL("listing/names assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 3: Spawn and despawn
// ---------------------------------------------------------------------------

static int testSpawnDespawn(lua_State *L)
{
	TEST("spawn and despawn characters");

	/*
	 * Example: Spawn a character into the world
	 *   local entityId = ecs.character.spawn(charId)
	 *   if entityId ~= nil then ... end
	 *
	 * Example: Check if spawned
	 *   if ecs.character.is_spawned(charId) then ... end
	 *
	 * Example: Despawn
	 *   ecs.character.despawn(charId)
	 */

	bool ok = runLua(
		L,
		"local id = ecs.character.create('Dave', 'Miller', '', true);"
		"assert(ecs.character.is_spawned(id) == false, 'should not be spawned');"
		"local eid = ecs.character.spawn(id);"
		"assert(eid ~= nil, 'spawn should return entity id');"
		"assert(type(eid) == 'number', 'entity id should be number');"
		"assert(ecs.character.is_spawned(id) == true, 'should be spawned');"
		"local ok = ecs.character.despawn(id);"
		"assert(ok == true, 'despawn should succeed');"
		"assert(ecs.character.is_spawned(id) == false, 'should not be spawned after despawn');");
	if (!ok)
		FAIL("spawn/despawn assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 4: Conceive and pregnancy state
// ---------------------------------------------------------------------------

static int testConceiveAndPregnancy(lua_State *L)
{
	TEST("conceive and query pregnancy state");

	/*
	 * Example: Make a character pregnant
	 *   local ok = ecs.character.conceive(motherId, fatherId)
	 *   if ok then ... end
	 *
	 * Example: Check pregnancy
	 *   if ecs.character.is_pregnant(motherId) then ... end
	 *
	 * Example: Get progress
	 *   local prog = ecs.character.get_pregnancy_progress(motherId)
	 *   print(prog.progress .. " / " .. prog.maxProgress)
	 */

	bool ok = runLua(
		L,
		"local mother = ecs.character.create('Eve', 'Adams', '', true);"
		"local father = ecs.character.create('Adam', 'Adams', '', true);"
		"assert(ecs.character.is_pregnant(mother) == false, 'should not be pregnant initially');"
		"local ok = ecs.character.conceive(mother, father);"
		"assert(ok == true, 'conceive should succeed');"
		"assert(ecs.character.is_pregnant(mother) == true, 'should be pregnant');"
		"local prog = ecs.character.get_pregnancy_progress(mother);"
		"assert(prog ~= nil, 'progress should not be nil');"
		"assert(prog.progress == 0, 'initial progress should be 0');"
		"assert(prog.maxProgress > 0, 'maxProgress should be positive');"
		"assert(prog.ratio == 0, 'initial ratio should be 0');");
	if (!ok)
		FAIL("conceive/pregnancy assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 5: Abort pregnancy
// ---------------------------------------------------------------------------

static int testAbortPregnancy(lua_State *L)
{
	TEST("abort pregnancy");

	/*
	 * Example: Abort a pregnancy
	 *   ecs.character.abort_pregnancy(motherId)
	 */

	bool ok = runLua(
		L,
		"local mother = ecs.character.create('Fay', 'Wray', '', true);"
		"local father = ecs.character.create('King', 'Kong', '', true);"
		"ecs.character.conceive(mother, father);"
		"assert(ecs.character.is_pregnant(mother) == true);"
		"ecs.character.abort_pregnancy(mother);"
		"assert(ecs.character.is_pregnant(mother) == false, 'should not be pregnant after abort');"
		"local prog = ecs.character.get_pregnancy_progress(mother);"
		"assert(prog == nil, 'progress should be nil after abort');");
	if (!ok)
		FAIL("abort pregnancy assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 6: Create child and lineage
// ---------------------------------------------------------------------------

static int testCreateChildAndLineage(lua_State *L)
{
	TEST("create child and query lineage");

	/*
	 * Example: Create a child from two parents
	 *   local childId = ecs.character.create_child(fatherId, motherId)
	 *
	 * Example: Get parents of a child
	 *   local parents = ecs.character.get_parents(childId)
	 *
	 * Example: Get children of a parent
	 *   local kids = ecs.character.get_children(parentId)
	 */

	bool ok = runLua(
		L,
		"local dad = ecs.character.create('Homer', 'Simpson', '', true);"
		"local mom = ecs.character.create('Marge', 'Simpson', '', true);"
		"local child = ecs.character.create_child(dad, mom);"
		"assert(type(child) == 'number', 'child id should be number');"
		"local parents = ecs.character.get_parents(child);"
		"assert(#parents == 2, 'should have 2 parents');"
		"local dadKids = ecs.character.get_children(dad);"
		"assert(#dadKids == 1, 'dad should have 1 child');"
		"assert(dadKids[1] == child, 'dad child should match');"
		"local momKids = ecs.character.get_children(mom);"
		"assert(#momKids == 1, 'mom should have 1 child');"
		"assert(momKids[1] == child, 'mom child should match');");
	if (!ok)
		FAIL("child/lineage assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 7: Birth event tracking via EventBus
// ---------------------------------------------------------------------------

static int testBirthEvent(lua_State *L)
{
	TEST("subscribe to birth event and receive it");

	/*
	 * Example: Listen for birth events
	 *   ecs.subscribe_event('birth', function(event, params)
	 *       print('Birth! Mother=' .. params.mother)
	 *       print('Father=' .. params.father)
	 *       print('Child=' .. params.child)
	 *   end)
	 *
	 * Example: Manually send a birth event (for scripted births)
	 *   ecs.send_event('birth', { mother = momId, father = dadId, child = babyId })
	 */

	bool ok = runLua(
		L,
		"local birthData = nil;"
		"local sub = ecs.subscribe_event('birth', function(event, params)\n"
		"    birthData = params;\n"
		"end);"
		"local mom = ecs.character.create('Sarah', 'Connor', '', true);"
		"local dad = ecs.character.create('Kyle', 'Reese', '', true);"
		"local baby = ecs.character.create_child(dad, mom);"
		"ecs.send_event('birth', { mother = mom, father = dad, child = baby });"
		"assert(birthData ~= nil, 'birth event should have been received');"
		"assert(birthData.mother == mom, 'mother id mismatch');"
		"assert(birthData.father == dad, 'father id mismatch');"
		"assert(birthData.child == baby, 'child id mismatch');");
	if (!ok)
		FAIL("birth event assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 8: Delete character
// ---------------------------------------------------------------------------

static int testDeleteCharacter(lua_State *L)
{
	TEST("delete character");

	/*
	 * Example: Delete a character permanently
	 *   ecs.character.delete(charId)
	 */

	bool ok = runLua(
		L,
		"local id = ecs.character.create('Goner', 'Dead', '', true);"
		"assert(ecs.character.find(id) ~= nil, 'should exist');"
		"ecs.character.delete(id);"
		"assert(ecs.character.find(id) == nil, 'should not exist after delete');");
	if (!ok)
		FAIL("delete character assertion failed");

	PASS();
	return 0;
}

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

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

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

	// Register APIs
	editScene::registerLuaEntityApi(L);
	editScene::registerLuaEventApi(L);
	editScene::registerLuaCharacterApi(L);

	// Run tests (clear stub state between each for isolation)
	int failures = 0;

	editScene::clearStubCharacters();
	failures += testCreateCharacters(L);

	editScene::clearStubCharacters();
	failures += testCharacterListingAndNames(L);

	editScene::clearStubCharacters();
	failures += testSpawnDespawn(L);

	editScene::clearStubCharacters();
	failures += testConceiveAndPregnancy(L);

	editScene::clearStubCharacters();
	failures += testAbortPregnancy(L);

	editScene::clearStubCharacters();
	failures += testCreateChildAndLineage(L);

	editScene::clearStubCharacters();
	failures += testBirthEvent(L);

	editScene::clearStubCharacters();
	failures += testDeleteCharacter(L);

	lua_close(L);

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

	return failures > 0 ? 1 : 0;
}
