#include "combat/reflex_manager.hpp"
#include "game_session.hpp"

#include <cmath>
#include <iostream>
#include <thread>
#include "common/logger.hpp"
#include "common/coordinate_transform.hpp"
#include "common/window_utils.hpp"
#include "input/non_blocking_actuator.hpp"

#include <algorithm>

ReflexManager::ReflexManager(const ReflexConfig& config)
    : m_config(config) {
}

uint32_t ReflexManager::GenerateGaussianDwellTime(float meanMs, float stddevMs, float minMs, float maxMs) {
    std::normal_distribution<float> dist(meanMs, stddevMs);
    float val = dist(m_rng);
    return static_cast<uint32_t>(std::clamp(val, minMs, maxMs));
}

uint32_t ReflexManager::GenerateReactionLatencyMs() {
    std::normal_distribution<float> dist(m_config.reactionMeanMs, m_config.reactionStddevMs);
    float val = dist(m_rng);
    return static_cast<uint32_t>(std::clamp(val, m_config.minReactionLatencyMs, m_config.maxReactionLatencyMs));
}

void ReflexManager::SendKey(uint8_t vkCode, uint8_t hidCode, KMBoxNet& kmbox, uint32_t holdMs, uint64_t nowMs) {
    if (!EnsurePoe2WindowFocus()) return;
    uint32_t actualHoldMs = (holdMs > 65) ? holdMs : GenerateGaussianDwellTime(48.0f, 7.0f, 35.0f, 65.0f);

    // Chế độ phi khóa NonBlockingActuator (Phase P7 SSoT: 0ms sleep, dispatch <0.005ms)
    if (m_actuator != nullptr) {
        m_actuator->KeyPressVk(vkCode, actualHoldMs, nowMs);
        return;
    }

    if (kmbox.IsEmulationMode()) {
        keybd_event(vkCode, 0, 0, 0);
        if (actualHoldMs > 0) {
            std::this_thread::sleep_for(std::chrono::milliseconds(actualHoldMs));
        }
        keybd_event(vkCode, 0, KEYEVENTF_KEYUP, 0);
    } else {
        kmbox.KeyPress(hidCode, actualHoldMs);
    }
}

bool ReflexManager::Update(const TelemetryPacket& packet, KMBoxNet& kmbox, uint64_t nowMs) {
    m_consumedExclusiveInput = false;
    if (!EnsurePoe2WindowFocus()) return false;
    const auto& player = packet.player;
    const bool isDead = (player.maxHP > 0 && player.currentHP == 0) ||
                        (player.maxHP == 1 && player.currentES == 0 && player.currentHP <= 1);
    if (isDead) {
        m_state.pendingDodge = false;
        HandleAutoRespawn(player, kmbox, nowMs);
        m_state.previousHP = player.currentHP;
        m_state.previousES = player.currentES;
        return false;
    }

    if (player.maxHP == 0) {
        // Nhân vật chưa vào map
        m_state.previousHP = player.currentHP;
        m_state.previousES = player.currentES;
        return false;
    }

    // Nếu nhân vật vừa hồi sinh thành công (từ currentHP == 0 sang > 0)
    if (m_state.deathTimestampMs != 0) {
        CoreLog("[AutoRespawn] Nhan vat da hoi sinh tai Checkpoint! (HP=" + std::to_string(player.currentHP) + "/" + std::to_string(player.maxHP) + ")");
        m_state.deathTimestampMs = 0;
        if (m_gameSession) {
            m_gameSession->OnPlayerRespawned();
        }
    }

    // 0. Cập nhật trạng thái hồi phục CI Recovery Mode độc lập với cooldown né đòn
    if (player.maxHP == 1 && m_config.ciEvasionEnabled) {
        const float esRatio = (player.maxES > 0) ? (static_cast<float>(player.currentES) / static_cast<float>(player.maxES)) : 1.0f;
        if (esRatio < m_config.ciDodgeEsThreshold) {
            m_state.isCiRecoveryMode = true;
        } else if (esRatio >= m_config.ciReengageEsThreshold) {
            if (m_state.isCiRecoveryMode) {
                m_state.isCiRecoveryMode = false;
                const std::string recLog = "[CI Recovery] ES đã hồi phục trên " + std::to_string(static_cast<int>(m_config.ciReengageEsThreshold * 100.0f)) + "% (" + std::to_string(player.currentES) + "/" + std::to_string(player.maxES) + ") -> Sẵn sàng tái tham chiến!";
                std::cout << recLog << std::endl;
                CoreLog(recLog);
            }
        }
    }

    // 1. Cấp cứu khẩn cấp (Emergency Escape) khi HP tụt sâu nguy hiểm (< 20%)
    if (HandleEmergencyLogout(player, kmbox, nowMs)) {
        // Không ngắt luồng mà tiếp tục bảo vệ nhân vật
    }

    // 2. Iframe Dodge Roll (Spacebar) khi phát hiện sốc sát thương bùng nổ (kèm độ trễ phản xạ người chơi 100-150ms)
    HandleIframeDodge(player, packet, kmbox, nowMs);

    // 3. Smart Life Flask (Phím 1) chuẩn POE2 (phá vỡ HoT khi dính đòn burst tiếp theo)
    HandleLifeFlask(player, kmbox, nowMs);

    // 4. Smart Mana Flask (Phím 2) chuẩn POE2
    HandleManaFlask(player, kmbox, nowMs);

    // 5. Dynamic Kiting (Thả diều bằng WASD theo tọa độ XYZ)
    if (m_config.kitingEnabled) {
        HandleKiting(player, packet, kmbox, nowMs);
    }

    // Lưu lại HP và ES của frame này để tính delta sát thương frame tiếp theo
    m_state.previousHP = player.currentHP;
    m_state.previousES = player.currentES;
    m_state.lastHpSampleMs = nowMs;

    return false;
}

bool ReflexManager::HandleEmergencyLogout(const PlayerTelemetryData& player, KMBoxNet& kmbox, uint64_t nowMs) {
    if (!m_config.emergencyLogoutEnabled || m_state.emergencyTriggered) return false;

    if (player.maxHP == 1) { // Chaos Inoculation (CI)
        if (player.maxES == 0) return false;
        const float esRatio = static_cast<float>(player.currentES) / static_cast<float>(player.maxES);
        if (esRatio <= m_config.emergencyHpThreshold && player.currentES > 0) {
            m_state.emergencyTriggered = true;
            m_state.pendingDodge = false; // Hủy lệnh hoãn né để dồn né ngay lập tức
            const std::string eLog = "[EMERGENCY] ES NGUY CẤP (CI: " + std::to_string(static_cast<int>(esRatio * 100.0f))
                + "%) <= " + std::to_string(static_cast<int>(m_config.emergencyHpThreshold * 100.0f))
                + "%! Kích hoạt mở Portal thoát hiểm khẩn cấp (Không dùng Life Flask)!";
            std::cout << "\n" << eLog << std::endl;
            CoreLog(eLog);

            // 1. Kích hoạt Iframe Dodge Roll (Spacebar) để né đòn kết liễu
            TriggerDodgeRoll(kmbox, nowMs, "EMERGENCY ES CRITICAL [CI]");

            // 2. Mở Town Portal (Phím 'T') rút lui an toàn về làng
            SendKey(m_config.portalVk, m_config.portalHid, kmbox, 40);

            return true;
        }
        return false;
    }

    const float hpRatio = static_cast<float>(player.currentHP) / static_cast<float>(player.maxHP);
    if (hpRatio <= m_config.emergencyHpThreshold && player.currentHP > 0) {
        m_state.emergencyTriggered = true;
        m_state.pendingDodge = false; // Hủy lệnh hoãn né để dồn né ngay lập tức
        const std::string eLog = "[EMERGENCY] HP NGUY CẤP (" + std::to_string(static_cast<int>(hpRatio * 100.0f))
            + "%) <= " + std::to_string(static_cast<int>(m_config.emergencyHpThreshold * 100.0f))
            + "%! Kích hoạt mở Portal thoát hiểm khẩn cấp & dồn ngụm máu sinh tồn!";
        std::cout << "\n" << eLog << std::endl;
        CoreLog(eLog);

        // 1. Bơm ngay lập tức bình máu số [1]
        SendKey(m_config.lifeFlaskVk, m_config.lifeFlaskHid, kmbox, 40);

        // 2. Kích hoạt Iframe Dodge Roll (Spacebar) để né đòn kết liễu
        TriggerDodgeRoll(kmbox, nowMs, "EMERGENCY HP CRITICAL");

        // 3. Mở Town Portal (Phím 'T') rút lui an toàn về làng
        SendKey(m_config.portalVk, m_config.portalHid, kmbox, 40);

        return true;
    }
    return false;
}

bool ReflexManager::HandleIframeDodge(const PlayerTelemetryData& player, const TelemetryPacket& packet, KMBoxNet& kmbox, uint64_t nowMs) {
    if (!m_config.dodgeEnabled) return false;

    if (m_state.isEvadingTelegraph && (nowMs - m_state.lastTelegraphDodgeMs >= 400)) {
        m_state.isEvadingTelegraph = false;
    }

    // 0. Phản xạ Né Telegraph / Kỹ năng Tích tụ Cực Đại của Boss (CHU KỲ 5 SSoT)
    if (m_config.telegraphEvasionEnabled && (nowMs - m_state.lastTelegraphDodgeMs >= m_config.telegraphDodgeCooldownMs)) {
        const uint32_t maxEntitiesToScan = (packet.entityCount < 32u) ? packet.entityCount : 32u;
        for (uint32_t i = 0; i < maxEntitiesToScan; ++i) {
            const auto& ent = packet.entities[i];
            if (ent.type == 1 && !(ent.extraFlags & 4) && ent.currentHP > 0) { // Monster còn sống
                const bool isBossOrRare = (ent.rarity >= 2) || (ent.extraFlags & 1);
                if (!isBossOrRare) continue;

                const bool isTelegraph = (ent.extraFlags & (1 << 6)) ||
                    (ent.currentAnimationId >= m_config.telegraphAnimMin && ent.currentAnimationId <= m_config.telegraphAnimMax && ent.currentAnimationId != 0);

                if (!isTelegraph) continue;

                const float dx = player.posX - ent.posX;
                const float dy = player.posY - ent.posY;
                const float dist = (player.posX != 0.0f || player.posY != 0.0f) ? std::sqrt(dx * dx + dy * dy) : ent.distanceToPlayer;

                if (dist <= m_config.telegraphDangerRadius && dist > 0.1f) {
                    // Vector tiếp tuyến thoát hiểm ngang: (-dy, dx)
                    float tanX = -dy / dist;
                    float tanY =  dx / dist;

                    m_state.lastTelegraphTangentX = tanX;
                    m_state.lastTelegraphTangentY = tanY;
                    m_state.isEvadingTelegraph = true;
                    m_state.lastTelegraphDodgeMs = nowMs;
                    ++m_state.telegraphDodgeCount;

                    // Kích hoạt ngay lập tức cú lướt né Iframe Roll (Spacebar) - 0ms delay!
                    const std::string reason = "BOSS TELEGRAPH EVASION (" + std::string(ent.name[0] ? ent.name : "Boss") +
                        " Anim:" + std::to_string(ent.currentAnimationId) + " Dist:" + std::to_string(static_cast<int>(dist)) + ")";
                    TriggerDodgeRoll(kmbox, nowMs, reason);
                    return true;
                }
            }
        }
    }

    // 1. Xử lý né đòn đang chờ khi đã hết độ trễ phản xạ sinh lý tự nhiên (Human Reaction Latency)
    if (m_state.pendingDodge) {
        if (nowMs >= m_state.dodgeScheduledMs) {
            TriggerDodgeRoll(kmbox, nowMs, m_state.pendingDodgeReason);
            m_state.pendingDodge = false;
            m_state.pendingDodgeReason.clear();
            return true;
        }
        return false; // Vẫn đang trong thời gian phản xạ tự nhiên của não bộ (100ms - 150ms)
    }

    // Cooldown giữa các lần lăn né
    if (nowMs - m_state.lastDodgeMs < m_config.dodgeCooldownMs) {
        return false;
    }

    if (player.maxHP == 1) {
        // Chaos Inoculation:
        // 1. Kiểm tra ngưỡng tụt ES dưới 75% -> Bắt buộc né
        const float esRatio = (player.maxES > 0) ? (static_cast<float>(player.currentES) / static_cast<float>(player.maxES)) : 1.0f;
        if (m_config.ciEvasionEnabled && esRatio < m_config.ciDodgeEsThreshold) {
            const std::string reason = "CI LOW ES (< 75%: " + std::to_string(static_cast<int>(esRatio * 100.0f)) + "%)";
            TriggerDodgeRoll(kmbox, nowMs, reason);
            return true;
        }

        // 2. Tính burst damage delta từ previousES - currentES so với maxES
        if (player.maxES > 0 && m_state.previousES > 0 && player.currentES < m_state.previousES) {
            const uint32_t damageTaken = m_state.previousES - player.currentES;
            const float burstRatio = static_cast<float>(damageTaken) / static_cast<float>(player.maxES);

            if (burstRatio >= m_config.burstDamageRatio) {
                const std::string reason = "BURST DAMAGE (-" + std::to_string(damageTaken)
                    + " ES, " + std::to_string(static_cast<int>(burstRatio * 100.0f)) + "% maxES) [CI]";

                if (!m_config.humanReactionEnabled || m_config.minReactionLatencyMs == 0) {
                    TriggerDodgeRoll(kmbox, nowMs, reason);
                    return true;
                }

                const uint32_t latency = GenerateReactionLatencyMs();
                m_state.pendingDodge = true;
                m_state.dodgeScheduledMs = nowMs + latency;
                m_state.pendingDodgeReason = reason + " [Reaction Latency: " + std::to_string(latency) + "ms]";
                return false;
            }
        }
    } else {
        if (m_state.previousHP > 0 && player.currentHP < m_state.previousHP) {
            const uint32_t damageTaken = m_state.previousHP - player.currentHP;
            const float burstRatio = static_cast<float>(damageTaken) / static_cast<float>(player.maxHP);

            // Nếu nhận burst damage lớn hơn hoặc bằng ngưỡng quy định (mặc định 22% maxHP trong tick)
            if (burstRatio >= m_config.burstDamageRatio) {
                const std::string reason = "BURST DAMAGE (-" + std::to_string(damageTaken)
                    + " HP, " + std::to_string(static_cast<int>(burstRatio * 100.0f)) + "% maxHP)";

                // Nếu không bật độ trễ phản xạ hoặc minReactionLatencyMs == 0 (cho unit test hoặc tức thì)
                if (!m_config.humanReactionEnabled || m_config.minReactionLatencyMs == 0) {
                    TriggerDodgeRoll(kmbox, nowMs, reason);
                    return true;
                }

                // Sinh độ trễ phản xạ tự nhiên (100ms - 150ms Gauss Jitter)
                const uint32_t latency = GenerateReactionLatencyMs();
                m_state.pendingDodge = true;
                m_state.dodgeScheduledMs = nowMs + latency;
                m_state.pendingDodgeReason = reason + " [Reaction Latency: " + std::to_string(latency) + "ms]";
                return false;
            }
        }
    }

    return false;
}

void ReflexManager::TriggerDodgeRoll(KMBoxNet& kmbox, uint64_t nowMs, const std::string& reason) {
    m_consumedExclusiveInput = true;
    ++m_state.dodgeCount;
    m_state.lastDodgeMs = nowMs;
    if (m_gameSession) {
        m_gameSession->NotifyRoll(nowMs);
    }

    // Thời gian giữ phím Spacebar phân phối chuẩn Gauss 35ms - 65ms
    uint32_t dwellMs = GenerateGaussianDwellTime(48.0f, 7.0f, 35.0f, 65.0f);

    // Lăn né Spacebar theo hướng di chuyển tự nhiên (tránh ép phím 'S' lùi đâm vào đá/tường)
    if (m_actuator != nullptr) {
        m_actuator->KeyPressVk(m_config.dodgeVk, dwellMs, nowMs);
    } else if (kmbox.IsEmulationMode()) {
        keybd_event(m_config.dodgeVk, 0, 0, 0); // Space down
        std::this_thread::sleep_for(std::chrono::milliseconds(dwellMs));
        keybd_event(m_config.dodgeVk, 0, KEYEVENTF_KEYUP, 0);
    } else {
        kmbox.KeyPress(m_config.dodgeHid, dwellMs);
    }

    const std::string logMsg = "[IframeDodge #" + std::to_string(m_state.dodgeCount)
        + "] Kich hoat 300ms bat tu (Space) do: " + reason;
    std::cout << logMsg << std::endl;
    CoreLog(logMsg);
}

void ReflexManager::TriggerLifeFlask(KMBoxNet& kmbox, uint64_t nowMs, const std::string& reason) {
    if (!m_config.flaskEnabled) return;
    m_consumedExclusiveInput = true;
    SendKey(m_config.lifeFlaskVk, m_config.lifeFlaskHid, kmbox, 30, nowMs);
    m_state.lastLifeMs = nowMs;
    ++m_state.lifeUses;
    m_state.isLifeHoT = true;
    m_state.lifeHoTStartMs = nowMs;
    const std::string logMsg = "[SmartFlask] Uong Life Flask [1] #" + std::to_string(m_state.lifeUses)
        + " (" + reason + ")";
    std::cout << logMsg << std::endl;
    CoreLog(logMsg);
}

void ReflexManager::HandleLifeFlask(const PlayerTelemetryData& player, KMBoxNet& kmbox, uint64_t nowMs) {
    if (!m_config.flaskEnabled) return;
    if (player.maxHP == 1) return; // Chaos Inoculation: Không uống Life Flask

    const float hpRatio = static_cast<float>(player.currentHP) / static_cast<float>(player.maxHP);

    // Kiểm tra trạng thái hồi máu theo thời gian (HoT)
    if (m_state.isLifeHoT) {
        if (nowMs - m_state.lifeHoTStartMs <= 3000) { // Bình máu POE2 hồi trong ~3 giây
            if (player.currentHP > m_state.lifePressHP) {
                // Máu đang tăng dần lên -> HoT đang phát huy tác dụng tốt
                m_state.lifePressHP = player.currentHP;
            } else if (player.currentHP < m_state.lifePressHP || hpRatio <= m_config.criticalHpThreshold) {
                // Sát thương dồn dập hoặc máu tụt nguy hiểm -> hủy HoT ngay để cho phép bấm tiếp ngụm 2!
                m_state.isLifeHoT = false;
            }
        } else {
            // Hết thời gian hiệu lực của ngụm trước
            m_state.isLifeHoT = false;
        }
    }

    // Logic uống bình máu:
    // 1. HP tụt xuống dưới ngưỡng cấu hình (mặc định <= 55%)
    // 2. Không bị chồng chéo ngụm uống trước nếu HoT vẫn đang hồi phục ổn định
    // 3. Đảm bảo cooldown tối thiểu giữa các lần bấm (mặc định 650ms)
    if (hpRatio <= m_config.hpThreshold && (!m_state.isLifeHoT || hpRatio <= m_config.criticalHpThreshold)) {
        if (nowMs - m_state.lastLifeMs >= m_config.lifeCooldownMs) {
            TriggerLifeFlask(kmbox, nowMs, "HP: " + std::to_string(player.currentHP) + "/" + std::to_string(player.maxHP)
                + " (" + std::to_string(static_cast<int>(hpRatio * 100.0f)) + "%)"
                + (hpRatio <= m_config.criticalHpThreshold ? " [CRITICAL BURST OVERRIDE]" : " [SMART HoT]"));
            m_state.lifePressHP = player.currentHP;
            m_state.isLifeHoT = true;
            m_state.lifeHoTStartMs = nowMs;
        }
    }
}

void ReflexManager::TriggerManaFlask(KMBoxNet& kmbox, uint64_t nowMs, const std::string& reason) {
    if (!m_config.manaEnabled) return;
    m_consumedExclusiveInput = true;
    SendKey(m_config.manaFlaskVk, m_config.manaFlaskHid, kmbox, 30, nowMs);
    m_state.lastManaMs = nowMs;
    ++m_state.manaUses;
    m_state.awaitManaEffect = true;
    m_state.manaPressMs = nowMs;
    const std::string logMsg = "[SmartMana] Uong Mana Flask [2] #" + std::to_string(m_state.manaUses)
        + " (" + reason + ")";
    std::cout << logMsg << std::endl;
    CoreLog(logMsg);
}

void ReflexManager::HandleManaFlask(const PlayerTelemetryData& player, KMBoxNet& kmbox, uint64_t nowMs) {
    if (!m_config.manaEnabled || m_state.manaEmpty || player.maxMana == 0) return;

    const float manaRatio = static_cast<float>(player.currentMana) / static_cast<float>(player.maxMana);
    const float threshold = (player.maxHP == 1) ? m_config.ciManaFlaskThreshold : m_config.manaThreshold;

    // Kiểm tra hiệu lực của lần bấm trước sau 600ms (phát hiện cạn sạc bình)
    if (m_state.awaitManaEffect && nowMs - m_state.manaPressMs >= 600) {
        m_state.awaitManaEffect = false;
        if (player.currentMana <= m_state.preManaValue) {
            ++m_state.manaNoEffectCount;
            if (m_state.manaNoEffectCount >= 3) {
                m_state.manaEmpty = true;
                const std::string eLog = "[SmartMana] Binh Mana khong hoi sau 3 lan bam (Het Charge) -> Tam dung tranh spam!";
                std::cout << eLog << std::endl;
                CoreLog(eLog);
            }
        } else {
            m_state.manaNoEffectCount = 0;
        }
    }

    if (manaRatio <= threshold && (nowMs - m_state.lastManaMs >= m_config.manaCooldownMs)) {
        TriggerManaFlask(kmbox, nowMs, "Mana: " + std::to_string(player.currentMana) + "/" + std::to_string(player.maxMana)
            + " (" + std::to_string(static_cast<int>(manaRatio * 100.0f)) + "%)" + ((player.maxHP == 1) ? " [CI Optimization]" : ""));
        m_state.preManaValue = player.currentMana;
    }
}

void ReflexManager::TriggerKiting(float dirX, float dirY, KMBoxNet& kmbox, uint64_t nowMs) {
    if (m_config.moveMode == MovementMode::MOUSE) {
        const float len = std::sqrt(dirX * dirX + dirY * dirY);
        if (len > 0.01f) {
            int screenW = 0;
            int screenH = 0;
            common::GetGameResolution(screenW, screenH);

            const float dist = 240.0f;
            const auto target = common::WorldToScreenIsometric(dirX, dirY, dist, static_cast<float>(screenW), static_cast<float>(screenH), 50.0f);

            if (!EnsurePoe2WindowFocus()) return;
            m_consumedExclusiveInput = true;
            kmbox.MoveMouseSmooth(static_cast<int>(target.x), static_cast<int>(target.y), m_curveGen, 10, 1);
            kmbox.ClickMouseHumanized(1, m_curveGen, 45.0f, 8.0f);
            m_state.lastKiteMs = nowMs;
            return;
        }
    }

    const auto wasd = common::WorldToWasd(dirX, dirY);
    if (wasd.empty()) return;

    if (!EnsurePoe2WindowFocus()) return;

    std::vector<uint8_t> moveKeys;
    if (wasd.up) moveKeys.push_back(0x57);    // 'W'
    if (wasd.down) moveKeys.push_back(0x53);  // 'S'
    if (wasd.left) moveKeys.push_back(0x41);  // 'A'
    if (wasd.right) moveKeys.push_back(0x44); // 'D'

    if (moveKeys.empty()) return;

    m_consumedExclusiveInput = true;

    // Chế độ phi khóa NonBlockingActuator (Phase P7 SSoT: 0ms sleep, dispatch <0.005ms)
    if (m_actuator != nullptr) {
        for (uint8_t vk : moveKeys) {
            m_actuator->KeyPressVk(vk, 120, nowMs);
        }
    } else {
        for (uint8_t vk : moveKeys) {
            kmbox.KeyPressVk(vk, 120);
        }
    }
    m_state.lastKiteMs = nowMs;
}

void ReflexManager::HandleKiting(const PlayerTelemetryData& player, const TelemetryPacket& packet, KMBoxNet& kmbox, uint64_t nowMs) {
    if (nowMs - m_state.lastKiteMs < m_config.kitingIntervalMs) return;

    // Tìm quái vật gần người chơi nhất từ danh sách thực thể
    float minDistance = 9999.0f;
    float nearestMonsterX = 0;
    float nearestMonsterY = 0;
    bool foundMonster = false;

    for (uint32_t i = 0; i < packet.entityCount; ++i) {
        const auto& ent = packet.entities[i];
        if (ent.type == 1 && !(ent.extraFlags & 4)) { // Monster và chưa chết
            const float dx = ent.posX - player.posX;
            const float dy = ent.posY - player.posY;
            const float dist = std::sqrt(dx * dx + dy * dy);
            if (dist < minDistance) {
                minDistance = dist;
                nearestMonsterX = ent.posX;
                nearestMonsterY = ent.posY;
                foundMonster = true;
            }
        }
    }

    // Nếu có quái áp sát trong vòng an toàn (ví dụ < 35 đơn vị), hoặc đang trong CI Recovery Mode, hoặc vừa dính đòn burst mà radar mù (Blind Kite)
    bool isBlindKite = false;
    float dirX = 0, dirY = 0;

    if (foundMonster && (minDistance < m_config.safeDistance || m_state.isCiRecoveryMode)) {
        dirX = player.posX - nearestMonsterX;
        dirY = player.posY - nearestMonsterY;
    } else if (!foundMonster && (nowMs - m_state.lastDodgeMs < 1500)) {
        // Blind Emergency Evasive Kite: Vừa dính sát thương bùng nổ, radar tạm thời chưa bắt được quái
        // Lùi khẩn cấp về phía sau (hướng Tây Nam - vector [-1, -1]) để kéo giãn cự ly
        isBlindKite = true;
        dirX = -1.0f;
        dirY = -1.0f;
    }

    if (foundMonster || isBlindKite) {
        TriggerKiting(dirX, dirY, kmbox, nowMs);
    }
}

bool ReflexManager::HandleAutoRespawn(const PlayerTelemetryData& player, KMBoxNet& kmbox, uint64_t nowMs) {
    if (!m_config.autoRespawn) return false;
    if (!EnsurePoe2WindowFocus()) return false;

    if (m_state.deathTimestampMs == 0) {
        m_state.deathTimestampMs = nowMs;
        const std::string msg = "[PlayerDeath] >> NHÂN VẬT ĐÃ TỬ TRẬN (HP=0/" + std::to_string(player.maxHP)
                              + ")! Đang chờ nút 'RESURRECT AT CHECKPOINT'... <<";
        std::cout << "\n" << msg << std::endl;
        CoreLog(msg);
    }

    // Chờ respawnDelayMs (2.2s) để animation tử trận và nút xuất hiện hoàn toàn
    if (nowMs - m_state.deathTimestampMs >= m_config.respawnDelayMs) {
        if (nowMs - m_state.lastRespawnClickMs >= 2000) {
            m_state.lastRespawnClickMs = nowMs;
            ++m_state.totalRespawns;

            int screenW = 0;
            int screenH = 0;
            common::GetGameResolution(screenW, screenH);

            // INV-UI-RESPAWN (RCA Doc 47):
            // Nút 'RESURRECT AT CHECKPOINT' nằm giữa màn hình theo chiều ngang (X = 50%)
            // và nằm tại Y = 73.6111% (Y = 1060 trên 1440p, Y = 795 trên 1080p, Y = 1590 trên 4K UHD).
            // Tránh lỗi cũ 0.5104f (Y=735) click trượt 325px lên trên tiêu đề 'YOU HAVE DIED'.
            const int btnX = screenW / 2;
            const int btnY = static_cast<int>(std::round(screenH * 0.736111f));

            const std::string clickMsg = "[AutoRespawn #" + std::to_string(m_state.totalRespawns)
                                       + "] >> BẤM NÚT 'RESURRECT AT CHECKPOINT' TẠI ("
                                       + std::to_string(btnX) + ", " + std::to_string(btnY) + ")! <<";
            std::cout << clickMsg << std::endl;
            CoreLog(clickMsg);

            if (!EnsurePoe2WindowFocus()) return false;
            m_consumedExclusiveInput = true;
            kmbox.MoveMouseSmooth(btnX, btnY, m_curveGen, 20, 2);
            kmbox.ClickMouseHumanized(1, m_curveGen, 65.0f, 12.0f);
            return true;
        }
    }
    return false;
}
