#include "brain/unstuck_handler.hpp"
#include "brain/character_fsm.hpp"
#include "common/coordinate_transform.hpp"
#include "common/window_utils.hpp"

#include <iostream>
#include <thread>
#include <chrono>
#include <cmath>
#include "common/logger.hpp"

UnstuckHandler::UnstuckHandler(const UnstuckConfig& config)
    : m_config(config) {}

void UnstuckHandler::RecordAction(BotActionKind kind, uint32_t entityId, uint64_t nowMs, bool isTown) {
    (void)nowMs;
    m_isTown = isTown;
    if (kind == BotActionKind::None || kind == BotActionKind::Idle || kind == BotActionKind::Unstuck) {
        return;
    }

    // BẤT BIẾN INV-UNSTUCK-EXPLORE-EXEMPTION:
    // Khi entityId == 0 (hành động không có mục tiêu thực thể cụ thể, ví dụ tuần tra Explore hoặc di chuyển tự do),
    // tuyệt đối KHÔNG tính vào RepetitiveAction! Việc kẹt khi di chuyển đã được đảm nhiệm 100% bởi MovementStuck (10s không đổi vị trí).
    if (entityId == 0) {
        m_consecutiveActionCount = 0;
        m_lastActionKind = kind;
        m_lastActionEntityId = entityId;
        return;
    }

    if (kind == m_lastActionKind && entityId == m_lastActionEntityId) {
        m_consecutiveActionCount++;
        const uint32_t limit = isTown ? m_config.maxRepetitiveTown : m_config.maxRepetitiveMap;
        if (m_consecutiveActionCount >= limit && m_reason == UnstuckReason::None) {
            m_reason = UnstuckReason::RepetitiveAction;
            CoreLog("[UnstuckHandler] Triggered: Repetitive action limit reached (" +
                    std::to_string(m_consecutiveActionCount) + " >= " + std::to_string(limit) +
                    ") for kind=" + BotActionKindToString(kind) + " entity=" + std::to_string(entityId));
        }
    } else {
        m_lastActionKind = kind;
        m_lastActionEntityId = entityId;
        m_consecutiveActionCount = 1;
    }
}

void UnstuckHandler::RecordPickupFailure(uint32_t itemId) {
    m_pickupFailCounts[itemId]++;
    if (m_pickupFailCounts[itemId] >= m_config.maxPickupFails) {
        m_blacklistedItemIds.insert(itemId);
        CoreLog("[UnstuckHandler] Item #" + std::to_string(itemId) + " blacklisted after " +
                std::to_string(m_config.maxPickupFails) + " consecutive failed pickup attempts.");
        if (m_reason == UnstuckReason::None) {
            m_reason = UnstuckReason::PickupFailure;
        }
    }
}

void UnstuckHandler::RecordPickupSuccess(uint32_t itemId) {
    m_pickupFailCounts.erase(itemId);
    m_blacklistedItemIds.erase(itemId);
}

bool UnstuckHandler::IsItemBlacklisted(uint32_t itemId) const {
    return m_blacklistedItemIds.find(itemId) != m_blacklistedItemIds.end();
}

void UnstuckHandler::ManualTriggerStuck(UnstuckReason reason, uint64_t nowMs) {
    (void)nowMs;
    m_reason = reason;
}

void UnstuckHandler::Reset() {
    m_reason = UnstuckReason::None;
    m_recoveryAttempts = 0;
    m_lastRecoveryMs = 0;
    m_stuckOriginX = 0.0f;
    m_stuckOriginY = 0.0f;
    m_stuckOriginRecorded = false;
    m_consecutiveActionCount = 0;
    m_lastMovementCheckMs = 0;
    m_initialPosRecorded = false;
    m_heldWasdHids.clear();
    m_wasdHoldUntilMs = 0;
}

#include "common/zone_classifier.hpp"

ActionProposal UnstuckHandler::Propose(const TelemetryPacket& packet, uint64_t nowMs) {
    if (packet.player.maxHP > 0 && packet.player.currentHP == 0) {
        return {};
    }

    // INV-FSM-UNSTUCK-NO-TOWN-ROLL: Cấm kích hoạt kẹt chuyển động hoặc dodge roll trong Town/Hideout Idle
    const bool isSafeTown = m_isTown || common::IsTownOrHideout(common::ClassifyZone(packet.area)) ||
                            (m_fsm != nullptr && m_fsm->IsTownOrHideoutIdle());
    if (isSafeTown) {
        return {};
    }

    // 1. Kiểm tra kẹt chuyển động (Movement Check)
    // Trong Combat Map, nếu nhân vật không đổi vị trí > failMovementCheckIntervalMs (10s),
    // kể cả khi đang Explore, Combat, Pickup hay bị kẹt vào Idle do deadlock FSM,
    // đều phải phát hiện và kích hoạt Unstuck để giải phóng!
    if (!m_initialPosRecorded) {
        m_lastCheckX = packet.player.posX;
        m_lastCheckY = packet.player.posY;
        m_lastCheckZ = packet.player.posZ;
        m_lastMovementCheckMs = nowMs;
        m_initialPosRecorded = true;
    } else if (nowMs - m_lastMovementCheckMs >= m_config.failMovementCheckIntervalMs) {
        const float dx = packet.player.posX - m_lastCheckX;
        const float dy = packet.player.posY - m_lastCheckY;
        const float dist = std::sqrt(dx * dx + dy * dy);

        if (dist < m_config.minMovementDelta) {
            if (m_reason == UnstuckReason::None) {
                m_reason = UnstuckReason::MovementStuck;
                m_stuckOriginX = packet.player.posX;
                m_stuckOriginY = packet.player.posY;
                m_stuckOriginRecorded = true;
                CoreLog("[UnstuckHandler] Triggered: Player position static in Combat Map for " +
                        std::to_string(nowMs - m_lastMovementCheckMs) + "ms (dist=" +
                        std::to_string(dist) + "u < " + std::to_string(m_config.minMovementDelta) + "u)");
            }
        } else {
            m_lastCheckX = packet.player.posX;
            m_lastCheckY = packet.player.posY;
            m_lastCheckZ = packet.player.posZ;
            m_lastMovementCheckMs = nowMs;
        }
    }

    // 2. Nếu đang kẹt, kiểm tra xem nhân vật đã tự thoát ra chưa (di chuyển > 15u)
    if (m_reason != UnstuckReason::None) {
        if (!m_stuckOriginRecorded) {
            m_stuckOriginX = packet.player.posX;
            m_stuckOriginY = packet.player.posY;
            m_stuckOriginRecorded = true;
        } else {
            const float dx = packet.player.posX - m_stuckOriginX;
            const float dy = packet.player.posY - m_stuckOriginY;
            const float moveAway = std::sqrt(dx * dx + dy * dy);
            if (moveAway >= 15.0f) {
                CoreLog("[UnstuckHandler] Resolved: Character moved " + std::to_string(moveAway) + "u away from stuck origin.");
                Reset();
                return {};
            }
        }

        ActionProposal p{};
        p.kind = BotActionKind::Unstuck;
        p.priority = bot_priority::UNSTUCK; // 8000 (Doc 29 mục 4)
        p.priorityName = "UNSTUCK";
        return p;
    }

    return {};
}

bool UnstuckHandler::Execute(const ActionProposal& chosen, const TelemetryPacket& packet, KMBoxNet& kmbox, uint64_t nowMs) {
    if (chosen.kind != BotActionKind::Unstuck) return false;
    if (!EnsurePoe2WindowFocus()) return false;

    if (!m_heldWasdHids.empty()) {
        if (nowMs < m_wasdHoldUntilMs) {
            return true;
        }
        ReleaseHeldWasd(kmbox);
        m_lastRecoveryMs = nowMs;
        return true;
    }

    if (m_stuckOriginX == 0.0f && m_stuckOriginY == 0.0f) {
        m_stuckOriginX = packet.player.posX;
        m_stuckOriginY = packet.player.posY;
    }

    if (m_recoveryAttempts < m_config.maxRecoveryAttempts) {
        if (m_recoveryAttempts == 1) {
            kmbox.KeyPress(0x2C, 40); // Space dodge (HID)
            CoreLog("[UnstuckHandler] Recovery #" + std::to_string(m_recoveryAttempts + 1) + ": Performed Iframe Dodge Roll (Space).");
            m_recoveryAttempts++;
            m_lastRecoveryMs = nowMs;
            return true;
        }

        const float angle = static_cast<float>(m_escapeStep) * 1.5707963f;
        m_escapeStep = (m_escapeStep + 1) % 4;
        const float escapeDx = std::cos(angle) * m_config.unstuckRadius;
        const float escapeDy = std::sin(angle) * m_config.unstuckRadius;
        const auto wasd = common::WorldToWasd(escapeDx, escapeDy);
        if (wasd.up) m_heldWasdHids.push_back(0x1A);
        if (wasd.down) m_heldWasdHids.push_back(0x16);
        if (wasd.left) m_heldWasdHids.push_back(0x04);
        if (wasd.right) m_heldWasdHids.push_back(0x07);
        if (m_heldWasdHids.empty()) {
            m_heldWasdHids.push_back(0x1A); // deadzone: vẫn hold W để nhịp 350ms không nhảy dodge sớm
        }
        for (uint8_t hid : m_heldWasdHids) {
            kmbox.KeyDown(hid);
        }
        m_wasdHoldUntilMs = nowMs + 350;
        m_recoveryAttempts++;
        CoreLog("[UnstuckHandler] Recovery #" + std::to_string(m_recoveryAttempts) + ": Radial WASD hold toward angle=" + std::to_string(angle));
        m_lastRecoveryMs = nowMs;
        return true;
    }

    // Invariant: Nếu đang ở Town hoặc Hideout (hoặc FSM đang IsTownOrHideoutIdle)
    // -> TUYỆT ĐỐI CẤM mở Portal khẩn cấp (vì đã ở nhà rồi, hoặc portal key vô nghĩa/phí scroll).
    if (m_isTown || (m_fsm != nullptr && m_fsm->IsTownOrHideoutIdle())) {
        CoreLog("[UnstuckHandler] Recovery attempts exhausted (" + std::to_string(m_recoveryAttempts) + ") in Town/Hideout. CẤM mở Town Portal tại đây. Resetting handler.");
        ReleaseHeldWasd(kmbox);
        Reset();
        return false;
    }

    CoreLog("[UnstuckHandler] Recovery attempts exhausted (" + std::to_string(m_recoveryAttempts) + "). Triggering Emergency Town Portal ('.')!");
    ReleaseHeldWasd(kmbox);
    kmbox.KeyPress(m_config.portalHid, 50);
    Reset();
    return true;
}

void UnstuckHandler::ReleaseHeldWasd(KMBoxNet& kmbox) {
    for (uint8_t hid : m_heldWasdHids) {
        kmbox.KeyUp(hid);
    }
    m_heldWasdHids.clear();
    m_wasdHoldUntilMs = 0;
}
