/**
 * @file event_lua_test.cpp
 * @brief Standalone test for the Lua Event API with EventParams.
 *
 * Tests event subscription, unsubscription, and sending functions
 * exposed via the ecs.* Lua API. Uses the new EventParams type
 * which supports entity IDs, integers, floats, doubles, strings,
 * and arrays of each type.
 *
 * Build with:
 *   g++ -std=c++17 -I. -I../.. -I../../lua/lua-5.4.8/src \
 *       event_lua_test.cpp \
 *       ../lua/LuaEventApi.cpp \
 *       ../lua/LuaEntityApi.cpp \
 *       ../systems/EventBus.cpp \
 *       ../../lua/lua-5.4.8/src/liblua.a \
 *       -o event_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>
}

// Include the components we need
#include "../systems/EventBus.hpp"
#include "../components/EventParams.hpp"

// Forward declare the registration function
namespace editScene
{
void registerLuaEventApi(lua_State *L);
void registerLuaEntityApi(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: Send a simple event
// ---------------------------------------------------------------------------

static int testSendSimpleEvent(lua_State *L)
{
	TEST("send a simple event");

	bool ok = runLua(L, "ecs.send_event('test_event')");
	if (!ok)
		FAIL("failed to send simple event");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 2: Subscribe and receive an event
// ---------------------------------------------------------------------------

static int testSubscribeAndReceive(lua_State *L)
{
	TEST("subscribe and receive an event");

	bool ok = runLua(
		L,
		"local received = false;"
		"local sub_id = ecs.subscribe_event('hello', function(event, params)"
		"    received = true;"
		"end);"
		"assert(sub_id ~= nil, 'subscription ID should not be nil');"
		"assert(type(sub_id) == 'number', 'subscription ID should be a number');"
		"ecs.send_event('hello');"
		"assert(received == true, 'callback should have been called')");
	if (!ok)
		FAIL("subscribe and receive assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 3: Subscribe with event name and params
// ---------------------------------------------------------------------------

static int testSubscribeWithParams(lua_State *L)
{
	TEST("subscribe with event name and params");

	bool ok = runLua(
		L,
		"local received_event = nil;"
		"local sub_id = ecs.subscribe_event('collision', function(event, params)"
		"    received_event = event;"
		"end);"
		"ecs.send_event('collision', { damage = 10 });"
		"assert(received_event == 'collision', 'expected collision event')");
	if (!ok)
		FAIL("subscribe with params assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 4: Unsubscribe from an event
// ---------------------------------------------------------------------------

static int testUnsubscribe(lua_State *L)
{
	TEST("unsubscribe from an event");

	bool ok = runLua(
		L,
		"local call_count = 0;"
		"local sub_id = ecs.subscribe_event('temp', function(event, params)"
		"    call_count = call_count + 1;"
		"end);"
		"ecs.send_event('temp');"
		"assert(call_count == 1, 'should have been called once');"
		"ecs.unsubscribe_event(sub_id);"
		"ecs.send_event('temp');"
		"assert(call_count == 1, 'should not have been called after unsubscribe')");
	if (!ok)
		FAIL("unsubscribe assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 5: Multiple subscribers to same event
// ---------------------------------------------------------------------------

static int testMultipleSubscribers(lua_State *L)
{
	TEST("multiple subscribers to same event");

	bool ok = runLua(
		L,
		"local count1 = 0; local count2 = 0;"
		"local s1 = ecs.subscribe_event('multi', function() count1 = count1 + 1; end);"
		"local s2 = ecs.subscribe_event('multi', function() count2 = count2 + 1; end);"
		"ecs.send_event('multi');"
		"assert(count1 == 1, 'subscriber 1 should have been called');"
		"assert(count2 == 1, 'subscriber 2 should have been called');"
		"ecs.send_event('multi');"
		"assert(count1 == 2, 'subscriber 1 should have been called twice');"
		"assert(count2 == 2, 'subscriber 2 should have been called twice')");
	if (!ok)
		FAIL("multiple subscribers assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 6: Send event with EventParams (flat key-value pairs)
// ---------------------------------------------------------------------------

static int testEventWithParams(lua_State *L)
{
	TEST("send event with EventParams (flat key-value pairs)");

	bool ok = runLua(
		L, "local result = nil;"
		   "ecs.subscribe_event('data_event', function(event, params)"
		   "    result = params;"
		   "end);"
		   "ecs.send_event('data_event', {"
		   "  score = 42,"
		   "  level = 5,"
		   "  speed = 1.5,"
		   "  name = 'hero'"
		   "});"
		   "assert(result ~= nil, 'params should not be nil');"
		   "assert(result.score == 42, 'expected score 42');"
		   "assert(result.level == 5, 'expected level 5');"
		   "assert(result.speed == 1.5, 'expected speed 1.5');"
		   "assert(result.name == 'hero', 'expected name hero')");
	if (!ok)
		FAIL("event with params assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 7: Send event with array params
// ---------------------------------------------------------------------------

static int testEventWithArrayParams(lua_State *L)
{
	TEST("send event with array params");

	bool ok = runLua(
		L, "local result = nil;"
		   "ecs.subscribe_event('array_event', function(event, params)"
		   "    result = params;"
		   "end);"
		   "ecs.send_event('array_event', {"
		   "  positions = { 10, 20, 30 },"
		   "  names = { 'a', 'b', 'c' }"
		   "});"
		   "assert(result ~= nil, 'params should not be nil');"
		   "assert(result.positions[1] == 10, 'expected pos[1]=10');"
		   "assert(result.positions[2] == 20, 'expected pos[2]=20');"
		   "assert(result.positions[3] == 30, 'expected pos[3]=30');"
		   "assert(result.names[1] == 'a', 'expected names[1]=a');"
		   "assert(result.names[2] == 'b', 'expected names[2]=b');"
		   "assert(result.names[3] == 'c', 'expected names[3]=c')");
	if (!ok)
		FAIL("event with array params assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 8: Send event with entity ID params
// ---------------------------------------------------------------------------

static int testEventWithEntityId(lua_State *L)
{
	TEST("send event with entity ID params");

	bool ok = runLua(
		L, "local result = nil;"
		   "ecs.subscribe_event('entity_event', function(event, params)"
		   "    result = params;"
		   "end);"
		   "local eid = ecs.create_entity();"
		   "ecs.send_event('entity_event', {"
		   "  target = eid"
		   "});"
		   "assert(result ~= nil, 'params should not be nil');"
		   "assert(result.target == eid, 'expected target entity ID')");
	if (!ok)
		FAIL("event with entity ID assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 9: Multiple events with different names
// ---------------------------------------------------------------------------

static int testMultipleEvents(lua_State *L)
{
	TEST("multiple events with different names");

	bool ok = runLua(
		L,
		"local events = {};"
		"ecs.subscribe_event('event_a', function() table.insert(events, 'a'); end);"
		"ecs.subscribe_event('event_b', function() table.insert(events, 'b'); end);"
		"ecs.send_event('event_a');"
		"ecs.send_event('event_b');"
		"ecs.send_event('event_a');"
		"assert(#events == 3, 'expected 3 events, got ' .. #events);"
		"assert(events[1] == 'a', 'expected a');"
		"assert(events[2] == 'b', 'expected b');"
		"assert(events[3] == 'a', 'expected a')");
	if (!ok)
		FAIL("multiple events assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 10: Unsubscribe invalid ID (should not crash)
// ---------------------------------------------------------------------------

static int testUnsubscribeInvalid(lua_State *L)
{
	TEST("unsubscribe invalid ID (should not crash)");

	bool ok = runLua(L, "ecs.unsubscribe_event(99999);");
	if (!ok)
		FAIL("unsubscribe invalid ID should not error");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 11: EventParams type metadata (_types table)
// ---------------------------------------------------------------------------

static int testEventParamsTypes(lua_State *L)
{
	TEST("EventParams type metadata (_types table)");

	bool ok = runLua(
		L,
		"local result = nil;"
		"ecs.subscribe_event('typed_event', function(event, params)"
		"    result = params;"
		"end);"
		"ecs.send_event('typed_event', {"
		"  my_int = 42,"
		"  my_float = 3.14,"
		"  my_str = 'hello'"
		"});"
		"assert(result ~= nil, 'params should not be nil');"
		"assert(result._types ~= nil, '_types table should exist');"
		"assert(result._types.my_int == 'int', 'expected int type');"
		"assert(result._types.my_str == 'string', 'expected string type')");
	if (!ok)
		FAIL("EventParams type metadata assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 12: Send event with double precision float
// ---------------------------------------------------------------------------

static int testEventWithDouble(lua_State *L)
{
	TEST("send event with double precision float");

	bool ok = runLua(
		L, "local result = nil;"
		   "ecs.subscribe_event('double_event', function(event, params)"
		   "    result = params;"
		   "end);"
		   "ecs.send_event('double_event', {"
		   "  pi = 3.141592653589793"
		   "});"
		   "assert(result ~= nil, 'params should not be nil');"
		   "assert(math.abs(result.pi - 3.141592653589793) < 0.000001, "
		   "'expected pi value')");
	if (!ok)
		FAIL("event with double assertion failed");

	PASS();
	return 0;
}

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

int main()
{
	printf("Event Lua API Tests (EventParams)\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 entity and event APIs
	editScene::registerLuaEntityApi(L);
	editScene::registerLuaEventApi(L);

	// Run tests
	int failures = 0;
	failures += testSendSimpleEvent(L);
	failures += testSubscribeAndReceive(L);
	failures += testSubscribeWithParams(L);
	failures += testUnsubscribe(L);
	failures += testMultipleSubscribers(L);
	failures += testEventWithParams(L);
	failures += testEventWithArrayParams(L);
	failures += testEventWithEntityId(L);
	failures += testMultipleEvents(L);
	failures += testUnsubscribeInvalid(L);
	failures += testEventParamsTypes(L);
	failures += testEventWithDouble(L);

	// Cleanup
	lua_close(L);

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

	return failures > 0 ? 1 : 0;
}
