/**
 * @file dialogue_lua_test.cpp
 * @brief Standalone test for the Lua Dialogue API.
 *
 * Tests the ecs.dialogue.* functions exposed via the ecs.* Lua API.
 * Tests show/hide, is_active, select_choice, progress, settings,
 * and save/load settings.
 *
 * Build with:
 *   g++ -std=c++17 -I. -I../.. -I../../lua/lua-5.4.8/src \
 *       dialogue_lua_test.cpp \
 *       lua_test_stubs.cpp \
 *       ../../lua/lua-5.4.8/src/liblua.a \
 *       -o dialogue_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>
}

// Forward declare the registration function
namespace editScene
{
void registerLuaDialogueApi(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: Show dialogue with text only
// ---------------------------------------------------------------------------

static int testShowTextOnly(lua_State *L)
{
	TEST("show dialogue with text only");

	bool ok = runLua(L, "ecs.dialogue.show('Hello world');"
			    "assert(ecs.dialogue.is_active() == false, "
			    "'stub returns false for is_active')");
	if (!ok)
		FAIL("show text only assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 2: Show dialogue with text and speaker
// ---------------------------------------------------------------------------

static int testShowWithSpeaker(lua_State *L)
{
	TEST("show dialogue with text and speaker");

	bool ok = runLua(L, "ecs.dialogue.show('Welcome!', {}, 'Narrator');"
			    "assert(ecs.dialogue.is_active() == false, "
			    "'stub returns false for is_active')");
	if (!ok)
		FAIL("show with speaker assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 3: Show dialogue with choices
// ---------------------------------------------------------------------------

static int testShowWithChoices(lua_State *L)
{
	TEST("show dialogue with choices");

	bool ok = runLua(L, "ecs.dialogue.show('Choose:', "
			    "{ 'Option A', 'Option B', 'Option C' }, 'Guide');"
			    "assert(ecs.dialogue.is_active() == false, "
			    "'stub returns false for is_active')");
	if (!ok)
		FAIL("show with choices assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 4: Hide dialogue
// ---------------------------------------------------------------------------

static int testHide(lua_State *L)
{
	TEST("hide dialogue");

	bool ok = runLua(L, "ecs.dialogue.show('Test');"
			    "ecs.dialogue.hide();"
			    "assert(ecs.dialogue.is_active() == false, "
			    "'should be inactive after hide')");
	if (!ok)
		FAIL("hide assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 5: Select choice
// ---------------------------------------------------------------------------

static int testSelectChoice(lua_State *L)
{
	TEST("select choice");

	bool ok = runLua(L, "ecs.dialogue.show('Pick one:', "
			    "{ 'A', 'B' }, 'Test');"
			    "ecs.dialogue.select_choice(1);"
			    "ecs.dialogue.select_choice(2);"
			    "ecs.dialogue.select_choice(0);");
	if (!ok)
		FAIL("select choice assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 6: Progress (click-to-dismiss)
// ---------------------------------------------------------------------------

static int testProgress(lua_State *L)
{
	TEST("progress (click-to-dismiss)");

	bool ok = runLua(L, "ecs.dialogue.show('Narration...');"
			    "ecs.dialogue.progress();");
	if (!ok)
		FAIL("progress assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 7: Get settings returns a table with expected fields
// ---------------------------------------------------------------------------

static int testGetSettings(lua_State *L)
{
	TEST("get_settings returns table with expected fields");

	bool ok = runLua(
		L, "local s = ecs.dialogue.get_settings();"
		   "assert(type(s) == 'table', 'settings should be a table');"
		   "assert(type(s.font_name) == 'string', "
		   "'font_name should be string');"
		   "assert(type(s.font_size) == 'number', "
		   "'font_size should be number');"
		   "assert(type(s.speaker_font_size) == 'number', "
		   "'speaker_font_size should be number');"
		   "assert(type(s.background_opacity) == 'number', "
		   "'background_opacity should be number');"
		   "assert(type(s.box_height_fraction) == 'number', "
		   "'box_height_fraction should be number');"
		   "assert(type(s.box_position_fraction) == 'number', "
		   "'box_position_fraction should be number');");
	if (!ok)
		FAIL("get_settings assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 8: Set settings
// ---------------------------------------------------------------------------

static int testSetSettings(lua_State *L)
{
	TEST("set_settings");

	bool ok = runLua(L, "ecs.dialogue.set_settings({"
			    "  font_name = 'Custom.ttf',"
			    "  font_size = 18.0,"
			    "  speaker_font_size = 16.0,"
			    "  background_opacity = 0.9,"
			    "  box_height_fraction = 0.3,"
			    "  box_position_fraction = 0.7"
			    "});");
	if (!ok)
		FAIL("set_settings assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 9: Save settings returns boolean
// ---------------------------------------------------------------------------

static int testSaveSettings(lua_State *L)
{
	TEST("save_settings returns boolean");

	bool ok = runLua(
		L, "local ok = ecs.dialogue.save_settings();"
		   "assert(type(ok) == 'boolean', "
		   "'save_settings should return boolean');"
		   "local ok2 = ecs.dialogue.save_settings('custom.json');"
		   "assert(type(ok2) == 'boolean', "
		   "'save_settings with path should return boolean')");
	if (!ok)
		FAIL("save_settings assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 10: Load settings returns boolean
// ---------------------------------------------------------------------------

static int testLoadSettings(lua_State *L)
{
	TEST("load_settings returns boolean");

	bool ok = runLua(
		L, "local ok = ecs.dialogue.load_settings();"
		   "assert(type(ok) == 'boolean', "
		   "'load_settings should return boolean');"
		   "local ok2 = ecs.dialogue.load_settings('custom.json');"
		   "assert(type(ok2) == 'boolean', "
		   "'load_settings with path should return boolean')");
	if (!ok)
		FAIL("load_settings assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 11: Return types are correct for all functions
// ---------------------------------------------------------------------------

static int testReturnTypes(lua_State *L)
{
	TEST("return types are correct");

	bool ok = runLua(L, "assert(ecs.dialogue.show == nil or "
			    "type(ecs.dialogue.show) == 'function', "
			    "'show should be a function');"
			    "assert(ecs.dialogue.hide == nil or "
			    "type(ecs.dialogue.hide) == 'function', "
			    "'hide should be a function');"
			    "assert(ecs.dialogue.is_active == nil or "
			    "type(ecs.dialogue.is_active) == 'function', "
			    "'is_active should be a function');"
			    "assert(ecs.dialogue.select_choice == nil or "
			    "type(ecs.dialogue.select_choice) == 'function', "
			    "'select_choice should be a function');"
			    "assert(ecs.dialogue.progress == nil or "
			    "type(ecs.dialogue.progress) == 'function', "
			    "'progress should be a function');"
			    "assert(ecs.dialogue.get_settings == nil or "
			    "type(ecs.dialogue.get_settings) == 'function', "
			    "'get_settings should be a function');"
			    "assert(ecs.dialogue.set_settings == nil or "
			    "type(ecs.dialogue.set_settings) == 'function', "
			    "'set_settings should be a function');"
			    "assert(ecs.dialogue.save_settings == nil or "
			    "type(ecs.dialogue.save_settings) == 'function', "
			    "'save_settings should be a function');"
			    "assert(ecs.dialogue.load_settings == nil or "
			    "type(ecs.dialogue.load_settings) == 'function', "
			    "'load_settings should be a function')");
	if (!ok)
		FAIL("return types assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 12: Show with empty choices table
// ---------------------------------------------------------------------------

static int testShowEmptyChoices(lua_State *L)
{
	TEST("show with empty choices table");

	bool ok = runLua(L, "ecs.dialogue.show('No choices', {}, 'Speaker');"
			    "assert(ecs.dialogue.is_active() == false, "
			    "'stub returns false for is_active')");
	if (!ok)
		FAIL("show empty choices assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 13: Show with nil speaker
// ---------------------------------------------------------------------------

static int testShowNilSpeaker(lua_State *L)
{
	TEST("show with nil speaker");

	bool ok = runLua(L, "ecs.dialogue.show('No speaker');"
			    "assert(ecs.dialogue.is_active() == false, "
			    "'stub returns false for is_active')");
	if (!ok)
		FAIL("show nil speaker assertion failed");

	PASS();
	return 0;
}

// ---------------------------------------------------------------------------
// Test 14: Settings round-trip (get -> modify -> set -> get)
// ---------------------------------------------------------------------------

static int testSettingsRoundTrip(lua_State *L)
{
	TEST("settings round-trip");

	bool ok = runLua(L, "local s = ecs.dialogue.get_settings();"
			    "s.font_size = 30.0;"
			    "s.background_opacity = 0.5;"
			    "ecs.dialogue.set_settings(s);"
			    "local s2 = ecs.dialogue.get_settings();"
			    "assert(type(s2) == 'table', "
			    "'settings after set should be a table')");
	if (!ok)
		FAIL("settings round-trip assertion failed");

	PASS();
	return 0;
}

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

int main()
{
	printf("Dialogue Lua API Tests\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 dialogue API
	editScene::registerLuaDialogueApi(L);

	// Run tests
	int failures = 0;
	failures += testShowTextOnly(L);
	failures += testShowWithSpeaker(L);
	failures += testShowWithChoices(L);
	failures += testHide(L);
	failures += testSelectChoice(L);
	failures += testProgress(L);
	failures += testGetSettings(L);
	failures += testSetSettings(L);
	failures += testSaveSettings(L);
	failures += testLoadSettings(L);
	failures += testReturnTypes(L);
	failures += testShowEmptyChoices(L);
	failures += testShowNilSpeaker(L);
	failures += testSettingsRoundTrip(L);

	// Cleanup
	lua_close(L);

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

	return failures > 0 ? 1 : 0;
}
