#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#include <winsock2.h>
#include <windows.h>
#include <timeapi.h>

#pragma comment(lib, "winmm.lib")

#include "common/protocol.hpp"
#include "common/logger.hpp"
#include "common/zone_classifier.hpp"
#include "common/cli_options.hpp"
#include "overlay.hpp"
#include "ipc/shared_memory.hpp"
#include "input/kmbox_net.hpp"
#include "input/human_curve.hpp"
#include "input/non_blocking_actuator.hpp"
#include "sensor/log_sensor.hpp"
#include "memory/offsets_store.hpp"
#include "combat/reflex_manager.hpp"
#include "combat/combo_manager.hpp"
#include "looting/loot_controller.hpp"
#include "navigation/movement_controller.hpp"
#include "navigation/quest_navigator.hpp"
#include "navigation/terrain_grid.hpp"
#include "combat/kiting_engine.hpp"
#include "game_session.hpp"
#include "autologin/auto_login.hpp"
#include "navigation/town_quest_engine.hpp"
#include "brain/bot_brain.hpp"
#include "brain/area_events.hpp"
#include "brain/inventory_handler.hpp"
#include "brain/unstuck_handler.hpp"
#include "brain/map_device_handler.hpp"
#include "brain/character_fsm.hpp"
#include "brain/npc_interaction_handler.hpp"

#include <shellapi.h>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <chrono>
#include <cstdio>
#include <cstring>
#include <fstream>
#include <string>
#include <thread>

namespace {

bool IsElevated() {
    BOOL elevated = FALSE;
    HANDLE token = nullptr;
    if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) {
        TOKEN_ELEVATION elev{};
        DWORD size = sizeof(elev);
        if (GetTokenInformation(token, TokenElevation, &elev, sizeof(elev), &size)) {
            elevated = elev.TokenIsElevated;
        }
        CloseHandle(token);
    }
    return elevated != FALSE;
}

void RelaunchAsAdmin(int argc, char** argv) {
    wchar_t exePath[MAX_PATH] = {};
    GetModuleFileNameW(nullptr, exePath, MAX_PATH);

    wchar_t dirPath[MAX_PATH] = {};
    wcscpy_s(dirPath, exePath);
    for (int i = static_cast<int>(wcslen(dirPath)) - 1; i >= 0; --i) {
        if (dirPath[i] == L'\\' || dirPath[i] == L'/') {
            dirPath[i] = L'\0';
            break;
        }
    }

    std::wstring params;
    for (int i = 1; i < argc; ++i) {
        if (i > 1) params += L" ";
        params += L"\"" + std::wstring(argv[i], argv[i] + std::strlen(argv[i])) + L"\"";
    }

    SHELLEXECUTEINFOW sei{};
    sei.cbSize = sizeof(sei);
    sei.lpVerb = L"runas";
    sei.lpFile = exePath;
    sei.lpDirectory = dirPath;
    sei.lpParameters = params.c_str();
    sei.nShow = SW_SHOWNORMAL;
    ShellExecuteExW(&sei);
}

// Ngưỡng watchdog: Agent (Python) phải gửi nhịp tim ít nhất mỗi 5 giây
constexpr uint64_t kAgentWatchdogTimeoutMs = 5000;
// Chu kỳ in trạng thái ra console
constexpr uint64_t kStatusPrintIntervalMs = 5000;

// ==========================================================
// AUTO FLASK (G4.1 - Co-Pilot): HP thật <= ngưỡng -> bấm phím bình
// Chỉ chạy khi: --autoflask + PlayerState đã calibrate + cooldown
// ==========================================================
struct AutoFlaskConfig {
    bool enabled = false;
    float hpThreshold = 0.5f;   // bấm flask khi HP <= 50%
    uint8_t keyVk = 0x31;       // VK code phím '1' (emulation: keybd_event)
    uint8_t keyHid = 0x1E;      // HID scan code phím '1' (KMBox thật)
    uint32_t cooldownMs = 800;  // chống spam
};

struct AutoFlaskState {
    uint64_t lastFlaskMs = 0;
    uint32_t uses = 0;
    bool wasDead = false;
    // Phát hiện "bình hết charge": bấm mà giá trị không tăng 3 lần liên tiếp
    bool awaitEffect = false;
    uint64_t pressMs = 0;
    uint32_t prePressValue = 0;
    uint32_t noEffect = 0;
    bool empty = false;
};

std::string Hex(uint64_t v) {
    char buf[32];
    std::snprintf(buf, sizeof(buf), "%llX", static_cast<unsigned long long>(v));
    return buf;
}

// ---- File log (chẩn đoán khi Core chạy elevated - console có thể ẩn)
// Đã chuyển sang common/logger.hpp (dùng chung với game_session)


// Failsafe: Giải phóng toàn bộ phím và chuột khi Panic Stop (TASK-OPT-05)
void ReleaseAllInputsFailsafe() {
    // Chuột trái và chuột phải
    mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
    mouse_event(MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0);

    // Bàn phím: WASD, Space, 1, 2, Q, E
    const BYTE keysToRelease[] = {
        'W', 'A', 'S', 'D',
        VK_SPACE,
        '1', '2',
        'Q', 'E'
    };
    for (BYTE vk : keysToRelease) {
        keybd_event(vk, 0, KEYEVENTF_KEYUP, 0);
    }
}

// Thực thi một lệnh vĩ mô nhận từ Agent
// Trả về true nếu Core được yêu cầu dừng (PANIC_STOP)
bool DispatchCommand(const CommandPacket& cmd,
                     KMBoxNet& kmbox,
                     HumanCurveGenerator& curveGen,
                     bool allowPhysicalMove,
                     GameSession& gameSession,
                     AutoLogin& autoLogin,
                     TownQuestEngine& townQuestEngine,
                     QuestNavigator* questNav = nullptr,
                     InventoryHandler* invHandler = nullptr,
                     MapDeviceHandler* mapDeviceHandler = nullptr,
                     NpcInteractionHandler* npcHandler = nullptr,
                     ComboManager* comboMgr = nullptr,
                     ReflexManager* reflexMgr = nullptr,
                     input::NonBlockingActuator* actuator = nullptr) {
    std::cout << "[Core Command] Nhận lệnh #" << cmd.commandId
              << " OpCode=" << static_cast<uint32_t>(cmd.opCode)
              << " Target=(" << cmd.targetX << ", " << cmd.targetY << ")" << std::endl;

    switch (cmd.opCode) {
    case MacroOpCode::EXECUTE_STASH_ROUTINE: {
        std::cout << "[Core Command] Nhận lệnh EXECUTE_STASH_ROUTINE từ Agent/GUI!" << std::endl;
        if (invHandler) {
            invHandler->StartStash(cmd, SharedMemoryManager::NowMs());
        }
        break;
    }
    case MacroOpCode::EXECUTE_VENDOR_ROUTINE: {
        std::cout << "[Core Command] Nhận lệnh EXECUTE_VENDOR_ROUTINE từ Agent/GUI!" << std::endl;
        if (invHandler) {
            invHandler->StartVendor(cmd, SharedMemoryManager::NowMs());
        }
        break;
    }
    case MacroOpCode::EXECUTE_MAP_DEVICE_ROUTINE: {
        std::cout << "[Core Command] Nhận lệnh EXECUTE_MAP_DEVICE_ROUTINE từ Agent/GUI!" << std::endl;
        if (mapDeviceHandler) {
            mapDeviceHandler->StartMapDevice(cmd, SharedMemoryManager::NowMs());
        }
        break;
    }
    case MacroOpCode::SELECT_ATLAS_NODE: {
        std::cout << "[Core Command] Nhận lệnh SELECT_ATLAS_NODE từ Agent/GUI (X="
                  << cmd.targetX << ", Y=" << cmd.targetY << ", Flags=" << cmd.targetZ << ")!" << std::endl;
        if (mapDeviceHandler) {
            mapDeviceHandler->SelectAtlasNode(cmd, SharedMemoryManager::NowMs());
        }
        break;
    }
    case MacroOpCode::VERIFY_MAP_DEVICE_UI: {
        std::cout << "[Core Command] Nhận lệnh VERIFY_MAP_DEVICE_UI từ Agent/Coordinator! Xác nhận Atlas UI đã mở." << std::endl;
        if (mapDeviceHandler) {
            mapDeviceHandler->SetAtlasUiVerified(true);
        }
        break;
    }
    case MacroOpCode::EXECUTE_NPC_INTERACT_ROUTINE: {
        std::cout << "[Core Command] Nhận lệnh EXECUTE_NPC_INTERACT_ROUTINE từ Agent/GUI!" << std::endl;
        if (npcHandler) {
            npcHandler->StartInteract(cmd, SharedMemoryManager::NowMs());
        }
        break;
    }
    case MacroOpCode::SET_MAP_SCENARIO: {
        uint32_t scVal = static_cast<uint32_t>(cmd.targetX);
        bool autoPortal = (cmd.targetY >= 0.5f);
        MapScenario sc = MapScenario::BOSS_RUSH;
        if (scVal == 1) sc = MapScenario::ATLAS_QUEST_RUSH;
        else if (scVal == 2) sc = MapScenario::FAST_CLEAR;
        else if (scVal == 3) sc = MapScenario::FULL_CLEAR;

        std::string scName = "BOSS_RUSH";
        if (sc == MapScenario::ATLAS_QUEST_RUSH) scName = "ATLAS_QUEST_RUSH";
        else if (sc == MapScenario::FAST_CLEAR) scName = "FAST_CLEAR";
        else if (sc == MapScenario::FULL_CLEAR) scName = "FULL_CLEAR";

        std::cout << "[Core Command] Nhận lệnh SET_MAP_SCENARIO: " << scName
                  << " (AutoPortal=" << (autoPortal ? "true" : "false") << ") từ GUI/Agent!" << std::endl;
        CoreLog("[Core Command] Set MapScenario to " + scName);

        if (questNav) {
            questNav->SetMapScenario(sc, autoPortal);
        }
        break;
    }
    case MacroOpCode::FORCE_PORTAL_RETURN:
    case MacroOpCode::CAST_EMERGENCY_PORTAL: {
        std::cout << "[Core Command] Nhận lệnh FORCE_PORTAL_RETURN / CAST_EMERGENCY_PORTAL từ Agent! Mở Town Portal phím '.'..." << std::endl;
        CoreLog("[Core Command] FORCE_PORTAL_RETURN -> KeyPress '.' (VK_OEM_PERIOD 0xBE)");
        if (actuator) {
            actuator->KeyPressVk(0xBE, 100, SharedMemoryManager::NowMs());
        } else {
            kmbox.KeyPressVk(0xBE, 100);
        }
        if (questNav) {
            questNav->StopMovement(kmbox);
        }
        break;
    }
    case MacroOpCode::SET_COMBAT_PROFILE: {
        uint32_t profVal = static_cast<uint32_t>(cmd.targetX);
        CombatProfile profile = CombatProfile::MOB_CLEARING;
        if (profVal == 1) profile = CombatProfile::BOSS_BURST;
        else if (profVal == 2) profile = CombatProfile::SURVIVAL_KITING;

        std::string profName = (profile == CombatProfile::BOSS_BURST) ? "BOSS_BURST" :
                               (profile == CombatProfile::SURVIVAL_KITING) ? "SURVIVAL_KITING" : "MOB_CLEARING";

        std::cout << "[Core Command] Nhận lệnh SET_COMBAT_PROFILE: " << profName
                  << " (TargetEntity=" << cmd.targetEntityId << ") từ Agent!" << std::endl;
        CoreLog("[Core Command] Switch CombatProfile to " + profName);
        if (comboMgr) {
            comboMgr->SetCombatProfile(profile, cmd.targetEntityId);
        }
        break;
    }
    case MacroOpCode::NAVIGATE_TO_COORD: {
        // Bắt đầu quỹ đạo từ vị trí con trỏ chuột thực tế
        POINT cursor{};
        GetCursorPos(&cursor);
        auto steps = curveGen.GenerateTrajectory(
            { static_cast<float>(cursor.x), static_cast<float>(cursor.y) },
            { cmd.targetX, cmd.targetY });

        std::cout << "[Core Input] Đã tính quỹ đạo Bézier ("
                  << steps.size() << " bước) tới ("
                  << cmd.targetX << ", " << cmd.targetY << ")" << std::endl;

        // Chỉ di chuyển chuột thật khi bật cờ --move (tránh gây nhiễu khi kiểm thử)
        if (allowPhysicalMove) {
            for (const auto& step : steps) {
                kmbox.MoveMouse(static_cast<short>(step.x), static_cast<short>(step.y));
                std::this_thread::sleep_for(std::chrono::milliseconds(1));
            }
            std::cout << "[Core Input] Đã thực thi " << steps.size()
                      << " bước di chuyển qua KMBox." << std::endl;
        }
        break;
    }
    case MacroOpCode::TRIGGER_WORLD_MAP_TRAVEL: {
        uint32_t act = static_cast<uint32_t>(cmd.targetX);
        uint32_t zoneIndex = static_cast<uint32_t>(cmd.targetY);

        // INTERLOCK AN TOAN CHONG XUNG DOT HAI NAO (INV-BRAIN-CONFLICT-GATEKEEPER):
        // 1. Cam kich hoat phim U di Campaign Act khi MapDeviceHandler dang chay chu trinh mo map
        // 2. Cam kich hoat phim U di Campaign Act khi nhan vat dang o Hideout / Endgame
        const std::string sceneName = gameSession.CurrentSceneName();
        std::string sceneLower = sceneName;
        for (char& c : sceneLower) c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));

        const bool isHideout = sceneLower.find("hideout") != std::string::npos;
        const bool isMapDeviceBusy = (mapDeviceHandler != nullptr && mapDeviceHandler->IsActive());

        if (isMapDeviceBusy || (isHideout && act <= 3)) {
            std::cout << "[Core Command] REJECTED TRIGGER_WORLD_MAP_TRAVEL (Act " << act 
                      << ", Zone #" << zoneIndex << "): "
                      << (isMapDeviceBusy ? "MapDeviceHandler dang hoat dong!" : "Dang o Hideout, khong the travel Campaign Act!")
                      << std::endl;
            break;
        }

        std::cout << "[Core Command] Nhận lệnh TRIGGER_WORLD_MAP_TRAVEL từ GUI (Act "
                  << act << ", Zone #" << zoneIndex << ")! Kích hoạt World Map phím 'U'..." << std::endl;
        townQuestEngine.StartTravel(act, zoneIndex, SharedMemoryManager::NowMs());
        break;
    }
    case MacroOpCode::TRIGGER_AUTO_LOGIN: {
        std::cout << "[Core Command] Nhận lệnh TRIGGER_AUTO_LOGIN từ GUI! Kích hoạt quy trình tự động đăng nhập..." << std::endl;
        autoLogin.TriggerManualLogin(SharedMemoryManager::NowMs());
        break;
    }
    case MacroOpCode::RECALIBRATE_XYZ: {
        std::cout << "[Core Command] Nhận lệnh RECALIBRATE_XYZ từ GUI! Kích hoạt tự động quét..." << std::endl;
        if (cmd.targetX > 0.0f) {
            uint32_t newMaxHP = static_cast<uint32_t>(cmd.targetX);
            std::cout << "[Core Command] Cập nhật Expected Max HP sang " << newMaxHP << " từ GUI/Optical Sensor!" << std::endl;
            uint32_t curMaxHP = 0;
            if (gameSession.PlayerAddress() != 0 && gameSession.Reader()) {
                gameSession.Reader()->ReadValue<uint32_t>(gameSession.PlayerAddress() + 4, curMaxHP);
            }
            if (curMaxHP != newMaxHP) {
                std::cout << "[Core Command] curMaxHP (" << curMaxHP << ") != newMaxHP (" << newMaxHP
                          << ") -> Reset Player & Position address để quét lại toàn diện!" << std::endl;
                gameSession.SetPlayerAddress(0);
                gameSession.SetPositionAddress(0);
            }
            gameSession.SetExpectedMaxHP(newMaxHP);
            gameSession.AutoDetectPlayerHP(newMaxHP);
        }
        if (cmd.targetY > 0.0f) {
            uint32_t newMaxMana = static_cast<uint32_t>(cmd.targetY);
            std::cout << "[Core Command] Cập nhật Expected Max Mana sang " << newMaxMana << " từ GUI/Optical Sensor!" << std::endl;
            gameSession.SetExpectedMaxMana(newMaxMana);
            gameSession.AutoDetectManaPool(newMaxMana);
        }
        if (cmd.targetZ > 0.0f) {
            uint32_t newMaxES = static_cast<uint32_t>(cmd.targetZ);
            std::cout << "[Core Command] Cập nhật Expected Max ES sang " << newMaxES << " từ GUI/Optical Sensor!" << std::endl;
            gameSession.SetExpectedMaxES(newMaxES);
            gameSession.AutoDetectShield(newMaxES);
        }
        // Chỉ kích hoạt TriggerManualAutoScan() nếu tọa độ XYZ thực sự chưa được xác định
        if (!gameSession.PositionCalibrated()) {
            gameSession.TriggerManualAutoScan();
        }
        break;
    }
    case MacroOpCode::PANIC_STOP:
        std::cout << "[ALERT] Agent yêu cầu PANIC_STOP! Kích hoạt Failsafe giải phóng phím/chuột..." << std::endl;
        ReleaseAllInputsFailsafe();
        townQuestEngine.Reset(kmbox);
        return true;
    default:
        std::cout << "[Core Command] OpCode " << static_cast<uint32_t>(cmd.opCode)
                  << " chưa được hỗ trợ - bỏ qua." << std::endl;
        break;
    }
    return false;
}

using namespace poe2::cli;


static void AttachToDefaultDesktop() {
    HWINSTA hwinsta = OpenWindowStationW(L"WinSta0", FALSE, MAXIMUM_ALLOWED);
    if (hwinsta) {
        SetProcessWindowStation(hwinsta);
    }
    HDESK hdesk = OpenDesktopW(L"Default", 0, FALSE, MAXIMUM_ALLOWED);
    if (hdesk) {
        SetThreadDesktop(hdesk);
    }
}

} // namespace

int main(int argc, char* argv[]) {
    SetConsoleOutputCP(CP_UTF8);

    // Gắn vào Desktop Station WinSta0\Default để các API GetForegroundWindow, FindWindow, SetCursorPos hoạt động
    AttachToDefaultDesktop();

    bool userModeRequested = false;
    for (int i = 1; i < argc; ++i) {
        if (std::strcmp(argv[i], "--user-mode") == 0 || std::strcmp(argv[i], "--no-relaunch") == 0) {
            userModeRequested = true;
            break;
        }
    }

    if (!IsElevated() && !userModeRequested) {
        std::cout << "[Core] Yêu cầu quyền Administrator để đọc bộ nhớ game (High Integrity)..." << std::endl;
        RelaunchAsAdmin(argc, argv);
        return 0;
    }

    // TASK-OPT-07: Mở khóa độ phân giải timer hệ thống 1ms (1000Hz)
    timeBeginPeriod(1);

    ResetCoreLog();
    overlay::Start();

    std::cout << "=================================================" << std::endl;
    std::cout << "   AutoPOE2 Low-Latency Core Engine (v0.5.5)     " << std::endl;
    std::cout << "  IPC v1.1: Seqlock Double-Buffer + SPSC Ring    " << std::endl;
    std::cout << "=================================================" << std::endl;

    // Phân tích tham số dòng lệnh và nạp cấu hình (Common CLI Options)
    auto opt = poe2::cli::ParseCommandLineArgs(argc, argv);
    bool allowPhysicalMove = opt.allowPhysicalMove;
    bool calibrate = opt.calibrate;
    bool calibrateXyzOnly = opt.calibrateXyzOnly;
    std::string logPath = opt.logPath;
    std::string backend = opt.backend;
    uintptr_t playerAddr = opt.playerAddr;
    uintptr_t manaAddrCmd = opt.manaAddrCmd;
    uintptr_t entityOffsetCmd = opt.entityOffsetCmd;
    uint32_t expectedMaxMana = opt.expectedMaxMana;
    uint32_t expectedMaxHP = opt.expectedMaxHP;
    uint32_t expectedMaxES = opt.expectedMaxES;
    uint32_t expectedMaxWard = opt.expectedMaxWard;
    uintptr_t wardAddrCmd = opt.wardAddrCmd;
    ReflexConfig reflexCfg = opt.reflexCfg;
    LootConfig lootCfg = opt.lootCfg;
    ComboConfig comboCfg = opt.comboCfg;
    QuestNavConfig questCfg = opt.questCfg;
    AutoLoginConfig autoLoginCfg = opt.autoLoginCfg;
    MovementMode moveMode = opt.moveMode;
    bool explicitMoveModeFromCli = opt.explicitMoveModeFromCli;

    std::cout << "[Core] Chế độ di chuyển: " << (moveMode == MovementMode::MOUSE ? "CHUỘT (Click-to-Move)" : "BÀN PHÍM (WASD)") << std::endl;
    std::cout << "[Core] Chế độ chiến đấu: " << (comboCfg.isRangedMode ? "ĐÁNH XA (Chuột Phải [RMB], cự ly 380)" : "CẬN CHIẾN (Chuột Trái [LMB], cự ly 220)") << std::endl;

    // 1. Khởi tạo khối nhớ chia sẻ IPC 4MB
    SharedMemoryManager shm;
    if (!shm.Initialize(true)) {
        std::cerr << "[Core Fatal] Không thể khởi tạo IPC Shared Memory." << std::endl;
        return 1;
    }

    // 2. Khởi tạo điều khiển phần cứng KMBox
    KMBoxNet kmbox;
    // Mặc định bật chế độ mô phỏng an toàn (software fallback) nếu chưa nối KMBox phần cứng
    kmbox.EnableEmulationMode(true);

    // Khởi tạo các bộ điều phối: Phản xạ, Nhặt đồ, Kỹ năng Combo, Quest Runner, Lưới địa hình & Kiting AI
    navigation::TerrainGrid terrainGrid;
    combat::KitingEngine kitingEngine;

    ReflexManager reflexMgr(reflexCfg);
    LootController lootCtrl(lootCfg);
    ComboManager comboMgr(comboCfg);
    QuestNavigator questNav(questCfg);
    AutoLogin autoLogin(autoLoginCfg);
    TownQuestEngine townQuestEngine;

    // NonBlockingActuator: Bộ chấp hành phi khóa theo nhịp tick 120Hz (Phase P7 SSoT)
    input::NonBlockingActuator nonBlockingActuator(kmbox);
    reflexMgr.SetActuator(&nonBlockingActuator);
    comboMgr.SetActuator(&nonBlockingActuator);

    // MovementController: Động cơ điều khiển di chuyển phi khóa ExiledBot 2 (Move-Only Key hoặc WASD State-Holding)
    navigation::MovementConfig moveCfg;
    moveCfg.mode = (moveMode == MovementMode::MOUSE) ? navigation::MovementMode::MOUSE : navigation::MovementMode::WASD;
    moveCfg.moveOnlyVk = 0x54; // 'T' (Move-Only key gán trong game POE2)
    navigation::MovementController moveCtrl(moveCfg);
    questNav.SetMovementController(&moveCtrl);
    comboMgr.SetMovementController(&moveCtrl);

    // Nạp Pickup Filter JSON (Doc 29 Phase P2)
    std::string filterPath = "bin/Release/pickup_filter.json";
    if (GetFileAttributesA(filterPath.c_str()) == INVALID_FILE_ATTRIBUTES) {
        filterPath = "data/pickup_filter.json";
    }
    if (GetFileAttributesA(filterPath.c_str()) != INVALID_FILE_ATTRIBUTES) {
        lootCtrl.SetFilterPath(filterPath);
        lootCtrl.LoadFilter(filterPath);
    }

    comboMgr.SetTerrainGrid(&terrainGrid);
    comboMgr.SetKitingEngine(&kitingEngine);
    comboMgr.SetReflexManager(&reflexMgr);
    questNav.SetTerrainGrid(&terrainGrid);

    // Khởi tạo BotBrain Arbiter, AreaEvent Memory, InventoryHandler & UnstuckHandler (Doc 29 - Giai đoạn P0-P4)
    AreaEventManager areaEventMgr;
    AreaEventHandler areaEventHdl(&areaEventMgr);
    questNav.SetAreaEventManager(&areaEventMgr);

    InventoryHandler invHandler;
    invHandler.LoadCoordinates("data/ui/coordinates.toml");

    MapDeviceHandler mapDeviceHandler;
    mapDeviceHandler.LoadCoordinates("data/ui/coordinates.toml");
    mapDeviceHandler.SetTerrainGrid(&terrainGrid);

    NpcInteractionHandler npcHandler;
    npcHandler.LoadCoordinates("data/ui/coordinates.toml");

    if (comboCfg.isCiChargedStaffMode || std::filesystem::exists("data/combat/skills_hollow_form.toml")) {
        if (!comboMgr.LoadSkillProfile("data/combat/skills_hollow_form.toml")) {
            comboMgr.LoadSkills("data/combat/skills.toml");
        }
    } else {
        comboMgr.LoadSkills("data/combat/skills.toml");
    }

    UnstuckConfig unstuckCfg;
    unstuckCfg.portalVk = questCfg.portalVk;
    unstuckCfg.portalHid = questCfg.portalHid;
    UnstuckHandler unstuckHandler(unstuckCfg);

    // Khởi tạo CharacterFSM (Character State Machine 2026)
    CharacterFSM charFsm;
    charFsm.SetMapDeviceHandler(&mapDeviceHandler);
    charFsm.SetInventoryHandler(&invHandler);
    charFsm.SetNpcHandler(&npcHandler);
    questNav.SetCharacterFSM(&charFsm);
    unstuckHandler.SetCharacterFSM(&charFsm);

    BotBrain botBrain;
    botBrain.SetMovementController(&moveCtrl);
    botBrain.AddHandler(&unstuckHandler);
    botBrain.AddHandler(&comboMgr);
    botBrain.AddHandler(&lootCtrl);
    botBrain.AddHandler(&areaEventHdl);
    botBrain.AddHandler(&invHandler);
    botBrain.AddHandler(&npcHandler);
    botBrain.AddHandler(&mapDeviceHandler);
    botBrain.AddHandler(&questNav);

    if (!questCfg.targetZone.empty()) {
        std::cout << "[Core] Khu vực mục tiêu được chỉ định: " << questCfg.targetZone << std::endl;
    }

    // 3. Khởi tạo động cơ sinh quỹ đạo chuột tự nhiên
    HumanCurveGenerator curveGen;

    // 4. Khởi tạo cảm biến nhật ký Zero-Risk (Giai đoạn 2.1)
    LogSensor logSensor(logPath);
    if (!logSensor.Start()) {
        std::cerr << "[LogSensor Warning] Không tạo được worker thread tail log." << std::endl;
    }

    // Nạp danh sách bạn bè / đồng đội vào Whitelist để không out game nhầm
    logSensor.LoadFriendsList("friends_whitelist.txt");
    logSensor.LoadFriendsList("config/friends_whitelist.txt");
    std::string iniCfgPath = GetPoe2ConfigPath();
    if (!iniCfgPath.empty()) {
        std::ifstream iniF(iniCfgPath);
        std::string iniL;
        while (std::getline(iniF, iniL)) {
            if (iniL.find("account_name=") != std::string::npos) {
                auto val = iniL.substr(iniL.find('=') + 1);
                auto hashPos = val.find('#');
                if (hashPos != std::string::npos) val = val.substr(0, hashPos);
                while (!val.empty() && (val.back() == '\r' || val.back() == ' ')) val.pop_back();
                if (!val.empty()) {
                    logSensor.AddFriend(val);
                    std::cout << "[Security Whitelist] Đã thêm tài khoản người chơi: " << val << std::endl;
                }
                break;
            }
        }
    }

    // 5. Khởi tạo phiên game: chọn backend đọc bộ nhớ + EntityManager (Giai đoạn 2)
    GameSession gameSession;
    gameSession.SetTownQuestEngine(&townQuestEngine);
    gameSession.SetKMBox(&kmbox);
    gameSession.SetAllowPhysicalMove(allowPhysicalMove);
    gameSession.SetMovementMode(moveMode); // Micro-Step cần biết WASD hay Click-to-Move
    reflexMgr.SetGameSession(&gameSession);
    std::string sessionStatus;
    if (!gameSession.Initialize(backend, sessionStatus)) {
        std::cerr << "[Core Fatal] Không khởi tạo được backend đọc bộ nhớ: "
                  << sessionStatus << std::endl;
        shm.Shutdown();
        return 1;
    }

    if (gameSession.IsSimulated()) {
        allowPhysicalMove = false;
        gameSession.SetAllowPhysicalMove(false);
        std::cout << "[SAFETY LOCK] Backend dang o che do SIMULATED -> TU DONG KHOA (MUTE) toan bo chuot & phim vat ly de bao ve Desktop nguoi dung!" << std::endl;
    }

    // ---- Auto-calibration / Resolution PlayerState (Giai đoạn 2) ----
    if (calibrateXyzOnly) {
        wchar_t exePathW[MAX_PATH] = {};
        GetModuleFileNameW(nullptr, exePathW, MAX_PATH);
        char exeDir[MAX_PATH] = {};
        WideCharToMultiByte(CP_UTF8, 0, exePathW, -1, exeDir, sizeof(exeDir), nullptr, nullptr);
        std::string dir = exeDir;
        auto slash = dir.find_last_of("\\/");
        std::string baseDir = (slash != std::string::npos) ? dir.substr(0, slash) : ".";
        OffsetsStore store;
        store.Load(baseDir + "\\offsets.toml");
        // Tuyệt đối không đọc player_addr từ offsets.toml vì đây là địa chỉ heap thay đổi mỗi phiên
        if (playerAddr != 0) {
            gameSession.SetPlayerAddress(playerAddr);
        } else if (!gameSession.IsSimulated()) {
            std::cout << "[Core] calibrate-xyz-only: Tự động dò quét Player HP trong RAM..." << std::endl;
            gameSession.AutoDetectPlayerHP(expectedMaxHP);
        }
        std::cout << "[Core] Bat dau che do CALIBRATE XYZ (bo qua HP)..." << std::endl;
        if (!gameSession.CalibratePositionInteractive()) {
            std::cerr << "[Core] XYZ calibration thất bại." << std::endl;
        } else {
            std::cout << "[Core] XYZ calibration THÀNH CÔNG!" << std::endl;
        }
    } else if (calibrate) {
        if (!gameSession.CalibrateInteractive()) {
            std::cerr << "[Core] Calibration thất bại - chạy tiếp KHÔNG có HP thật." << std::endl;
            CoreLog("[Calibrate] HP FAILED");
        } else {
            CoreLog("[Calibrate] HP OK, playerAddr=0x" + Hex(gameSession.PlayerAddress()));
            // HP xong -> calibration tọa độ XYZ (float differential tự động)
            if (!gameSession.CalibratePositionInteractive()) {
                std::cerr << "[Core] XYZ calibration thất bại - NavMesh cần XYZ, thử lại sau." << std::endl;
                CoreLog("[Calibrate] XYZ FAILED");
            } else {
                CoreLog("[Calibrate] XYZ OK, xyzAddr=0x" + Hex(gameSession.PositionAddress()));
            }
        }
    } else if (playerAddr != 0) {
        // Địa chỉ truyền trực tiếp qua tham số dòng lệnh
        gameSession.SetPlayerAddress(playerAddr);
        std::cout << "[Core] PlayerState address = 0x" << std::hex << playerAddr
                  << std::dec << " (từ --player-addr)" << std::endl;
    } else {
        // Tự động tìm offsets.toml hoặc player_candidates.txt
        wchar_t exePathW[MAX_PATH] = {};
        GetModuleFileNameW(nullptr, exePathW, MAX_PATH);
        char exeDir[MAX_PATH] = {};
        WideCharToMultiByte(CP_UTF8, 0, exePathW, -1, exeDir, sizeof(exeDir), nullptr, nullptr);
        std::string dir = exeDir;
        auto slash = dir.find_last_of("\\/");
        std::string baseDir = (slash != std::string::npos) ? dir.substr(0, slash) : ".";

        OffsetsStore store;
        if (store.Load(baseDir + "\\offsets.toml")) {
            if (store.Has("static_rva") && store.Has("num_offsets")) {
                uintptr_t rva = store.Get("static_rva");
                size_t numOff = store.Get("num_offsets");
                std::vector<uintptr_t> offsets;
                for (size_t i = 0; i < numOff; ++i) {
                    std::string k = "offset_" + std::to_string(i + 1);
                    if (store.Has(k)) offsets.push_back(store.Get(k));
                }
                if (!offsets.empty()) {
                    gameSession.SetPointerChain(rva, offsets);
                    std::cout << "[Core] Đã nạp Pointer Chain từ offsets.toml: RVA=0x" << std::hex << rva
                              << " (" << std::dec << offsets.size() << " cấp offset)" << std::endl;
                }
            }
            if (store.Has("entity_map_offset")) {
                uintptr_t emo = static_cast<uintptr_t>(store.Get("entity_map_offset"));
                gameSession.SetEntityMapOffset(emo);
                std::cout << "[Core] Đọc entity_map_offset từ offsets.toml: 0x" << std::hex << emo
                          << std::dec << std::endl;
            }
            if (store.Has("terrain_offset")) {
                uintptr_t tro = static_cast<uintptr_t>(store.Get("terrain_offset"));
                std::cout << "[Core] Đọc terrain_offset từ offsets.toml: 0x" << std::hex << tro
                          << std::dec << std::endl;
            }
        }
        if (playerAddr == 0 && expectedMaxHP == 0) {
            std::ifstream candFile(baseDir + "\\player_candidates.txt");
            if (candFile) {
                std::string line;
                if (std::getline(candFile, line) && line.size() >= 4) {
                    uintptr_t candAddr = std::strtoull(line.c_str(), nullptr, 16);
                    if (candAddr != 0 && gameSession.IsValidLifeComponent(candAddr, false)) {
                        playerAddr = candAddr;
                        std::cout << "[Core] Đọc player_addr hợp lệ từ player_candidates.txt: 0x" << std::hex << playerAddr
                                  << std::dec << std::endl;
                    } else if (candAddr != 0) {
                        std::cout << "[Core Warning] player_addr từ player_candidates.txt (0x" << std::hex << candAddr
                                  << std::dec << ") không hợp lệ -> Bỏ qua!" << std::endl;
                    }
                }
            }
        }
        if (playerAddr != 0) {
            gameSession.SetPlayerAddress(playerAddr);
        }
        if (manaAddrCmd != 0) {
            gameSession.SetManaAddress(manaAddrCmd);
        }
        if (entityOffsetCmd != 0) {
            gameSession.SetEntityMapOffset(entityOffsetCmd);
        }
        gameSession.SetExpectedMaxMana(expectedMaxMana);
        gameSession.SetExpectedMaxES(expectedMaxES);
        gameSession.SetExpectedMaxWard(expectedMaxWard);
        if (wardAddrCmd != 0) {
            gameSession.SetWardAddress(wardAddrCmd);
        }
        if (expectedMaxHP > 0) {
            gameSession.SetExpectedMaxHP(expectedMaxHP);
            std::cout << "[Core] Đã thiết lập Expected Max HP = " << expectedMaxHP << " cho AutoScanHP" << std::endl;
        } else {
            gameSession.SetExpectedMaxHP(0);
            std::cout << "[Core] Chế độ TỰ ĐỘNG NHẬN DIỆN HP (Zero User Input / Dynamic Scaling) đã kích hoạt." << std::endl;
        }
        if (gameSession.PlayerAddress() == 0 && !gameSession.IsSimulated()) {
            std::cout << "[Core] Đang tự động dò quét vị trí Player LifeComponent trong RAM..." << std::endl;
            gameSession.AutoDetectPlayerHP(expectedMaxHP);
        }
        if (gameSession.PlayerAddress() != 0 && !gameSession.ManaCalibrated()) {
            std::cout << "[Core] Mana chưa calibrate hoặc địa chỉ cũ đã xóa -> Bắt đầu tự động dò tìm Mana ("
                      << expectedMaxMana << ")..." << std::endl;
            gameSession.AutoDetectManaPool(expectedMaxMana);
        }
    }

    std::cout << "[Core Ready] Đang vận hành vòng lặp phản xạ thời gian thực (120Hz)..." << std::endl;
    // Build-stamp: giúp phát hiện ngay tình huống chạy nhầm binary cũ (stale binary)
    {
        const std::string buildStamp = std::string("[Core Build] ") + __DATE__ + " " + __TIME__
                                     + " | PID " + std::to_string(GetCurrentProcessId());
        std::cout << buildStamp << std::endl;
        CoreLog(buildStamp);
    }
    std::cout << "[Panic Hotkey] Nhấn Pause/Break hoặc Ctrl+Shift+F12 để dừng khẩn cấp toàn bộ hệ thống (tránh xung đột F12 Steam Screenshot)." << std::endl;
    std::cout << "[Toggle Hotkey] Nhấn F8 hoặc F6 để Bật/Tắt chế độ tự hành." << std::endl;
    if (!allowPhysicalMove) {
        std::cout << "[Info] Chạy kèm cờ --move để cho phép di chuyển chuột thật (hoặc bấm F8/F6 trong game)." << std::endl;
    }

    uint64_t snapshotCounter = 0;
    uint64_t commandsProcessed = 0;
    uint64_t lastStatusPrintMs = SharedMemoryManager::NowMs();
    TelemetryPacket samplePacket{};

    // Player + Entity được GameSession điền mỗi tick từ backend bộ nhớ
    // (sim = game ảo 8MB; rpm = PathOfExile.exe thật khi chạy Admin)

    // Trạng thái khu vực - cập nhật realtime từ LogSensor (dữ liệu thật)
    std::string currentAreaName;
    std::string currentAreaCode;
    uint32_t areaLevel = 0;
    uint64_t areaSeed = 0;
    uint64_t areaLoadTimestampMs = 0;
    bool hasInstanceTransition = false;

    // Copy chuỗi vào buffer char[] giới hạn độ dài
    auto copyBounded = [](char* dst, size_t dstSize, const std::string& src) {
        const size_t n = (std::min)(dstSize - 1, src.size());
        std::memcpy(dst, src.data(), n);
        dst[n] = '\0';
    };

    const uint64_t coreStartMs = SharedMemoryManager::NowMs();
    bool isPassiveCoPilotMode = false;
    uint64_t lastF11PressMs = 0;
    uint64_t lastF8PressMs = 0;
    uint64_t lastHotkeyPollMs = 0;
    GetAsyncKeyState(VK_PAUSE);
    GetAsyncKeyState(VK_F12);
    GetAsyncKeyState(VK_F8);
    GetAsyncKeyState(VK_F6);

    while (shm.IsRunning()) {
        const uint64_t loopNowMs = SharedMemoryManager::NowMs();

        // TASK-OPT-05 / AUDIT-3AM: Kiểm tra phím dừng khẩn cấp Pause/Break hoặc Ctrl+Shift+F12
        // Tuyệt đối không bắt phím F12 đơn lẻ để tránh xung đột với phím chụp ảnh màn hình Steam!
        const bool isPanicStop = (GetAsyncKeyState(VK_PAUSE) & 0x8000) ||
            ((GetAsyncKeyState(VK_CONTROL) & 0x8000) &&
             (GetAsyncKeyState(VK_SHIFT) & 0x8000) &&
             (GetAsyncKeyState(VK_F12) & 0x8000));

        if ((loopNowMs - coreStartMs > 600) && isPanicStop) {
            std::cout << "\n[ALERT] Nhận tín hiệu dừng khẩn cấp Panic Killswitch (Pause/Break hoặc Ctrl+Shift+F12)! Đang ngắt hệ thống an toàn..." << std::endl;
            // TASK-OPT-05: Failsafe giải phóng toàn bộ chuột và phím WASD, Space, 1, 2, Q, E
            ReleaseAllInputsFailsafe();
            shm.RequestStop();
            break;
        }

        // Throttling polling các phím chức năng (F8/F6/F11) mỗi 25ms thay vì mỗi tick 8.33ms (tiết kiệm ~360 syscalls/giây)
        if (loopNowMs - lastHotkeyPollMs >= 25) {
            lastHotkeyPollMs = loopNowMs;

            // AUDIT-3AM: Kiểm tra phím tắt Toggle Bot F8 hoặc F6 (non-blocking debounce 500ms)
            if ((GetAsyncKeyState(VK_F8) & 0x8000) || (GetAsyncKeyState(VK_F6) & 0x8000)) {
                if (loopNowMs - lastF8PressMs >= 500) {
                    lastF8PressMs = loopNowMs;
                    if (!gameSession.IsSimulated()) {
                        allowPhysicalMove = !allowPhysicalMove;
                        gameSession.SetAllowPhysicalMove(allowPhysicalMove);
                        if (allowPhysicalMove && !terrainGrid.HasNativeTerrain() && (samplePacket.player.posX != 0.0f || samplePacket.player.posY != 0.0f)) {
                            gameSession.AutoDetectTerrain(&terrainGrid, samplePacket.player.posX, samplePacket.player.posY);
                        }
                        std::cout << "\n[HotKey F8/F6] Chuyển đổi trạng thái Tự Hành: "
                                  << (allowPhysicalMove ? "▶ BẬT (ON)" : "⏸ TẠM DỪNG (PAUSE)") << std::endl;
                        CoreLog(std::string("[HotKey F8/F6] Bot toggle state: ") + (allowPhysicalMove ? "ON" : "PAUSE"));
                    } else {
                        std::cout << "\n[SAFETY LOCK] Backend dang o che do SIMULATED -> Khong cho phep bat phim vat ly!" << std::endl;
                    }
                }
            }

            // Kiểm tra phím tắt định vị lại tọa độ F11 (non-blocking debounce)
            if (GetAsyncKeyState(VK_F11) & 0x8000) {
                if (loopNowMs - lastF11PressMs >= 500) {
                    lastF11PressMs = loopNowMs;
                    std::cout << "\n[HotKey F11] Nhận tín hiệu phím F11: Kích hoạt tự động quét tọa độ XYZ..." << std::endl;
                    gameSession.TriggerManualAutoScan();
                }
            }
        }

        // Watchdog: nếu Agent từng kết nối nhưng mất nhịp tim quá lâu -> tự động hạ cấp về Co-Pilot Passive (duy trì Auto-Flask / Logout)
        const auto watchdogStatus = shm.CheckAgentWatchdog(kAgentWatchdogTimeoutMs);
        if (watchdogStatus == SharedMemoryManager::AgentWatchdogStatus::TimedOut) {
            if (!isPassiveCoPilotMode) {
                isPassiveCoPilotMode = true;
                std::cerr << "\n[ALERT] Watchdog: Agent mất kết nối quá "
                          << kAgentWatchdogTimeoutMs << "ms -> TỰ ĐỘNG HẠ CẤP VỀ CO-PILOT PASSIVE (Duy trì Auto-Flask & Emergency Logout)!" << std::endl;
            }
        } else if (isPassiveCoPilotMode && watchdogStatus == SharedMemoryManager::AgentWatchdogStatus::Healthy) {
            isPassiveCoPilotMode = false;
            std::cout << "\n[INFO] Watchdog: Agent đã kết nối lại -> Phục hồi chế độ tự hành đầy đủ!" << std::endl;
        }

        // Rút toàn bộ sự kiện từ LogSensor (Zero-Risk telemetry) - Giai đoạn 2.1
        GameEvent ev{};
        while (logSensor.PollEvent(ev)) {
            switch (ev.type) {
            case GameEventType::AreaGenerated:
                currentAreaCode = ev.text;
                areaLevel = static_cast<uint32_t>(ev.areaLevel);
                areaSeed = ev.areaSeed;
                areaEventMgr.OnAreaChanged(static_cast<uint32_t>(areaSeed));
                gameSession.OnInstanceTransition();
                hasInstanceTransition = true;
                std::cout << "[LogSensor] Tạo instance mới: " << ev.text
                          << " (Lv " << ev.areaLevel << ", seed " << ev.areaSeed << ")" << std::endl;
                break;
            case GameEventType::SceneChanged:
                if (GameSession::IsUIOverlayScene(ev.text)) {
                    std::cout << "[LogSensor] Bỏ qua SceneChanged UI Overlay hoặc không hợp lệ: '" << ev.text << "'." << std::endl;
                    break;
                }
                if (ev.text == currentAreaName && !hasInstanceTransition && !gameSession.IsInstanceTransitionPending()) {
                    std::cout << "[LogSensor] Bỏ qua SceneChanged trùng map (không có chuyển instance): '" << ev.text << "'." << std::endl;
                    break;
                }
                hasInstanceTransition = false;
                currentAreaName = ev.text;
                lootCtrl.ResetAreaState();
                areaEventMgr.OnAreaChanged(static_cast<uint32_t>(areaSeed));
                terrainGrid.Reset();
                gameSession.OnZoneSceneChanged(ev.text);
                std::cout << "[LogSensor] Vào khu vực: " << ev.text << std::endl;
                break;
            case GameEventType::LoadingFinished:
                hasInstanceTransition = false;
                areaLoadTimestampMs = ev.timestampMs;
                gameSession.OnZoneLoadingFinished(ev.text);
                gameSession.AutoDetectTerrain(&terrainGrid, samplePacket.player.posX, samplePacket.player.posY);
                autoLogin.OnLoadingFinished(ev.text, ev.timestampMs);
                townQuestEngine.OnLoadingFinished(ev.text, ev.timestampMs);
                std::cout << "[LogSensor] Tải màn xong: " << ev.text
                          << " (" << ev.loadDurationSec << "s)" << std::endl;
                break;
            case GameEventType::WhisperReceived:
                std::cout << "[LogSensor][ALERT] Whisper từ " << ev.text
                          << ": " << ev.text2 << std::endl;
                break;
            case GameEventType::AbnormalDisconnect:
                std::cout << "[LogSensor][ALERT] Mất kết nối: " << ev.text << std::endl;
                if (autoLogin.GetConfig().enabled) {
                    autoLogin.OnDisconnect(ev.text, loopNowMs);
                }
                break;
            case GameEventType::AccountLocked:
                std::cout << "[LogSensor][RED ALERT] TÀI KHOẢN BỊ KHÓA! Dừng toàn bộ hoạt động." << std::endl;
                break;
            case GameEventType::WindowFocus:
                std::cout << "[LogSensor] Window focus: " << ev.text << std::endl;
                break;
            case GameEventType::PlayerJoinedArea: {
                const std::string& playerName = ev.text;
                if (logSensor.IsFriend(playerName)) {
                    std::cout << "[LogSensor] Đồng đội/bạn bè vào khu vực: " << playerName
                              << " (Trong Whitelist - Tiếp tục hoạt động bình thường)" << std::endl;
                    CoreLog("[LogSensor] Whitelisted companion entered: " + playerName);
                } else {
                    std::cout << "[LogSensor][SECURITY WARNING] PHÁT HIỆN NGƯỜI LẠ VÀO KHU VỰC: "
                              << playerName << " (KHÔNG CÓ TRONG WHITELIST)!" << std::endl;
                    CoreLog("[LogSensor][SECURITY WARNING] Stranger entered area: " + playerName);
                    overlay::SetLines({
                        ">> CẢNH BÁO: NGƯỜI LẠ <<",
                        playerName,
                        "(Khong trong Whitelist)"
                    });
                }
                break;
            }
            case GameEventType::PlayerLeftArea:
                std::cout << "[LogSensor] Người chơi rời khu vực: " << ev.text << std::endl;
                CoreLog("[LogSensor] Player left area: " + ev.text);
                break;
            case GameEventType::PlayerSlain:
                std::cout << "[LogSensor][COMBAT ALERT] Người chơi bị hạ gục: " << ev.text << std::endl;
                CoreLog("[LogSensor][COMBAT ALERT] Player slain: " + ev.text);
                reflexMgr.State() = ReflexState{};
                comboMgr.State() = ComboState{};
                overlay::SetLines({ ">> NHÂN VẬT TỬ VONG <<", ev.text, "Tạm dừng chuỗi combo" });
                break;
            case GameEventType::PlayerSuicide:
                std::cout << "[LogSensor] Người chơi tự sát: " << ev.text << std::endl;
                CoreLog("[LogSensor] Player suicide: " + ev.text);
                break;
            case GameEventType::PartyChat:
                std::cout << "[LogSensor][Party] " << ev.text << ": " << ev.text2 << std::endl;
                break;
            case GameEventType::GuildChat:
                std::cout << "[LogSensor][Guild] " << ev.text << ": " << ev.text2 << std::endl;
                break;
            case GameEventType::TradeAccepted:
                std::cout << "[LogSensor][TRADE] Giao dịch thành công!" << std::endl;
                CoreLog("[LogSensor][TRADE] Trade accepted");
                break;
            case GameEventType::TradeCancelled:
                std::cout << "[LogSensor][TRADE] Giao dịch bị hủy." << std::endl;
                CoreLog("[LogSensor][TRADE] Trade cancelled");
                break;
            case GameEventType::InstanceConnected:
                if (ev.loadDurationSec > 0) {
                    std::cout << "[LogSensor] Ping máy chủ: " << static_cast<int>(ev.loadDurationSec) << "ms" << std::endl;
                } else {
                    std::cout << "[LogSensor] Đã kết nối Instance Server: " << ev.text << std::endl;
                }
                break;
            case GameEventType::MonstersRemain:
                std::cout << "[LogSensor][INFO] Số quái vật còn lại trong map: " << ev.areaLevel << " (" << ev.text << ")" << std::endl;
                CoreLog("[LogSensor] Monsters remaining: " + std::to_string(ev.areaLevel));
                break;
            default:
                break;
            }
        }

        // Xử lý tự động quét ngầm tọa độ XYZ (Zero User Input)
        gameSession.ProcessAutoPositionScan();

        // RCA 21:03: XYZ khởi động — nếu đã khóa HP mà chưa có XYZ và máy trạng thái
        // đang IDLE (không có sự kiện đổi map sắp tới) -> tự kích hoạt calibration.
        // Thử lại mỗi 30s cho tới khi PositionCalibrated() (chỉ ở backend thật).
        {
            static uint64_t s_lastStartupXyzMs = 0;
            const uint64_t startupNowMs = SharedMemoryManager::NowMs();
            if (!gameSession.IsSimulated()
                && gameSession.PlayerCalibrated()
                && !gameSession.PositionCalibrated()
                && gameSession.AutoScanState() == GameSession::AutoPositionState::IDLE
                && startupNowMs - coreStartMs > 2000
                && startupNowMs - s_lastStartupXyzMs >= 30000) {
                s_lastStartupXyzMs = startupNowMs;
                gameSession.StartStartupXYZCalibration();
            }
        }

        // Publish snapshot vào Shared Memory (Seqlock double-buffer, 120Hz)
        samplePacket.snapshotId = ++snapshotCounter;
        samplePacket.timestamp = SharedMemoryManager::NowMs();
        gameSession.Update(samplePacket); // Đọc player + entity từ backend bộ nhớ
        if (samplePacket.player.posX != 0.0f || samplePacket.player.posY != 0.0f) {
            terrainGrid.RecenterIfNeeded(samplePacket.player.posX, samplePacket.player.posY);
        }
        samplePacket.area.areaLoadTimestampMs = areaLoadTimestampMs;
        samplePacket.area.areaLevel = areaLevel;
        samplePacket.area.areaSeed = static_cast<uint32_t>(areaSeed);
        copyBounded(samplePacket.area.areaCode, sizeof(samplePacket.area.areaCode), currentAreaCode);
        copyBounded(samplePacket.area.areaName, sizeof(samplePacket.area.areaName), currentAreaName);

        const uint64_t tickMs = SharedMemoryManager::NowMs();
        nonBlockingActuator.ProcessPendingReleases(tickMs);
        areaEventMgr.UpdateCandidates(samplePacket, tickMs);

        // Nếu đang trong quy trình tự động đăng nhập lại (AutoLogin) hoặc điều hướng Làng (TownQuestEngine)
        if (autoLogin.IsActive()) {
            autoLogin.Tick(kmbox, tickMs);
        } else if (townQuestEngine.IsActive()) {
            townQuestEngine.Tick(kmbox, tickMs);
        } else {
            // 1. Điều phối phản xạ sinh tồn & chiến đấu POE2 (Iframe Dodge Roll, Smart Flask Sipping, Kiting XYZ)
            if (reflexMgr.Update(samplePacket, kmbox, tickMs)) {
                std::cout << "[Core Alert] >> EMERGENCY ESCAPE PORTAL ĐÃ MỞ << Bot tiếp tục duy trì phản xạ bảo vệ!" << std::endl;
            }

            // 1b. Cập nhật Character State Machine vòng đời nhân vật 120Hz (O(1), Zero Dynamic Allocation)
            charFsm.SetAutonomousEnabled(allowPhysicalMove && !isPassiveCoPilotMode);
            charFsm.Update(samplePacket, tickMs);

            // 2. BotBrain Priority Arbiter (Doc 29 - INV-BRAIN-01 & INV-BRAIN-02)
            // INV-F8-MUTES-BRAIN: Chỉ Execute BotBrain khi allowPhysicalMove == true và không ở Co-Pilot Passive!
            if (!isPassiveCoPilotMode && allowPhysicalMove) {
                if (!reflexMgr.ConsumedExclusiveInput()) {
                    botBrain.Process(samplePacket, kmbox, tickMs);
                    const bool isTown = common::IsTownOrHideout(common::ClassifyZone(samplePacket.area));
                    unstuckHandler.RecordAction(botBrain.LastChosen().kind, botBrain.LastChosen().entityId, tickMs, isTown);
                }
            } else if (!allowPhysicalMove) {
                // Đảm bảo khi F8 PAUSE, nhả toàn bộ phím WASD / Move-Only để nhân vật đứng yên hoàn toàn (INV-F8-MUTES-BRAIN)
                moveCtrl.Stop(kmbox);
                questNav.StopMovement(kmbox);
                mapDeviceHandler.ReleaseApproachKeys(kmbox);
                lootCtrl.ReleaseLootWasd(kmbox);
                unstuckHandler.ReleaseHeldWasd(kmbox);
                areaEventHdl.ReleaseApproachKeys(kmbox);
                comboMgr.ReleaseEngageKeys(kmbox);
            }
        }

        // Đồng bộ hóa trạng thái CharacterFSM vào byte cao của movementFlags cho Python Agent (GAP-6)
        samplePacket.player.movementFlags = (samplePacket.player.movementFlags & 0x0000FFFF) | (static_cast<uint32_t>(charFsm.State()) << 16);
        shm.WriteTelemetry(samplePacket);

        // Rút toàn bộ lệnh vĩ mô đang chờ từ Agent (SPSC ring, FIFO)
        CommandPacket cmd{};
        while (shm.FetchNextCommand(cmd)) {
            ++commandsProcessed;
            if (DispatchCommand(cmd, kmbox, curveGen, allowPhysicalMove, gameSession, autoLogin, townQuestEngine, &questNav, &invHandler, &mapDeviceHandler, &npcHandler, &comboMgr, &reflexMgr, &nonBlockingActuator)) {
                shm.RequestStop();
            }
            if (!shm.IsRunning()) break;
        }
        if (!shm.IsRunning()) break;

        // In trạng thái định kỳ
        const uint64_t nowMs = SharedMemoryManager::NowMs();

        // 5c. Live-adaptation: Tự động thích ứng khi người dùng đổi WASD <-> Chuột hoặc độ phân giải trong game
        static uint64_t lastConfigCheckMs = 0;
        static std::filesystem::file_time_type lastConfigWriteTime{};
        if (nowMs - lastConfigCheckMs >= 1000) {
            lastConfigCheckMs = nowMs;
            std::string cfgPath = GetPoe2ConfigPath();
            if (!cfgPath.empty()) {
                std::error_code ec;
                auto wt = std::filesystem::last_write_time(cfgPath, ec);
                if (!ec && wt != lastConfigWriteTime) {
                    lastConfigWriteTime = wt;
                    Poe2GameSettings newSettings = ReadPoe2GameSettings();
                    if (!explicitMoveModeFromCli && newSettings.moveMode != moveMode) {
                        moveMode = newSettings.moveMode;
                        gameSession.SetMovementMode(moveMode); // Micro-Step thích ứng theo mode mới
                        reflexCfg.moveMode = moveMode; reflexMgr.SetConfig(reflexCfg);
                        lootCfg.moveMode = moveMode;
                        questCfg.moveMode = moveMode; questNav.SetConfig(questCfg);

                        const std::string mStr = (moveMode == MovementMode::WASD ? "WASD" : "MOUSE");
                        std::cout << "[LiveAdapt] >> PHÁT HIỆN THAY ĐỔI TRONG GAME -> TỰ ĐỘNG THÍCH ỨNG: "
                                  << mStr << " <<" << std::endl;
                        CoreLog("[LiveAdapt] Movement mode dynamically adapted to: " + mStr);
                    }

                    if (lootCfg.screenWidth != newSettings.resolutionWidth ||
                        lootCfg.screenHeight != newSettings.resolutionHeight ||
                        lootCfg.hideFilteredGroundItems != newSettings.hideFilteredGroundItems ||
                        lootCfg.moveMode != moveMode) {
                        lootCfg.screenWidth = newSettings.resolutionWidth;
                        lootCfg.screenHeight = newSettings.resolutionHeight;
                        lootCfg.hideFilteredGroundItems = newSettings.hideFilteredGroundItems;
                        lootCfg.moveMode = moveMode;
                        lootCtrl.SetConfig(lootCfg);
                        std::cout << "[LiveAdapt] Cập nhật LootConfig: " << lootCfg.screenWidth << "x" << lootCfg.screenHeight
                                  << ", FilterGround=" << (lootCfg.hideFilteredGroundItems ? "true" : "false")
                                  << ", Mode=" << (moveMode == MovementMode::WASD ? "WASD" : "MOUSE") << std::endl;
                        CoreLog("[LiveAdapt] LootConfig synchronized with in-game settings.");
                    }
                }
            }
        }

        if (nowMs - lastStatusPrintMs >= kStatusPrintIntervalMs) {
            lastStatusPrintMs = nowMs;

            static float lastRecordedX = 0, lastRecordedY = 0, lastRecordedZ = 0;
            static uint64_t lastRecordedMoveMs = SharedMemoryManager::NowMs();
            static bool isPositionStale = false;

            const float dX = samplePacket.player.posX - lastRecordedX;
            const float dY = samplePacket.player.posY - lastRecordedY;
            const float dZ = samplePacket.player.posZ - lastRecordedZ;
            if ((dX * dX + dY * dY + dZ * dZ) > 0.04f) {
                lastRecordedX = samplePacket.player.posX;
                lastRecordedY = samplePacket.player.posY;
                lastRecordedZ = samplePacket.player.posZ;
                lastRecordedMoveMs = nowMs;
                isPositionStale = false;
            } else if (nowMs - lastRecordedMoveMs > 4000 && samplePacket.player.maxHP > 0) {
                isPositionStale = true;

                // TỰ ĐỘNG PHỤC HỒI (Zero-Touch Auto-Recovery):
                // Chỉ tự động quét lại khi tọa độ thực sự chưa có (0, 0, 0) hoặc không hợp lệ,
                // và người chơi đang nhấn phím di chuyển (WASD) hoặc bot đang tự động di chuyển.
                // Tuyệt đối không xóa baseline khi nhân vật chỉ đang đứng yên tương tác UI.
                const bool isKeyMoveInput = (GetAsyncKeyState('W') & 0x8000) ||
                                            (GetAsyncKeyState('A') & 0x8000) ||
                                            (GetAsyncKeyState('S') & 0x8000) ||
                                            (GetAsyncKeyState('D') & 0x8000);
                const bool coordEmpty = (samplePacket.player.posX == 0.0f && samplePacket.player.posY == 0.0f);
                const bool coordInvalid = !PlayerFinder::IsValidXYZ(samplePacket.player.posX, samplePacket.player.posY, samplePacket.player.posZ);
                static uint64_t lastAutoRecoveryMs = 0;
                if ((coordEmpty || coordInvalid) && (isKeyMoveInput || allowPhysicalMove) && (nowMs - lastAutoRecoveryMs >= 6000)) {
                    lastAutoRecoveryMs = nowMs;
                    // CHỈ quét lại khi hệ thống đang ở trạng thái IDLE!
                    // Nếu đang CAPTURING_STILL_BASELINE hoặc WAITING_FIRST_STEP, tuyệt đối không được xóa baseline!
                    if (gameSession.AutoScanState() == GameSession::AutoPositionState::IDLE) {
                        std::cout << "\n[AutoRecovery] ⚠️ Phát hiện di chuyển nhưng chưa có tọa độ hợp lệ -> Tự động quét lại XYZ..." << std::endl;
                        CoreLog("[AutoRecovery] Movement input with empty/invalid coords. Auto-triggering silent XYZ scan.");
                        gameSession.TriggerManualAutoScan();
                    }
                }
            }

            std::string xyzStatus = isPositionStale ? " [!] DUNG IM" : " [OK]";

            uint32_t monsterCnt = 0, itemCnt = 0, portalCnt = 0;
            for (uint32_t i = 0; i < samplePacket.entityCount; ++i) {
                if (samplePacket.entities[i].type == 1) ++monsterCnt;
                else if (samplePacket.entities[i].type == 2) ++itemCnt;
                else if (samplePacket.entities[i].type == 3) ++portalCnt;
            }

            std::string statusStr = "[Status] Snapshots=" + std::to_string(snapshotCounter)
                      + " | Commands=" + std::to_string(commandsProcessed)
                      + " | Radar=" + std::to_string(samplePacket.entityCount)
                      + " (Mobs:" + std::to_string(monsterCnt) + " Items:" + std::to_string(itemCnt) + " Portals:" + std::to_string(portalCnt) + ")"
                      + " | HP=" + std::to_string(samplePacket.player.currentHP) + "/" + std::to_string(samplePacket.player.maxHP)
                      + " | ES=" + std::to_string(samplePacket.player.currentES) + "/" + std::to_string(samplePacket.player.maxES)
                      + " | Mana=" + std::to_string(samplePacket.player.currentMana) + "/" + std::to_string(samplePacket.player.maxMana)
                      + " | Spirit=" + std::to_string(samplePacket.player.currentSpirit) + "/" + std::to_string(samplePacket.player.maxSpirit)
                      + (reflexMgr.State().totalRespawns > 0 ? (" | Respawns=" + std::to_string(reflexMgr.State().totalRespawns)) : "")
                      + " | XYZ=(" + std::to_string(samplePacket.player.posX).substr(0, 6) + "," + std::to_string(samplePacket.player.posY).substr(0, 6) + ")" + xyzStatus
                      + " | Pending=" + std::to_string(shm.PendingCommandCount())
                      + " | Agent=" + (shm.CheckAgentWatchdog(kAgentWatchdogTimeoutMs)
                                == SharedMemoryManager::AgentWatchdogStatus::Healthy
                              ? "ONLINE" : "OFFLINE");

            std::cout << statusStr << std::endl;
            CoreLog(statusStr);
        }

        // Cập nhật Overlay thời gian thực (Realtime In-Game HUD: 50Hz, 20ms)
        static uint64_t lastOverlayUpdateMs = 0;
        if (nowMs - lastOverlayUpdateMs >= 20) {
            lastOverlayUpdateMs = nowMs;

            uint32_t overlayMobCnt = 0, overlayItemCnt = 0, overlayPortalCnt = 0;
            for (uint32_t i = 0; i < samplePacket.entityCount; ++i) {
                if (samplePacket.entities[i].type == 1) ++overlayMobCnt;
                else if (samplePacket.entities[i].type == 2) ++overlayItemCnt;
                else if (samplePacket.entities[i].type == 3) ++overlayPortalCnt;
            }

            std::string xyzStat = (samplePacket.player.posX == 0.0f && samplePacket.player.posY == 0.0f) ? " [!] CHƯA CÓ XYZ" : " [OK]";

            // Xác định Trạng Thái & Hướng Dẫn cho Người Dùng
            std::string stateStr, guideStr;
            if (samplePacket.player.currentHP == 0 && samplePacket.player.maxHP > 0) {
                stateStr = ">> TRẠNG THÁI: 💀 NHÂN VẬT TỬ VONG";
                guideStr = ">> HƯỚNG DẪN: Chờ tự động hồi sinh hoặc bấm hồi sinh trong làng";
            } else if (samplePacket.player.maxHP == 0) {
                stateStr = ">> TRẠNG THÁI: ⏳ CHỜ KHÓA HP (Grace Period - ĐANG BẢO VỆ)";
                guideStr = ">> HƯỚNG DẪN: Đang tự dò tìm HP... Di chuyển 1 bước nếu cần";
            } else if (!allowPhysicalMove) {
                stateStr = ">> TRẠNG THÁI: ⏸️ ĐANG TẠM DỪNG (Bấm F8 hoặc F6 để Bật)";
                guideStr = ">> HƯỚNG DẪN: Nhấn F8 hoặc F6 để kích hoạt tự hành làm quest";
            } else if (comboMgr.State().inCombatUntilMs > nowMs || overlayMobCnt > 0) {
                stateStr = ">> TRẠNG THÁI: ⚔️ ĐANG CHIẾN ĐẤU (Monk Combo & Smart Reflex)";
                guideStr = ">> HƯỚNG DẪN: Đang diệt " + std::to_string(overlayMobCnt) + " quái vật trong khu vực";
            } else if (lootCtrl.State().lastLootMs > 0 && (nowMs - lootCtrl.State().lastLootMs < 600)) {
                stateStr = ">> TRẠNG THÁI: 💰 ĐANG THU THẬP VẬT PHẨM (Auto-Loot <5ms)";
                guideStr = ">> HƯỚNG DẪN: Đang nhặt đồ & vàng xung quanh";
            } else {
                stateStr = ">> TRẠNG THÁI: 🟢 ĐANG TỰ HÀNH TUẦN TRA / DÒ MAP";
                guideStr = ">> HƯỚNG DẪN: Mục tiêu: " + (questNav.State().targetName.empty() ? "Khám phá bản đồ" : questNav.State().targetName);
            }

            overlay::SetLines({
                ">> AutoPOE2 HUD v2026.09 [F8 / F6 Bật-Tắt] [" + std::string(moveMode == MovementMode::MOUSE ? "Click-to-Move" : "WASD") + "]",
                stateStr,
                guideStr,
                ">> HP: " + std::to_string(samplePacket.player.currentHP) + "/" + std::to_string(samplePacket.player.maxHP)
                    + " | ES: " + std::to_string(samplePacket.player.currentES) + "/" + std::to_string(samplePacket.player.maxES)
                    + (samplePacket.player.maxWard > 0 ? (" | Ward: " + std::to_string(samplePacket.player.currentWard) + "/" + std::to_string(samplePacket.player.maxWard)) : "")
                    + " | Mana: " + std::to_string(samplePacket.player.currentMana) + "/" + std::to_string(samplePacket.player.maxMana)
                    + (samplePacket.player.maxSpirit > 0 ? (" | Spirit: " + std::to_string(samplePacket.player.currentSpirit) + "/" + std::to_string(samplePacket.player.maxSpirit)) : ""),
                ">> XYZ: " + std::to_string(samplePacket.player.posX).substr(0, 6)
                    + ", " + std::to_string(samplePacket.player.posY).substr(0, 6)
                    + ", " + std::to_string(samplePacket.player.posZ).substr(0, 6)
                    + xyzStat + " | Radar: " + std::to_string(overlayMobCnt) + " Mobs, "
                    + std::to_string(overlayItemCnt) + " Items",
                ">> Reflex: Flask#" + std::to_string(reflexMgr.State().lifeUses)
                    + " | Dodge#" + std::to_string(reflexMgr.State().dodgeCount)
                    + " | Loot: " + std::to_string(lootCtrl.State().totalItemsLooted)
                    + " | Done#" + std::to_string(questNav.State().questsProgressed)
            });
        }

        // Nhịp vòng lặp 120Hz (8.33ms)
        std::this_thread::sleep_for(std::chrono::milliseconds(8));
    }

    ReleaseAllInputsFailsafe();
    shm.Shutdown();
    overlay::Stop();
    // TASK-OPT-07: Khôi phục độ phân giải timer hệ thống trước khi thoát
    timeEndPeriod(1);
    std::cout << "[Core Clean] Tiến trình Core Engine đã đóng an toàn." << std::endl;
    return 0;
}
