#include "brain/area_events.hpp"

#include <cmath>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <thread>
#include <chrono>

#include "common/coordinate_transform.hpp"
#include "common/math2d.hpp"
#include "common/logger.hpp"
#include "common/window_utils.hpp"

AreaEventManager::AreaEventManager(const AreaEventConfig& config)
    : m_config(config) {}

void AreaEventManager::OnAreaChanged(uint32_t newAreaSeed) {
    if (newAreaSeed != m_currentAreaSeed) {
        m_events.clear();
        m_currentAreaSeed = newAreaSeed;
        CoreLog("[AreaEventManager] Reset bộ nhớ sự kiện cho AreaSeed mới: " + std::to_string(newAreaSeed));
    }
}

void AreaEventManager::UpdateCandidates(const TelemetryPacket& packet, uint64_t nowMs) {
    for (uint32_t i = 0; i < packet.entityCount; ++i) {
        const auto& ent = packet.entities[i];
        
        // Nhận diện các đối tượng sự kiện khu vực (Chest, Shrine, Essence, Breach, Ritual, Strongbox, Delirium)
        bool isEventCandidate = (ent.type >= game_layout::EntityTypeChest && ent.type <= game_layout::EntityTypeDelirium);
        if (!isEventCandidate && (ent.extraFlags & game_layout::kEntityFlagInteractable) &&
            ent.type != game_layout::EntityTypeMonster &&
            ent.type != game_layout::EntityTypeItemDrop) {
            isEventCandidate = true;
        }
        if (!isEventCandidate) continue;

        // Nếu thực thể đã chết hoặc đã biến mất/hoàn thành
        if (ent.extraFlags & game_layout::kEntityFlagDead) {
            MarkCompleted(ent.id);
            continue;
        }

        // Cập nhật hoặc thêm mới vào m_events
        auto it = std::find_if(m_events.begin(), m_events.end(), [id = ent.id](const AreaEventRecord& r) {
            return r.entityId == id;
        });

        if (it != m_events.end()) {
            it->x = ent.posX;
            it->y = ent.posY;
            it->z = ent.posZ;
            it->lastSeenMs = nowMs;

            // Kiểm tra timeout nếu đang ở trạng thái Committed
            if (it->status == AreaEventStatus::Committed && it->commitTimeMs > 0) {
                if (nowMs - it->commitTimeMs > m_config.timeoutMs) {
                    ++it->failCount;
                    it->status = (it->failCount >= m_config.maxFails) ? AreaEventStatus::Failed : AreaEventStatus::Candidate;
                    CoreLog("[AreaEventManager] Event id=" + std::to_string(it->entityId) + " timed out (" +
                            std::to_string(m_config.timeoutMs) + "ms) -> failCount=" + std::to_string(it->failCount));
                }
            }
        } else {
            AreaEventRecord rec{};
            rec.entityId = ent.id;
            rec.type = ent.type;
            rec.x = ent.posX;
            rec.y = ent.posY;
            rec.z = ent.posZ;
            rec.status = AreaEventStatus::Candidate;
            rec.lastSeenMs = nowMs;
            rec.commitTimeMs = 0;
            rec.failCount = 0;
            std::memcpy(rec.name, ent.name, sizeof(rec.name));
            m_events.push_back(rec);
        }
    }
}

bool AreaEventManager::HasCommittedEvent() const {
    for (const auto& ev : m_events) {
        if (ev.status == AreaEventStatus::Committed) {
            return true;
        }
    }
    return false;
}

AreaEventRecord* AreaEventManager::GetCommittedEvent() {
    for (auto& ev : m_events) {
        if (ev.status == AreaEventStatus::Committed) {
            return &ev;
        }
    }
    return nullptr;
}

const AreaEventRecord* AreaEventManager::GetCommittedEvent() const {
    for (const auto& ev : m_events) {
        if (ev.status == AreaEventStatus::Committed) {
            return &ev;
        }
    }
    return nullptr;
}

bool AreaEventManager::CommitEvent(uint32_t entityId, uint64_t nowMs) {
    for (auto& ev : m_events) {
        if (ev.entityId == entityId) {
            if (ev.status == AreaEventStatus::Failed) return false;
            ev.status = AreaEventStatus::Committed;
            ev.commitTimeMs = nowMs;
            return true;
        }
    }
    return false;
}

void AreaEventManager::MarkCompleted(uint32_t entityId) {
    for (auto& ev : m_events) {
        if (ev.entityId == entityId) {
            ev.status = AreaEventStatus::Completed;
            break;
        }
    }
}

void AreaEventManager::MarkFailed(uint32_t entityId) {
    for (auto& ev : m_events) {
        if (ev.entityId == entityId) {
            ++ev.failCount;
            ev.status = (ev.failCount >= m_config.maxFails) ? AreaEventStatus::Failed : AreaEventStatus::Candidate;
            break;
        }
    }
}

void AreaEventManager::AddOrUpdateEvent(const AreaEventRecord& record) {
    for (auto& ev : m_events) {
        if (ev.entityId == record.entityId) {
            ev = record;
            return;
        }
    }
    m_events.push_back(record);
}

AreaEventHandler::AreaEventHandler(AreaEventManager* mgr, const AreaEventConfig& config)
    : m_eventMgr(mgr), m_config(config) {}

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

    // 1. Nếu có Event đã Committed -> Đề xuất AREA_EVENT (6500)
    if (m_eventMgr && m_eventMgr->HasCommittedEvent()) {
        const auto* rec = m_eventMgr->GetCommittedEvent();
        if (rec) {
            const float dx = rec->x - player.posX;
            const float dy = rec->y - player.posY;
            const float dist = std::sqrt(dx * dx + dy * dy);

            ActionProposal p{};
            p.kind = BotActionKind::AreaEvent;
            p.priority = bot_priority::AREA_EVENT;
            p.priorityName = "AREA_EVENT";
            p.entityId = rec->entityId;
            p.targetX = rec->x;
            p.targetY = rec->y;
            p.targetZ = rec->z;
            p.distance = dist;
            return p;
        }
    }

    // 2. Nếu có Candidate Event trong tầm kích hoạt (<= eventEngageRange) -> Commit và đề xuất AREA_EVENT (6500)
    if (m_eventMgr) {
        float closestCandDist = 99999.0f;
        const AreaEventRecord* closestCand = nullptr;

        for (const auto& ev : m_eventMgr->GetEvents()) {
            if (ev.status == AreaEventStatus::Candidate) {
                // Loại 6-11: Shrine, Essence, Breach, Ritual, Strongbox, Delirium
                if (ev.type >= game_layout::EntityTypeShrine && ev.type <= game_layout::EntityTypeDelirium) {
                    const float dx = ev.x - player.posX;
                    const float dy = ev.y - player.posY;
                    const float dist = std::sqrt(dx * dx + dy * dy);
                    if (dist <= m_config.eventEngageRange && dist < closestCandDist) {
                        closestCandDist = dist;
                        closestCand = &ev;
                    }
                }
            }
        }

        if (closestCand) {
            m_eventMgr->CommitEvent(closestCand->entityId, nowMs);
            ActionProposal p{};
            p.kind = BotActionKind::AreaEvent;
            p.priority = bot_priority::AREA_EVENT;
            p.priorityName = "AREA_EVENT";
            p.entityId = closestCand->entityId;
            p.targetX = closestCand->x;
            p.targetY = closestCand->y;
            p.targetZ = closestCand->z;
            p.distance = closestCandDist;
            return p;
        }
    }

    // 3. Tìm Chest / Strongbox trong cự ly chestRange (<= 60.0f)
    float bestChestDist = 99999.0f;
    int bestChestIdx = -1;

    for (uint32_t i = 0; i < packet.entityCount; ++i) {
        const auto& ent = packet.entities[i];
        if (ent.extraFlags & game_layout::kEntityFlagDead) continue;

        bool isChest = (ent.type == game_layout::EntityTypeChest || ent.type == game_layout::EntityTypeStrongbox);
        if (!isChest && (ent.extraFlags & game_layout::kEntityFlagInteractable)) {
            std::string n(ent.name);
            std::transform(n.begin(), n.end(), n.begin(), [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
            if (n.find("chest") != std::string::npos || n.find("strongbox") != std::string::npos || n.find("cache") != std::string::npos) {
                isChest = true;
            }
        }

        if (isChest) {
            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 <= m_config.chestRange && dist < bestChestDist) {
                bestChestDist = dist;
                bestChestIdx = static_cast<int>(i);
            }
        }
    }

    if (bestChestIdx != -1) {
        // ==========================================================
        // KHÓA CỨNG BẤT BIẾN INV-SAFE-01:
        // Quái vật sống trong safe_range (<= 50) -> TUYỆT ĐỐI KHÔNG MỞ RƯƠNG!
        // ==========================================================
        for (uint32_t i = 0; i < packet.entityCount; ++i) {
            const auto& ent = packet.entities[i];
            if (ent.type == game_layout::EntityTypeMonster && !(ent.extraFlags & game_layout::kEntityFlagDead) && ent.currentHP > 0) {
                const float mdx = ent.posX - player.posX;
                const float mdy = ent.posY - player.posY;
                const float mdist = std::sqrt(mdx * mdx + mdy * mdy);
                if (mdist <= m_config.safeRange) {
                    // Quái vật sống sát bên -> Không mở rương, nhường cho Combat / Reflex
                    return {};
                }
            }
        }

        const auto& chestEnt = packet.entities[bestChestIdx];
        ActionProposal p{};
        p.kind = BotActionKind::OpenChest;
        p.priority = bot_priority::OPEN_CHEST;
        p.priorityName = "OPEN_CHEST";
        p.entityId = chestEnt.id;
        p.targetX = chestEnt.posX;
        p.targetY = chestEnt.posY;
        p.targetZ = chestEnt.posZ;
        p.distance = bestChestDist;
        return p;
    }

    return {};
}

bool AreaEventHandler::Execute(const ActionProposal& chosen, const TelemetryPacket& packet, KMBoxNet& kmbox, uint64_t nowMs) {
    if (!EnsurePoe2WindowFocus()) return false;

    if (chosen.kind == BotActionKind::OpenChest) {
        const auto& player = packet.player;
        int screenW = (m_config.screenWidth > 0) ? m_config.screenWidth : GetSystemMetrics(SM_CXSCREEN);
        int screenH = (m_config.screenHeight > 0) ? m_config.screenHeight : GetSystemMetrics(SM_CYSCREEN);
        if (screenW <= 0) screenW = 2560;
        if (screenH <= 0) screenH = 1440;

        const float dx = chosen.targetX - player.posX;
        const float dy = chosen.targetY - player.posY;
        const float dist = std::sqrt(dx * dx + dy * dy);
        if (dist > m_config.interactRadius) {
            const auto wasd = common::WorldToWasd(dx, dy);
            std::vector<uint8_t> keys;
            if (wasd.up) keys.push_back(0x1A);
            if (wasd.down) keys.push_back(0x16);
            if (wasd.left) keys.push_back(0x04);
            if (wasd.right) keys.push_back(0x07);
            for (uint8_t hid : m_heldWasdHids) {
                bool keep = false;
                for (uint8_t n : keys) if (n == hid) { keep = true; break; }
                if (!keep) kmbox.KeyUp(hid);
            }
            for (uint8_t n : keys) {
                bool already = false;
                for (uint8_t hid : m_heldWasdHids) if (hid == n) { already = true; break; }
                if (!already) kmbox.KeyDown(n);
            }
            m_heldWasdHids = keys;
            return true;
        }

        ReleaseApproachKeys(kmbox);
        const float clickRad = (dist > 0.5f) ? (std::min)(350.0f, (std::max)(60.0f, dist * 12.0f)) : 0.0f;
        const auto target = common::WorldToScreenIsometric(dx, dy, clickRad, static_cast<float>(screenW), static_cast<float>(screenH), 50.0f);
        kmbox.MoveMouseSmooth(static_cast<int>(target.x), static_cast<int>(target.y), m_curveGen, 10, 1);
        int dwellMs = m_curveGen.GenerateDwellTimeMs(40.0f, 8.0f);
        kmbox.ClickMouse(1, dwellMs);
        if (m_eventMgr) {
            m_eventMgr->MarkCompleted(chosen.entityId);
        }
        m_lastInteractMs = nowMs;
        CoreLog("[AreaEventHandler] Mở rương / Strongbox id=" + std::to_string(chosen.entityId));
        return true;
    }

    if (chosen.kind == BotActionKind::AreaEvent) {
        const auto& player = packet.player;
        const float dx = chosen.targetX - player.posX;
        const float dy = chosen.targetY - player.posY;
        const float dist = std::sqrt(dx * dx + dy * dy);

        int screenW = (m_config.screenWidth > 0) ? m_config.screenWidth : GetSystemMetrics(SM_CXSCREEN);
        int screenH = (m_config.screenHeight > 0) ? m_config.screenHeight : GetSystemMetrics(SM_CYSCREEN);
        if (screenW <= 0) screenW = 2560;
        if (screenH <= 0) screenH = 1440;

        if (dist <= m_config.interactRadius) {
            ReleaseApproachKeys(kmbox);
            const auto target = common::WorldToScreenIsometric(dx, dy, 0.0f, static_cast<float>(screenW), static_cast<float>(screenH), 50.0f);
            kmbox.MoveMouseSmooth(static_cast<int>(target.x), static_cast<int>(target.y), m_curveGen, 10, 1);
            int dwellEventMs = m_curveGen.GenerateDwellTimeMs(40.0f, 8.0f);
            kmbox.ClickMouse(1, dwellEventMs);
            m_lastInteractMs = nowMs;
            CoreLog("[AreaEventHandler] Tương tác thành công với Area Event id=" + std::to_string(chosen.entityId));
            return true;
        }

        const auto wasd = common::WorldToWasd(dx, dy);
        std::vector<uint8_t> keys;
        if (wasd.up) keys.push_back(0x1A);
        if (wasd.down) keys.push_back(0x16);
        if (wasd.left) keys.push_back(0x04);
        if (wasd.right) keys.push_back(0x07);
        for (uint8_t hid : m_heldWasdHids) {
            bool keep = false;
            for (uint8_t n : keys) if (n == hid) { keep = true; break; }
            if (!keep) kmbox.KeyUp(hid);
        }
        for (uint8_t n : keys) {
            bool already = false;
            for (uint8_t hid : m_heldWasdHids) if (hid == n) { already = true; break; }
            if (!already) kmbox.KeyDown(n);
        }
        m_heldWasdHids = keys;
        return true;
    }

    return false;
}

void AreaEventHandler::ReleaseApproachKeys(KMBoxNet& kmbox) {
    for (uint8_t hid : m_heldWasdHids) {
        kmbox.KeyUp(hid);
    }
    m_heldWasdHids.clear();
}
