/**
 * @file save_load_lua_test.cpp
 * @brief Standalone test for the Lua Save/Load API.
 *
 * Tests ecs.register_save_callback, ecs.register_load_callback,
 * and the Lua table <-> JSON conversion utilities.
 *
 * Build with:
 *   g++ -std=c++17 -I. -I../.. -I../../lua/lua-5.4.8/src \
 *       save_load_lua_test.cpp \
 *       lua_test_stubs.cpp \
 *       ../../lua/lua-5.4.8/src/liblua.a \
 *       -o save_load_lua_test -lm
 *
 * Or via CMake (see CMakeLists.txt in this directory).
 */

#include <cstdio>
#include <cstring>
#include <cassert>
#include <string>
#include <vector>
#include <map>

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

// Forward declare the registration function
namespace editScene
{
void registerLuaSaveLoadApi(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: register_save_callback exists and is a function
// ---------------------------------------------------------------------------

static int testRegisterSaveCallbackExists(lua_State *L)
{
	TEST("register_save_callback exists and is a function");

	bool ok = runLua(
		L, "assert(ecs.register_save_callback ~= nil, "
		   "'register_save_callback should exist');"
		   "assert(type(ecs.register_save_callback) == 'function', "
		   "'register_save_callback should be a function')");
	if (!ok)
		FAIL("register_save_callback assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 2: register_load_callback exists and is a function
// ---------------------------------------------------------------------------

static int testRegisterLoadCallbackExists(lua_State *L)
{
	TEST("register_load_callback exists and is a function");

	bool ok = runLua(
		L, "assert(ecs.register_load_callback ~= nil, "
		   "'register_load_callback should exist');"
		   "assert(type(ecs.register_load_callback) == 'function', "
		   "'register_load_callback should be a function')");
	if (!ok)
		FAIL("register_load_callback assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 3: Register a save callback and verify it can be called
// ---------------------------------------------------------------------------

static int testSaveCallbackReturnsTable(lua_State *L)
{
	TEST("save callback returns a table");

	bool ok = runLua(
		L, "local called = false;"
		   "ecs.register_save_callback('test_save1', function() "
		   "    called = true;"
		   "    return { key1 = 'value1', key2 = 42 };"
		   "end);"
		   "assert(called == false, "
		   "'callback should not be called during registration')");
	if (!ok)
		FAIL("save callback registration failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 4: Register a load callback and verify it can be called
// ---------------------------------------------------------------------------

static int testLoadCallbackReceivesData(lua_State *L)
{
	TEST("load callback receives data");

	bool ok = runLua(
		L, "local received = nil;"
		   "ecs.register_load_callback('test_load1', function(data) "
		   "    received = data;"
		   "end);"
		   "assert(received == nil, "
		   "'callback should not be called during registration')");
	if (!ok)
		FAIL("load callback registration failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 5: Multiple save callbacks with different names
// ---------------------------------------------------------------------------

static int testMultipleSaveCallbacks(lua_State *L)
{
	TEST("multiple save callbacks with different names");

	bool ok = runLua(L, "local results = {};"
			    "ecs.register_save_callback('cb_a', function() "
			    "    return { name = 'alpha' };"
			    "end);"
			    "ecs.register_save_callback('cb_b', function() "
			    "    return { name = 'beta', value = 99 };"
			    "end);"
			    "ecs.register_save_callback('cb_c', function() "
			    "    return { nested = { x = 1, y = 2 } };"
			    "end)");
	if (!ok)
		FAIL("multiple save callback registration failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 6: Re-registering a callback replaces the old one
// ---------------------------------------------------------------------------

static int testReregisterCallback(lua_State *L)
{
	TEST("re-registering a callback replaces the old one");

	bool ok = runLua(
		L, "local callCount = 0;"
		   "ecs.register_save_callback('replace_test', function() "
		   "    callCount = callCount + 1;"
		   "    return { version = 1 };"
		   "end);"
		   "ecs.register_save_callback('replace_test', function() "
		   "    callCount = callCount + 1;"
		   "    return { version = 2 };"
		   "end)");
	if (!ok)
		FAIL("re-register callback failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 7: Save callback returning nil (no data)
// ---------------------------------------------------------------------------

static int testSaveCallbackReturnsNil(lua_State *L)
{
	TEST("save callback returning nil");

	bool ok = runLua(L, "ecs.register_save_callback('nil_test', function() "
			    "    return nil;"
			    "end)");
	if (!ok)
		FAIL("nil-returning callback registration failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 8: Save callback returning complex nested table
// ---------------------------------------------------------------------------

static int testSaveCallbackComplexTable(lua_State *L)
{
	TEST("save callback returning complex nested table");

	bool ok = runLua(
		L, "ecs.register_save_callback('complex_test', function() "
		   "    return {"
		   "        string_val = 'hello',"
		   "        number_val = 3.14,"
		   "        int_val = 42,"
		   "        bool_val = true,"
		   "        nested = {"
		   "            inner = 'world',"
		   "            deep = { value = 7 }"
		   "        },"
		   "        array = { 'a', 'b', 'c' },"
		   "        num_array = { 1, 2, 3 }"
		   "    };"
		   "end)");
	if (!ok)
		FAIL("complex callback registration failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 9: Load callback with table data
// ---------------------------------------------------------------------------

static int testLoadCallbackWithTable(lua_State *L)
{
	TEST("load callback with table data");

	bool ok = runLua(
		L, "local received = nil;"
		   "ecs.register_load_callback('load_table_test', "
		   "function(data) "
		   "    received = data;"
		   "end);"
		   "assert(received == nil, "
		   "'callback should not be called during registration')");
	if (!ok)
		FAIL("load callback with table failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 10: Multiple load callbacks
// ---------------------------------------------------------------------------

static int testMultipleLoadCallbacks(lua_State *L)
{
	TEST("multiple load callbacks");

	bool ok = runLua(L,
			 "local receivedA = nil;"
			 "local receivedB = nil;"
			 "ecs.register_load_callback('load_a', function(data) "
			 "    receivedA = data;"
			 "end);"
			 "ecs.register_load_callback('load_b', function(data) "
			 "    receivedB = data;"
			 "end)");
	if (!ok)
		FAIL("multiple load callback registration failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 11: Save callback with empty table
// ---------------------------------------------------------------------------

static int testSaveCallbackEmptyTable(lua_State *L)
{
	TEST("save callback returning empty table");

	bool ok = runLua(L,
			 "ecs.register_save_callback('empty_test', function() "
			 "    return {};"
			 "end)");
	if (!ok)
		FAIL("empty table callback registration failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 12: Verify all registered functions are functions
// ---------------------------------------------------------------------------

static int testAllFunctionsAreCallable(lua_State *L)
{
	TEST("all registered functions are callable");

	bool ok = runLua(
		L, "assert(type(ecs.register_save_callback) == 'function', "
		   "'register_save_callback should be a function');"
		   "assert(type(ecs.register_load_callback) == 'function', "
		   "'register_load_callback should be a function')");
	if (!ok)
		FAIL("function type assertions failed");

	PASS();
	return 0;
}

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

int main()
{
	printf("=== Save/Load Lua API Test ===\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);

	// Create the ecs table
	lua_newtable(L);
	lua_setglobal(L, "ecs");

	// Register the save/load API
	editScene::registerLuaSaveLoadApi(L);

	// Run tests
	int failures = 0;

	failures += testRegisterSaveCallbackExists(L);
	failures += testRegisterLoadCallbackExists(L);
	failures += testSaveCallbackReturnsTable(L);
	failures += testLoadCallbackReceivesData(L);
	failures += testMultipleSaveCallbacks(L);
	failures += testReregisterCallback(L);
	failures += testSaveCallbackReturnsNil(L);
	failures += testSaveCallbackComplexTable(L);
	failures += testLoadCallbackWithTable(L);
	failures += testMultipleLoadCallbacks(L);
	failures += testSaveCallbackEmptyTable(L);
	failures += testAllFunctionsAreCallable(L);

	// Cleanup
	lua_close(L);

	printf("\n=== Results: %d/%d passed ===\n", passCount, testCount);
	if (failures > 0) {
		printf("FAILURES: %d test(s) failed\n", failures);
		return 1;
	}
	return 0;
}
