// ── WebRadarCPP — Entry Point ─────────────────────────────────────────────────
// Init sequence:
//   1. Load config
//   2. Setup memory (handle hijacking → cs2.exe)
//   3. Setup interfaces (pattern scans → schema system, entity list, global vars)
//   4. Setup schema (runtime enumeration of all class fields)
//   5. Init network (WSAStartup + curl_global_init)
//   6. Extract & start Node.js relay server
//   7. Connect WebSocket client to relay
//   8. Main loop: sdk::update() + f::run() + ws->send(json)
//   9. On exit: kill server, cleanup

#include "pch.hpp"

#include <csignal>
#include <iostream>

// libcurl for HTTP (avatar downloads)
#include <curl/curl.h>

#include "common.hpp"
#include "core/memory.hpp"
#include "core/interfaces.hpp"
#include "core/schema.hpp"
#include "core/sdk.hpp"
#include "sdk/entity.hpp"
#include "features/features.hpp"
#include "Embed.hpp"
#include "server/webradar_config.h"
#include "easywsclient.hpp"

namespace fs = std::filesystem;

static std::atomic<bool> g_running{ false };
static easywsclient::WebSocket::pointer g_ws = nullptr;
static std::thread g_main_thread;
static HANDLE g_mutex = nullptr;

static void cleanup()
{
    embed::kill_server();

    if (g_ws)
    {
        g_ws->close();
        g_ws->poll(100);
        delete g_ws;
        g_ws = nullptr;
    }

    if (g_mutex)
    {
        CloseHandle(g_mutex);
        g_mutex = nullptr;
    }
}

static std::string find_working_directory()
{
    wchar_t exe_path[MAX_PATH];
    GetModuleFileNameW(nullptr, exe_path, MAX_PATH);
    fs::path dir = fs::path(exe_path).parent_path();

    // Look for a directory containing BOTH the webapp/ frontend and the config file.
    // webradar.config.json is copied to the build output by the vcxproj, so checking
    // for the config alone would match build/ prematurely; we require webapp/ as well.
    for (int i = 0; i < 5; i++)
    {
        if (fs::exists(dir / "webapp") && fs::exists(dir / "webradar.config.json"))
            return dir.string();
        dir = dir.parent_path();
    }

    return fs::path(exe_path).parent_path().string();
}

static bool start_radar(const std::string& working_dir, std::string& error_out)
{
    // 1. Load config
    fs::path config_path = fs::path(working_dir) / "webradar.config.json";
    auto config = WebRadarConfig::load(config_path.string());
    LOG_INFO("config loaded: port=%d, asset_root=%s", config.websocket_port, config.web_asset_root.c_str());

    // 2. Setup memory (handle hijacking)
    if (!m_memory->setup(error_out))
        return false;
    LOG_INFO("memory initialization completed");

    // 3. Setup interfaces (pattern scans)
    if (!i::setup())
    {
        error_out = "Failed to resolve game interfaces.\n\nThe game may have updated.";
        return false;
    }
    LOG_INFO("interfaces initialization completed");

    // 4. Setup schema (runtime enumeration)
    if (!schema::setup())
    {
        error_out = "Failed to build schema table.\n\nTry restarting CS2.";
        return false;
    }
    LOG_INFO("schema initialization completed");

    // 5. Init network (Winsock + libcurl)
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2, 2), &wsaData);
    curl_global_init(CURL_GLOBAL_DEFAULT);
    LOG_INFO("network initialization completed");

    // 6. Extract and start Node.js relay server (with asset root for frontend)
    LOG_INFO("starting relay server...");
    {
        fs::path asset_root = fs::absolute(fs::path(working_dir) / config.web_asset_root);
        if (!embed::ensure_server_running(asset_root.string()))
        {
            LOG_WARNING("relay server may not have started — possible missing embedded resource");
        }
        else
        {
            LOG_INFO("relay server started (asset_root=%s)", asset_root.string().c_str());
        }
    }

    // Give the server a moment to start
    std::this_thread::sleep_for(std::chrono::milliseconds(500));

    // 7. Connect WebSocket client to relay
    const std::string ws_url = "ws://localhost:22006/ws";
    LOG_INFO("connecting to relay at %s", ws_url.c_str());

    g_ws = easywsclient::WebSocket::from_url(ws_url);
    if (!g_ws)
    {
        error_out = "Could not connect to the relay server.\n\nTry restarting WebRadar.";
        return false;
    }

    // Wait for connection to be established
    g_ws->poll(500);
    if (g_ws->getReadyState() != easywsclient::WebSocket::OPEN)
    {
        delete g_ws;
        g_ws = nullptr;
        error_out = "Could not connect to the relay server (timeout).\n\nTry restarting WebRadar.";
        return false;
    }

    LOG_INFO("connected to relay server");

    // 8. Run the main data loop
    g_running = true;

    while (g_running.load())
    {
        sdk::update();
        f::run();

        const auto json_str = f::m_data.dump();

        if (g_ws && g_ws->getReadyState() == easywsclient::WebSocket::OPEN)
        {
            g_ws->send(json_str);
        }
        else if (g_ws && g_ws->getReadyState() == easywsclient::WebSocket::CLOSED)
        {
            // Try to reconnect
            LOG_WARNING("WebSocket disconnected, attempting reconnect...");
            delete g_ws;

            g_ws = easywsclient::WebSocket::from_url(ws_url);
            if (g_ws)
            {
                g_ws->poll(500);
                if (g_ws->getReadyState() == easywsclient::WebSocket::OPEN)
                {
                    LOG_INFO("reconnected to relay");
                }
            }
        }

        // Poll for incoming messages (shutdown commands, etc.)
        if (g_ws)
        {
            g_ws->poll(0);
            g_ws->dispatch([](const std::string& msg)
            {
                try
                {
                    const auto j = nlohmann::json::parse(msg);
                    if (j.value("cmd", "") == "shutdown")
                    {
                        LOG_INFO("shutdown requested from web UI");
                        g_running = false;
                    }
                }
                catch (...) {}
            });
        }

        std::this_thread::sleep_for(std::chrono::milliseconds(33)); // ~30 Hz
    }

    return true;
}

static bool console_ctrl_handler(DWORD dwCtrlType)
{
    if (dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT ||
        dwCtrlType == CTRL_CLOSE_EVENT)
    {
        LOG_INFO("Console shutdown event received");
        g_running = false;
        return TRUE;
    }
    return FALSE;
}

int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int)
{
    // Single-instance mutex
    g_mutex = CreateMutexW(nullptr, TRUE, L"Global\\WebRadarCPP");
    if (GetLastError() == ERROR_ALREADY_EXISTS)
    {
        MessageBoxA(nullptr, "WebRadar is already running.", "WebRadarCPP", MB_OK | MB_ICONINFORMATION);
        return 1;
    }

    // Console for debugging
    AllocConsole();
    freopen("CONOUT$", "w", stdout);
    SetConsoleTitleW(L"WebRadarCPP");

    SetConsoleCtrlHandler((PHANDLER_ROUTINE)console_ctrl_handler, TRUE);

    printf("╔══════════════════════════════════════════════╗\n");
    printf("║       WebRadarCPP — C++ Backend              ║\n");
    printf("╚══════════════════════════════════════════════╝\n");
    LOG_INFO("WebRadar starting...");

    // Working directory
    std::string working_dir = find_working_directory();
    LOG_INFO("working directory: %s", working_dir.c_str());
    fs::current_path(working_dir);

    // Check admin
    {
        BOOL is_admin = FALSE;
        PSID admin_group = nullptr;
        SID_IDENTIFIER_AUTHORITY nt_auth = SECURITY_NT_AUTHORITY;
        if (AllocateAndInitializeSid(&nt_auth, 2, SECURITY_BUILTIN_DOMAIN_RID,
            DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &admin_group))
        {
            CheckTokenMembership(nullptr, admin_group, &is_admin);
            FreeSid(admin_group);
        }
        if (!is_admin)
            LOG_WARNING("not running as administrator — memory reads may fail");
        else
            LOG_INFO("running with administrator privileges");
    }

    // Start radar
    std::string error;
    if (!start_radar(working_dir, error))
    {
        LOG_ERROR("radar failed: %s", error.c_str());
        MessageBoxA(nullptr, error.c_str(), "WebRadarCPP Error", MB_OK | MB_ICONERROR);
        cleanup();
        return 1;
    }

    // Cleanup
    cleanup();
    LOG_INFO("WebRadar stopped");

    FreeConsole();
    return 0;
}
