#include "input/kmbox_net.hpp"
#include "common/window_utils.hpp"
#include <ws2tcpip.h>
#include <iostream>
#include <thread>
#include <chrono>
#include <cmath>

#pragma pack(push, 1)
// Cấu trúc gói tin nhị phân chuẩn của KMBox Net
struct KMBoxPacket {
    uint32_t mac;
    uint32_t randVal;
    uint32_t index;
    uint32_t cmd;
    union {
        struct {
            short x;
            short y;
        } move;
        struct {
            uint32_t buttonMask;
        } mouseBtn;
        struct {
            uint8_t scanCode;
            uint8_t isDown;
        } key;
    } payload;
};
#pragma pack(pop)

KMBoxNet::KMBoxNet() {
    WSADATA wsa;
    WSAStartup(MAKEWORD(2, 2), &wsa);
}

KMBoxNet::~KMBoxNet() {
    Disconnect();
    WSACleanup();
}

bool KMBoxNet::Connect(const std::string& ip, int port, uint32_t mac) {
    m_macAddress = mac;
    m_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    
    if (m_socket == INVALID_SOCKET) {
        std::cerr << "[KMBox Error] Không tạo được socket. Lỗi: " << WSAGetLastError() << std::endl;
        return false;
    }

    m_deviceAddr.sin_family = AF_INET;
    m_deviceAddr.sin_port = htons(port);
    inet_pton(AF_INET, ip.c_str(), &m_deviceAddr.sin_addr);

    m_isConnected = true;
    std::cout << "[KMBox] Đã cấu hình điều khiển tới " << ip << ":" << port 
              << " (MAC: 0x" << std::hex << mac << std::dec << ")" << std::endl;
    return true;
}

void KMBoxNet::Disconnect() {
    if (m_socket != INVALID_SOCKET) {
        closesocket(m_socket);
        m_socket = INVALID_SOCKET;
    }
    m_isConnected = false;
}

void KMBoxNet::EnableEmulationMode(bool enable) {
    m_emulationMode = enable;
    if (enable) {
        std::cout << "[KMBox] Chế độ mô phỏng phần mềm đã BẬT (Dành cho máy chưa cắm thiết bị KMBox)." << std::endl;
    }
}

bool KMBoxNet::IsGameWindowFocused() const {
    if (m_forcedFocus == 0) {
        if (m_lastGameFocused) {
            m_lastGameFocused = false;
            const_cast<KMBoxNet*>(this)->ReleaseAllKeys();
        }
        return false;
    }
    if (m_forcedFocus == 1 || m_ignoreWindowFocus || s_globalIgnoreWindowFocus) {
        m_lastGameFocused = true;
        return true;
    }

    HWND fg = GetForegroundWindow();
    bool currentlyFocused = (fg != nullptr) && common::IsPoe2WindowHwnd(fg);

    if (m_lastGameFocused && !currentlyFocused) {
        // Cửa sổ game vừa mất tiêu điểm (transition true -> false)! Cưỡng chế nhả toàn bộ phím phần cứng
        m_lastGameFocused = false;
        const_cast<KMBoxNet*>(this)->ReleaseAllKeys();
        return false;
    }

    m_lastGameFocused = currentlyFocused;
    return currentlyFocused;
}

void KMBoxNet::ReleaseAllKeys() {
    // 1. Nhả toàn bộ phím chuột đang giữ (Left, Right, Middle)
    for (int btn : {1, 2, 4}) {
        if ((m_heldMouseButtons & btn) != 0) {
            MouseUp(btn);
        }
    }
    m_heldMouseButtons = 0;

    // 2. Nhả toàn bộ các phím HID đã ghi nhận KeyDown
    for (size_t hid = 0; hid < m_heldHidKeys.size(); ++hid) {
        if (m_heldHidKeys[hid]) {
            KeyUp(static_cast<uint8_t>(hid));
            m_heldHidKeys[hid] = false;
        }
    }

    // 3. Cưỡng chế nhả danh sách phím điều khiển cốt lõi phòng ngừa rò rỉ phần cứng
    // WASD (W=0x1A, A=0x04, S=0x16, D=0x07), Move-Only (T=0x17), Space (0x2C), Modifiers (Shift=0xE1, Ctrl=0xE0, Alt=0xE2)
    // Combat: Q=0x14, E=0x08, R=0x15, Flasks: 1=0x1E, 2=0x1F, 3=0x20, 4=0x21, 5=0x22
    static constexpr uint8_t kCoreHidsToRelease[] = {
        0x1A, // 'W'
        0x04, // 'A'
        0x16, // 'S'
        0x07, // 'D'
        0x17, // 'T' (Move-Only)
        0x2C, // Space (Dodge Roll)
        0xE0, // Left Ctrl
        0xE1, // Left Shift
        0xE2, // Left Alt
        0x14, // 'Q'
        0x08, // 'E'
        0x15, // 'R'
        0x1E, // '1'
        0x1F, // '2'
        0x20, // '3'
        0x21, // '4'
        0x22  // '5'
    };
    for (uint8_t hid : kCoreHidsToRelease) {
        KeyUp(hid);
    }
}

bool KMBoxNet::MoveMouse(short rx, short ry) {
    if (!IsGameWindowFocused()) {
        return false; // KHÓA AN TOÀN: Game không ở tiền cảnh -> Tuyệt đối không gửi bất kỳ tín hiệu chuột/phím nào!
    }

    if (m_emulationMode) {
        // Dự phòng bằng Windows mouse_event nếu chưa có phần cứng
        mouse_event(MOUSEEVENTF_MOVE, rx, ry, 0, 0);
        return true;
    }

    if (!m_isConnected) return false;

    KMBoxPacket pkt{};
    pkt.mac = m_macAddress;
    pkt.cmd = 0xA2345678; // Opcode KMBox Move
    pkt.payload.move.x = rx;
    pkt.payload.move.y = ry;

    int sent = sendto(m_socket, reinterpret_cast<const char*>(&pkt), sizeof(pkt), 0,
                      reinterpret_cast<const sockaddr*>(&m_deviceAddr), sizeof(m_deviceAddr));
    return sent > 0;
}

bool KMBoxNet::MoveMouseSmooth(int targetX, int targetY, HumanCurveGenerator& curveGen, int steps, int stepDelayMs) {
    if (!IsGameWindowFocused()) {
        return false; // KHÓA AN TOÀN: Game không ở tiền cảnh -> Tuyệt đối không gửi bất kỳ tín hiệu chuột/phím nào!
    }

    POINT curPt{};
    GetCursorPos(&curPt);

    if (std::abs(targetX - curPt.x) < 2 && std::abs(targetY - curPt.y) < 2) {
        return true;
    }

    Vector2D startPos{ static_cast<float>(curPt.x), static_cast<float>(curPt.y) };
    Vector2D targetPos{ static_cast<float>(targetX), static_cast<float>(targetY) };
    common::TrajectoryBuffer trajectory;
    curveGen.GenerateTrajectory(startPos, targetPos, trajectory, steps, true);

    if (m_emulationMode) {
        float currX = startPos.x;
        float currY = startPos.y;
        for (const auto& step : trajectory) {
            currX += step.x;
            currY += step.y;
            SetCursorPos(static_cast<int>(std::round(currX)), static_cast<int>(std::round(currY)));
            if (stepDelayMs > 0) {
                std::this_thread::sleep_for(std::chrono::milliseconds(stepDelayMs));
            }
        }
        SetCursorPos(targetX, targetY);
    } else {
        for (const auto& step : trajectory) {
            MoveMouse(static_cast<short>(step.x), static_cast<short>(step.y));
            if (stepDelayMs > 0) {
                std::this_thread::sleep_for(std::chrono::milliseconds(stepDelayMs));
            }
        }
        POINT endPt{};
        GetCursorPos(&endPt);
        short remX = static_cast<short>(targetX - endPt.x);
        short remY = static_cast<short>(targetY - endPt.y);
        if (remX != 0 || remY != 0) {
            MoveMouse(remX, remY);
        }
    }
    return true;
}

bool KMBoxNet::MouseDown(int button) {
    if (!IsGameWindowFocused()) {
        return false; // KHÓA AN TOÀN: Game không ở tiền cảnh -> Tuyệt đối không gửi bất kỳ tín hiệu chuột/phím nào!
    }

    if (!CanPerformAction()) {
        return false; // Action Gatekeeper: Chặn spam click chuột chống kick server
    }
    RecordAction();

    m_heldMouseButtons |= static_cast<uint8_t>(button);

    if (m_emulationMode) {
        DWORD downFlag = (button == 1) ? MOUSEEVENTF_LEFTDOWN : ((button == 2) ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_MIDDLEDOWN);
        mouse_event(downFlag, 0, 0, 0, 0);
        return true;
    }

    if (!m_isConnected) return false;

    KMBoxPacket pkt{};
    pkt.mac = m_macAddress;
    pkt.cmd = 0xB3456789; // Opcode KMBox Button Down
    pkt.payload.mouseBtn.buttonMask = button;
    int sent = sendto(m_socket, reinterpret_cast<const char*>(&pkt), sizeof(pkt), 0,
                      reinterpret_cast<const sockaddr*>(&m_deviceAddr), sizeof(m_deviceAddr));
    return sent > 0;
}

bool KMBoxNet::MouseUp(int button) {
    // KHÓA AN TOÀN: MouseUp TUYỆT ĐỐI KHÔNG BỊ CHẶN bởi IsGameWindowFocused()!
    // Nhả phím chuột luôn luôn phải được gửi tới KMBox để giải phóng phần cứng!
    m_heldMouseButtons &= ~static_cast<uint8_t>(button);

    if (m_emulationMode) {
        DWORD upFlag = (button == 1) ? MOUSEEVENTF_LEFTUP : ((button == 2) ? MOUSEEVENTF_RIGHTUP : MOUSEEVENTF_MIDDLEUP);
        mouse_event(upFlag, 0, 0, 0, 0);
        return true;
    }

    if (!m_isConnected) return false;

    KMBoxPacket pkt{};
    pkt.mac = m_macAddress;
    pkt.cmd = 0xB345678A; // Opcode KMBox Button Up
    pkt.payload.mouseBtn.buttonMask = 0;
    int sent = sendto(m_socket, reinterpret_cast<const char*>(&pkt), sizeof(pkt), 0,
                      reinterpret_cast<const sockaddr*>(&m_deviceAddr), sizeof(m_deviceAddr));
    return sent > 0;
}

bool KMBoxNet::ClickMouse(int button, int dwellTimeMs) {
    if (!MouseDown(button)) return false;
    std::this_thread::sleep_for(std::chrono::milliseconds(dwellTimeMs));
    return MouseUp(button);
}

bool KMBoxNet::ClickMouseHumanized(int button, HumanCurveGenerator& curveGen, float meanMs, float stdDevMs) {
    if (!IsGameWindowFocused()) {
        return false; // KHÓA AN TOÀN: Game không ở tiền cảnh -> Tuyệt đối không gửi bất kỳ tín hiệu chuột/phím nào!
    }

    int dwellTime = curveGen.GenerateDwellTimeMs(meanMs, stdDevMs);
    return ClickMouse(button, dwellTime);
}

bool KMBoxNet::Wheel(int delta) {
    if (!IsGameWindowFocused()) {
        return false; // KHÓA AN TOÀN: Game không ở tiền cảnh -> Tuyệt đối không gửi bất kỳ tín hiệu chuột nào!
    }

    if (!CanPerformAction()) {
        return false;
    }
    RecordAction();

    if (m_emulationMode) {
        mouse_event(MOUSEEVENTF_WHEEL, 0, 0, static_cast<DWORD>(delta), 0);
        return true;
    }

    // Hardware packet fallback qua mouse_event
    mouse_event(MOUSEEVENTF_WHEEL, 0, 0, static_cast<DWORD>(delta), 0);
    return true;
}

uint8_t KMBoxNet::VkToUsbHid(uint8_t vk) {
    if (vk >= 'A' && vk <= 'Z') {
        return static_cast<uint8_t>(0x04 + (vk - 'A'));
    }
    if (vk >= 'a' && vk <= 'z') {
        return static_cast<uint8_t>(0x04 + (vk - 'a'));
    }
    if (vk >= '1' && vk <= '9') {
        return static_cast<uint8_t>(0x1E + (vk - '1'));
    }
    if (vk == '0') return 0x27;

    if (vk >= VK_F1 && vk <= VK_F12) {
        return static_cast<uint8_t>(0x3A + (vk - VK_F1));
    }

    switch (vk) {
        case VK_RETURN:    return 0x28; // Enter
        case VK_ESCAPE:    return 0x29; // Esc
        case VK_BACK:      return 0x2A; // Backspace
        case VK_TAB:       return 0x2B; // Tab
        case VK_SPACE:     return 0x2C; // Spacebar
        case VK_OEM_MINUS: return 0x2D; // -
        case VK_OEM_PLUS:  return 0x2E; // =
        case VK_OEM_4:     return 0x2F; // [
        case VK_OEM_6:     return 0x30; // ]
        case VK_OEM_5:     return 0x31; // \ (Backslash)
        case VK_OEM_1:     return 0x33; // ;
        case VK_OEM_7:     return 0x34; // '
        case VK_OEM_3:     return 0x35; // ` (Tilde)
        case VK_OEM_COMMA: return 0x36; // ,
        case VK_OEM_PERIOD:return 0x37; // .
        case VK_OEM_2:     return 0x38; // /
        case VK_CAPITAL:   return 0x39; // Caps Lock
        case VK_SNAPSHOT:  return 0x46; // PrintScreen
        case VK_SCROLL:    return 0x47; // ScrollLock
        case VK_PAUSE:     return 0x48; // Pause
        case VK_INSERT:    return 0x49; // Insert
        case VK_HOME:      return 0x4A; // Home
        case VK_PRIOR:     return 0x4B; // Page Up
        case VK_DELETE:    return 0x4C; // Delete
        case VK_END:       return 0x4D; // End
        case VK_NEXT:      return 0x4E; // Page Down
        case VK_RIGHT:     return 0x4F; // Right Arrow
        case VK_LEFT:      return 0x50; // Left Arrow
        case VK_DOWN:      return 0x51; // Down Arrow
        case VK_UP:        return 0x52; // Up Arrow
        case VK_NUMLOCK:   return 0x53; // Num Lock
        case VK_DIVIDE:    return 0x54; // Keypad /
        case VK_MULTIPLY:  return 0x55; // Keypad *
        case VK_SUBTRACT:  return 0x56; // Keypad -
        case VK_ADD:       return 0x57; // Keypad +
        case VK_CONTROL:
        case VK_LCONTROL:  return 0xE0; // Left Control
        case VK_SHIFT:
        case VK_LSHIFT:    return 0xE1; // Left Shift
        case VK_MENU:
        case VK_LMENU:     return 0xE2; // Left Alt
        case VK_LWIN:      return 0xE3; // Left Win
        case VK_RCONTROL:  return 0xE4; // Right Control
        case VK_RSHIFT:    return 0xE5; // Right Shift
        case VK_RMENU:     return 0xE6; // Right Alt
        case VK_RWIN:      return 0xE7; // Right Win
        default: break;
    }

    if (vk >= VK_NUMPAD1 && vk <= VK_NUMPAD9) {
        return static_cast<uint8_t>(0x59 + (vk - VK_NUMPAD1));
    }
    if (vk == VK_NUMPAD0) return 0x62;
    if (vk == VK_DECIMAL) return 0x63;

    return 0;
}

uint8_t KMBoxNet::UsbHidToVk(uint8_t hid) {
    if (hid >= 0x04 && hid <= 0x1D) {
        return static_cast<uint8_t>('A' + (hid - 0x04));
    }
    if (hid >= 0x1E && hid <= 0x26) {
        return static_cast<uint8_t>('1' + (hid - 0x1E));
    }
    if (hid == 0x27) return '0';

    if (hid >= 0x3A && hid <= 0x45) {
        return static_cast<uint8_t>(VK_F1 + (hid - 0x3A));
    }

    switch (hid) {
        case 0x28: return VK_RETURN;
        case 0x29: return VK_ESCAPE;
        case 0x2A: return VK_BACK;
        case 0x2B: return VK_TAB;
        case 0x2C: return VK_SPACE;
        case 0x2D: return VK_OEM_MINUS;
        case 0x2E: return VK_OEM_PLUS;
        case 0x2F: return VK_OEM_4; // [
        case 0x30: return VK_OEM_6; // ]
        case 0x31: return VK_OEM_5; // \ (Backslash)
        case 0x33: return VK_OEM_1; // ;
        case 0x34: return VK_OEM_7; // '
        case 0x35: return VK_OEM_3; // `
        case 0x36: return VK_OEM_COMMA;
        case 0x37: return VK_OEM_PERIOD; // . (0xBE)
        case 0x38: return VK_OEM_2; // /
        case 0x39: return VK_CAPITAL;
        case 0x46: return VK_SNAPSHOT;
        case 0x47: return VK_SCROLL;
        case 0x48: return VK_PAUSE;
        case 0x49: return VK_INSERT;
        case 0x4A: return VK_HOME;
        case 0x4B: return VK_PRIOR;
        case 0x4C: return VK_DELETE;
        case 0x4D: return VK_END;
        case 0x4E: return VK_NEXT;
        case 0x4F: return VK_RIGHT;
        case 0x50: return VK_LEFT;
        case 0x51: return VK_DOWN;
        case 0x52: return VK_UP;
        case 0x53: return VK_NUMLOCK;
        case 0x54: return VK_DIVIDE;
        case 0x55: return VK_MULTIPLY;
        case 0x56: return VK_SUBTRACT;
        case 0x57: return VK_ADD;
        case 0x58: return VK_RETURN;
        case 0x62: return VK_NUMPAD0;
        case 0x63: return VK_DECIMAL;
        case 0xE0: return VK_CONTROL;
        case 0xE1: return VK_SHIFT;
        case 0xE2: return VK_MENU;
        case 0xE3: return VK_LWIN;
        case 0xE4: return VK_RCONTROL;
        case 0xE5: return VK_RSHIFT;
        case 0xE6: return VK_RMENU;
        case 0xE7: return VK_RWIN;
        default: break;
    }

    if (hid >= 0x59 && hid <= 0x61) {
        return static_cast<uint8_t>(VK_NUMPAD1 + (hid - 0x59));
    }

    return 0;
}

bool KMBoxNet::KeyDown(uint8_t hidScanCode) {
    if (!IsGameWindowFocused()) {
        return false; // KHÓA AN TOÀN: Game không ở tiền cảnh -> Tuyệt đối không gửi bất kỳ tín hiệu chuột/phím nào!
    }

    if (!CanPerformAction()) {
        return false; // Action Gatekeeper: Chặn spam phím KeyDown bảo vệ tài khoản
    }
    RecordAction();

    // Chuẩn hóa sang HID code nếu lỡ truyền VK
    uint8_t finalHid = hidScanCode;
    if (hidScanCode >= 0x41 && hidScanCode <= 0x5A) {
        finalHid = VkToUsbHid(hidScanCode);
    } else if (hidScanCode == VK_CONTROL || hidScanCode == VK_SHIFT || hidScanCode == VK_MENU || hidScanCode == VK_SPACE) {
        finalHid = VkToUsbHid(hidScanCode);
    }

    m_heldHidKeys[finalHid] = true;

    if (m_emulationMode) {
        uint8_t vk = UsbHidToVk(finalHid);
        if (vk == 0) vk = finalHid; // Fallback nếu đã truyền VK sẵn
        keybd_event(vk, 0, 0, 0);
        return true;
    }

    if (!m_isConnected) return false;

    KMBoxPacket pkt{};
    pkt.mac = m_macAddress;
    pkt.cmd = 0xC4567890; // Opcode KMBox Key
    pkt.payload.key.scanCode = finalHid;
    pkt.payload.key.isDown = 1;

    int sent = sendto(m_socket, reinterpret_cast<const char*>(&pkt), sizeof(pkt), 0,
                      reinterpret_cast<const sockaddr*>(&m_deviceAddr), sizeof(m_deviceAddr));
    return sent > 0;
}

bool KMBoxNet::KeyUp(uint8_t hidScanCode) {
    // KHÓA AN TOÀN: KeyUp TUYỆT ĐỐI KHÔNG BỊ CHẶN bởi IsGameWindowFocused()!
    // Nhả phím luôn luôn phải được gửi tới KMBox để giải phóng phần cứng!
    // Chuẩn hóa sang HID code nếu lỡ truyền VK
    uint8_t finalHid = hidScanCode;
    if (hidScanCode >= 0x41 && hidScanCode <= 0x5A) {
        finalHid = VkToUsbHid(hidScanCode);
    } else if (hidScanCode == VK_CONTROL || hidScanCode == VK_SHIFT || hidScanCode == VK_MENU || hidScanCode == VK_SPACE) {
        finalHid = VkToUsbHid(hidScanCode);
    }

    m_heldHidKeys[finalHid] = false;

    if (m_emulationMode) {
        uint8_t vk = UsbHidToVk(finalHid);
        if (vk == 0) vk = finalHid; // Fallback nếu đã truyền VK sẵn
        keybd_event(vk, 0, KEYEVENTF_KEYUP, 0);
        return true;
    }

    if (!m_isConnected) return false;

    KMBoxPacket pkt{};
    pkt.mac = m_macAddress;
    pkt.cmd = 0xC4567890;
    pkt.payload.key.scanCode = finalHid;
    pkt.payload.key.isDown = 0;

    int sent = sendto(m_socket, reinterpret_cast<const char*>(&pkt), sizeof(pkt), 0,
                      reinterpret_cast<const sockaddr*>(&m_deviceAddr), sizeof(m_deviceAddr));
    return sent > 0;
}

bool KMBoxNet::KeyPress(uint8_t hidScanCode, int dwellTimeMs) {
    if (!IsGameWindowFocused()) {
        return false; // KHÓA AN TOÀN: Game không ở tiền cảnh -> Tuyệt đối không gửi bất kỳ tín hiệu chuột/phím nào!
    }

    if (!KeyDown(hidScanCode)) return false;
    std::this_thread::sleep_for(std::chrono::milliseconds(dwellTimeMs));
    return KeyUp(hidScanCode);
}

bool KMBoxNet::SendKey(uint8_t hidScanCode, int dwellTimeMs) {
    if (!IsGameWindowFocused()) {
        return false; // KHÓA AN TOÀN: Game không ở tiền cảnh -> Tuyệt đối không gửi bất kỳ tín hiệu chuột/phím nào!
    }

    return KeyPress(hidScanCode, dwellTimeMs);
}

bool KMBoxNet::KeyDownVk(uint8_t vkCode) {
    if (!IsGameWindowFocused()) return false;
    uint8_t hid = VkToUsbHid(vkCode);
    if (hid == 0) hid = vkCode;
    return KeyDown(hid);
}

bool KMBoxNet::KeyUpVk(uint8_t vkCode) {
    // KHÓA AN TOÀN SƯ ĐOÀN 1: KeyUpVk TUYỆT ĐỐI KHÔNG BỊ CHẶN bởi IsGameWindowFocused()!
    uint8_t hid = VkToUsbHid(vkCode);
    if (hid == 0) hid = vkCode;
    return KeyUp(hid);
}

bool KMBoxNet::KeyPressVk(uint8_t vkCode, int dwellTimeMs) {
    if (!IsGameWindowFocused()) return false;
    if (!KeyDownVk(vkCode)) return false;
    std::this_thread::sleep_for(std::chrono::milliseconds(dwellTimeMs));
    return KeyUpVk(vkCode);
}

bool KMBoxNet::IsVkKeyHeld(uint8_t vk) const {
    uint8_t hid = VkToUsbHid(vk);
    if (hid == 0) hid = vk;
    return IsHidKeyHeld(hid);
}

bool KMBoxNet::CanPerformAction(bool isEmergency) {
    if (!m_rateLimitEnabled) return true;
    uint64_t nowMs = static_cast<uint64_t>(std::chrono::duration_cast<std::chrono::milliseconds>(
        std::chrono::steady_clock::now().time_since_epoch()).count());

    while (!m_actionHistory.empty() && (nowMs >= m_actionHistory.front()) && (nowMs - m_actionHistory.front() > 1000)) {
        m_actionHistory.erase(m_actionHistory.begin());
    }

    if (isEmergency) return true;

    return m_actionHistory.size() < m_maxActionsPerSecond;
}

void KMBoxNet::RecordAction() {
    uint64_t nowMs = static_cast<uint64_t>(std::chrono::duration_cast<std::chrono::milliseconds>(
        std::chrono::steady_clock::now().time_since_epoch()).count());
    m_actionHistory.push_back(nowMs);
    if (m_actionHistory.size() > 64) {
        m_actionHistory.erase(m_actionHistory.begin());
    }
}

size_t KMBoxNet::GetRecentActionCount() const {
    uint64_t nowMs = static_cast<uint64_t>(std::chrono::duration_cast<std::chrono::milliseconds>(
        std::chrono::steady_clock::now().time_since_epoch()).count());
    size_t count = 0;
    for (uint64_t ts : m_actionHistory) {
        if (nowMs >= ts && nowMs - ts <= 1000) {
            ++count;
        }
    }
    return count;
}
