#include <atomic>
#include <chrono>
#include <filesystem>

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <Windows.h>

#include <imgui.h>
#include "Config.h"
#include "Logger.h"
#include "Overlay/GameOverlay.h"
#include "Overlay/OverlayWindow.h"
#include "Runtime/DayZRuntimeService.h"
#include "Web/WebRadarServer.h"

// Shared stop flag so the console Ctrl+C / close handler can reach it.
static std::atomic<bool>* g_stopFlag = nullptr;

static BOOL WINAPI ConsoleCtrlHandler(DWORD signal) {
    if (signal == CTRL_C_EVENT  || signal == CTRL_CLOSE_EVENT ||
        signal == CTRL_BREAK_EVENT)
    {
        if (g_stopFlag) g_stopFlag->store(true);
        return TRUE;
    }
    return FALSE;
}

int main() {
    std::filesystem::create_directories("logs");
    AppLogger::Init();
    auto log = AppLogger::Get();
    log->info("DayZ Memory Reader - Starting");

    RuntimeConfig rtCfg = RuntimeConfig::Load("config/config.cfg");
    DayZRuntimeService service(rtCfg);

    // -------------------------------------------------------------------------
    // Config — loaded from config/overlay.json (defaults if missing).
    // -------------------------------------------------------------------------
    static const std::string kCfgPath = "config/overlay.json";
    OverlayConfig cfg = OverlayConfig::Load(kCfgPath);

    // -------------------------------------------------------------------------
    // Web radar server — settings come from config.
    // -------------------------------------------------------------------------
    WebRadarConfig webCfg;
    webCfg.bindAddress = cfg.webBindAddress;
    webCfg.port        = cfg.webPort;
    webCfg.password    = cfg.webPassword;

    WebRadarServer webRadar(webCfg);
    webRadar.Start();

    // -------------------------------------------------------------------------
    // Runtime service callback: log stats + push snapshot to web radar.
    // -------------------------------------------------------------------------

    service.SetUpdateCallback([&log, &webRadar](const RuntimeUpdate& update) {
        // Always push to the web radar (even when not yet attached) so the
        // browser client sees connection/status changes immediately.
        webRadar.PushSnapshot(update);

        if (!update.areBaseObjectsReady) {
            // Status messages are already de-duplicated inside WaitForSession.
            return;
        }

        // Periodic heartbeat at most once every 10 seconds.  Kept terse — the
        // interesting per-event detail (admins detected, skeletons lost) is logged
        // as it happens by the runtime service, not polled here.
        using Clock = std::chrono::steady_clock;
        static auto nextLog = Clock::time_point{};
        auto now = Clock::now();
        if (now < nextLog) return;
        nextLog = now + std::chrono::seconds(10);

        // Skeleton coverage: how many in-range players currently have valid bones.
        size_t skelOk = 0, admins = 0;
        for (const auto& p : update.players) {
            if (p.skeleton.valid) ++skelOk;
            if (p.isAdmin)        ++admins;
        }

        log->info("[Live] Players={} (bones {}/{}, admins {}) Zombies={} Vehicles={} Server='{}'",
                  update.players.size(),
                  skelOk, update.players.size(), admins,
                  update.zombies.size(),
                  update.carsAndBoats.size(),
                  update.serverName.value_or("?"));
    });

    service.Start();

    // Overlay polls GetLatestUpdate() each frame; no extra callback needed.
    std::atomic<bool> stopFlag{false};
    g_stopFlag = &stopFlag;

    GameOverlay   overlay(service, cfg, kCfgPath);
    overlay.SetWebRadarPort(webCfg.port);
    overlay.SetExitCallback([&stopFlag]() { stopFlag.store(true); });
    OverlayWindow window;
    window.SetResolutionOverride(cfg.overlayWidth, cfg.overlayHeight);
    overlay.SetResizeCallback([&window](int w, int h) { window.ResizeTo(w, h); });

    window.SetDrawCallback([&overlay](float w, float h) {
        overlay.Draw(w, h);
    });
    window.SetInputQueryCallback([&overlay]() {
        return overlay.IsMenuOpen();
    });
    window.SetFontReadyCallback([&overlay](ImFont* close, ImFont* lootFar) {
        overlay.SetLootFonts(close, lootFar);
    });

    SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);

    // Blocks until the overlay window is closed or stopFlag is set externally.
    window.Run(stopFlag);

    webRadar.Stop();
    service.Stop();
    log->info("Stopped.");
    spdlog::shutdown();
    return 0;
}
