#pragma once
#include <Windows.h>
#include <cstdio>
#include <fstream>
#include <sstream>
#include <string>
#include "vex_cheat_data.h"

namespace Exploits
{
    // === Direct Lua Execution via lua_shared.dll ===
    using luaL_loadbufferx_t = int(*)(void* L, const char* buff, size_t sz, const char* name, const char* mode);
    using lua_pcall_t = int(*)(void* L, int nargs, int nresults, int errfunc);
    using lua_settop_t = void(*)(void* L, int index);
    using lua_gettop_t = int(*)(void* L);
    using lua_tolstring_t = const char*(*)(void* L, int idx, size_t* len);

    inline luaL_loadbufferx_t fn_loadbufferx = nullptr;
    inline lua_pcall_t fn_pcall = nullptr;
    inline lua_settop_t fn_settop = nullptr;
    inline lua_gettop_t fn_gettop = nullptr;
    inline lua_tolstring_t fn_tolstring = nullptr;
    inline void* g_ClientLuaState = nullptr;
    inline bool g_LuaReady = false;
    inline bool g_luaRunning = false; // re-entrancy guard

    inline bool InitLuaExec()
    {
        if (g_LuaReady) return true;

        HMODULE hLuaShared = GetModuleHandleA("lua_shared.dll");
        if (!hLuaShared) {
            printf("[-] lua_shared.dll not found\n");
            return false;
        }

        fn_loadbufferx = (luaL_loadbufferx_t)GetProcAddress(hLuaShared, "luaL_loadbufferx");
        fn_pcall = (lua_pcall_t)GetProcAddress(hLuaShared, "lua_pcall");
        fn_settop = (lua_settop_t)GetProcAddress(hLuaShared, "lua_settop");
        fn_gettop = (lua_gettop_t)GetProcAddress(hLuaShared, "lua_gettop");
        fn_tolstring = (lua_tolstring_t)GetProcAddress(hLuaShared, "lua_tolstring");

        if (!fn_loadbufferx || !fn_pcall || !fn_settop || !fn_gettop || !fn_tolstring) {
            printf("[-] Failed to get lua functions: load=%p pcall=%p settop=%p gettop=%p tolstring=%p\n",
                fn_loadbufferx, fn_pcall, fn_settop, fn_gettop, fn_tolstring);
            return false;
        }

        printf("[+] Lua functions resolved from lua_shared.dll\n");

        using CreateInterfaceFn = void*(*)(const char*, int*);
        auto createInterface = (CreateInterfaceFn)GetProcAddress(hLuaShared, "CreateInterface");
        if (!createInterface) {
            printf("[-] CreateInterface not found in lua_shared.dll\n");
            return false;
        }

        void* luaShared = createInterface("LUASHARED003", nullptr);
        if (!luaShared) {
            printf("[-] LUASHARED003 interface not found\n");
            return false;
        }
        printf("[+] ILuaShared = 0x%p\n", luaShared);

        using GetLuaInterfaceFn = void*(__thiscall*)(void*, int);
        void** lsVtable = *(void***)luaShared;
        auto getLuaInterface = (GetLuaInterfaceFn)lsVtable[6];
        void* clientLuaInterface = getLuaInterface(luaShared, 0); // 0 = CLIENT
        if (!clientLuaInterface) {
            printf("[-] Client Lua interface is null (not in game?)\n");
            return false;
        }
        printf("[+] ILuaInterface(CLIENT) = 0x%p\n", clientLuaInterface);

        void** iluaMembers = (void**)clientLuaInterface;
        void* foundState = nullptr;
        int tryOffsets[] = { 1, 2, 3, 4, 5 };
        for (int off : tryOffsets) {
            void* candidate = iluaMembers[off];
            if (!candidate || (uintptr_t)candidate < 0x10000) continue;
            
            __try {
                int top = fn_gettop(candidate);
                if (top >= 0 && top < 1000) {
                    printf("[+] lua_State* found at offset 0x%x = 0x%p (top=%d)\n", off * 8, candidate, top);
                    foundState = candidate;
                    break;
                }
            } __except(1) {
                printf("    offset 0x%x crashed\n", off * 8);
            }
        }

        if (!foundState) {
            printf("[-] Could not find lua_State* in ILuaInterface\n");
            return false;
        }
        
        g_ClientLuaState = foundState;

        // Test running a simple Lua string
        const char* test = "print('[VEX] C++ loader initialized client connection successfully!')";
        __try {
            int top = fn_gettop(g_ClientLuaState);
            int loadResult = fn_loadbufferx(g_ClientLuaState, test, strlen(test), "@lua/vgui/dframe.lua", nullptr);
            if (loadResult == 0) {
                int callResult = fn_pcall(g_ClientLuaState, 0, 0, 0);
                if (callResult != 0) {
                    fn_settop(g_ClientLuaState, top);
                    printf("[-] Lua pcall failed (%d)\n", callResult);
                } else {
                    printf("[+] Lua test executed successfully!\n");
                    g_LuaReady = true;
                }
            } else {
                fn_settop(g_ClientLuaState, top);
                printf("[-] Lua load failed (%d)\n", loadResult);
            }
        } __except(1) {
            printf("[-] Lua execution crashed - wrong lua_State?\n");
            g_ClientLuaState = nullptr;
        }

        return g_LuaReady;
    }

    inline bool RunLua(const char* code)
    {
        if (!g_LuaReady || !g_ClientLuaState || g_luaRunning) return false;
        g_luaRunning = true;
        int top = fn_gettop(g_ClientLuaState);
        bool ok = false;
        __try {
            int lr = fn_loadbufferx(g_ClientLuaState, code, strlen(code), "@lua/vgui/dframe.lua", nullptr);
            if (lr == 0) {
                int callResult = fn_pcall(g_ClientLuaState, 0, 0, 0);
                if (callResult == 0) {
                    ok = true;
                } else {
                    if (fn_tolstring) {
                        const char* err = fn_tolstring(g_ClientLuaState, -1, nullptr);
                        printf("[-] Lua runtime error: %s\n", err ? err : "unknown");
                    } else {
                        printf("[-] Lua runtime error (pcall=%d)\n", callResult);
                    }
                }
            } else {
                if (fn_tolstring) {
                    const char* err = fn_tolstring(g_ClientLuaState, -1, nullptr);
                    printf("[-] Lua compile error: %s\n", err ? err : "unknown");
                } else {
                    printf("[-] Lua compile error (loadbuffer=%d)\n", lr);
                }
            }
            fn_settop(g_ClientLuaState, top); // clean inside try
        } __except(1) {
            __try { fn_settop(g_ClientLuaState, top); } __except(1) {} // clean on crash
        }
        __try { fn_settop(g_ClientLuaState, top); } __except(1) {} // final safety
        g_luaRunning = false;
        return ok;
    }

    inline void* GetCurrentLuaState()
    {
        if (!fn_gettop) return nullptr;

        HMODULE hLuaShared = GetModuleHandleA("lua_shared.dll");
        if (!hLuaShared) return nullptr;

        using CreateInterfaceFn = void*(*)(const char*, int*);
        auto createInterface = (CreateInterfaceFn)GetProcAddress(hLuaShared, "CreateInterface");
        if (!createInterface) return nullptr;

        void* luaShared = createInterface("LUASHARED003", nullptr);
        if (!luaShared) return nullptr;

        using GetLuaInterfaceFn = void*(__thiscall*)(void*, int);
        void** lsVtable = *(void***)luaShared;
        auto getLuaInterface = (GetLuaInterfaceFn)lsVtable[6];
        void* clientLuaInterface = getLuaInterface(luaShared, 0); // 0 = CLIENT
        if (!clientLuaInterface) return nullptr;

        void** iluaMembers = (void**)clientLuaInterface;
        int tryOffsets[] = { 1, 2, 3, 4, 5 };
        for (int off : tryOffsets) {
            void* candidate = iluaMembers[off];
            if (!candidate || (uintptr_t)candidate < 0x10000) continue;
            
            __try {
                int top = fn_gettop(candidate);
                if (top >= 0 && top < 1000) {
                    return candidate;
                }
            } __except(1) {}
        }
        return nullptr;
    }

    inline bool RunLuaBuffer(const char* code, size_t len)
    {
        if (!g_LuaReady || !g_ClientLuaState || g_luaRunning) return false;
        g_luaRunning = true;
        int top = fn_gettop(g_ClientLuaState);
        bool ok = false;
        __try {
            int lr = fn_loadbufferx(g_ClientLuaState, code, len, "@lua/vgui/dframe.lua", nullptr);
            if (lr == 0) {
                int callResult = fn_pcall(g_ClientLuaState, 0, 0, 0);
                if (callResult == 0) {
                    ok = true;
                } else {
                    if (fn_tolstring) {
                        const char* err = fn_tolstring(g_ClientLuaState, -1, nullptr);
                        printf("[-] Lua runtime error: %s\n", err ? err : "unknown");
                    } else {
                        printf("[-] Lua runtime error (pcall=%d)\n", callResult);
                    }
                }
            } else {
                if (fn_tolstring) {
                    const char* err = fn_tolstring(g_ClientLuaState, -1, nullptr);
                    printf("[-] Lua compile error: %s\n", err ? err : "unknown");
                } else {
                    printf("[-] Lua compile error (loadbuffer=%d)\n", lr);
                }
            }
            fn_settop(g_ClientLuaState, top); // clean inside try
        } __except(1) {
            __try { fn_settop(g_ClientLuaState, top); } __except(1) {} // clean on crash
        }
        __try { fn_settop(g_ClientLuaState, top); } __except(1) {} // final safety
        g_luaRunning = false;
        return ok;
    }

    inline bool RunEmbeddedLua()
    {
        if (!InitLuaExec()) {
            printf("[-] Lua state not initialized yet.\n");
            return false;
        }
        printf("[+] Executing embedded Lua cheat (%u bytes)...\n", vex_cheat_lua_len);
        return RunLuaBuffer((const char*)vex_cheat_lua, vex_cheat_lua_len);
    }

    inline bool LoadAndRunLuaFile(const char* filepath)
    {
        if (!InitLuaExec()) {
            printf("[-] Lua state not initialized yet.\n");
            return false;
        }
        
        std::ifstream file(filepath, std::ios::in | std::ios::binary);
        if (!file.is_open()) {
            printf("[-] Failed to open Lua script at %s\n", filepath);
            return false;
        }
        
        std::ostringstream ss;
        ss << file.rdbuf();
        std::string content = ss.str();
        file.close();
        
        printf("[+] Read %zu bytes from %s, executing...\n", content.size(), filepath);
        return RunLua(content.c_str());
    }
}