#include "memory/entity_manager.hpp"
#include "memory/player_finder.hpp"

#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>

EntityManager::EntityManager(IMemoryReader& reader, const EntityManagerConfig& config)
    : m_reader(reader), m_config(config) {
    m_entities.reserve(m_config.maxEntities);
    if (reader.BackendName() && std::strstr(reader.BackendName(), "Simulated") != nullptr) {
        m_isSimulated = true;
    }
}

bool EntityManager::ReadInGameState(uintptr_t& outInGame) {
    // Nếu đã có địa chỉ InGameState được cấp từ bên ngoài (GameSession)
    if (m_inGameAddress != 0) {
        outInGame = m_inGameAddress;
        return true;
    }

    // Fallback: đọc từ cấu hình tĩnh nếu đang ở chế độ mô phỏng
    if (m_isSimulated) {
        uintptr_t inGamePtr = 0;
        if (!m_reader.ReadValue<uintptr_t>(
                game_layout::kSimulatedBaseAddress + game_layout::kInGameStatePtrStaticOffset,
                inGamePtr)) {
            return false;
        }
        if (inGamePtr == 0) return false;

        uint32_t magic = 0;
        if (!m_reader.ReadValue<uint32_t>(inGamePtr + game_layout::kInGameMagicOffset, magic)) {
            return false;
        }
        if (magic != game_layout::kInGameMagic) return false;

        outInGame = inGamePtr;
        return true;
    }

    // Trên game thật: Thử nghiệm danh sách các con trỏ trạng thái tĩnh đã biết trong .data
    if (m_mainBase != 0) {
        static const std::vector<uintptr_t> kCandidateInGameRVAs = {
            0x45CFEF8, 0x4715740, 0x45CCA20, 0x45D3280, 0x45D3288, 0x45CCA28, 0x45CCA30, 0x45D3290,
            0x45CCFE0, 0x45CCFD8, 0x45D4100, 0x45D3DD8, 0x45D40F8, 0x4716880, 0x4435F90, 0x4435F98
        };
        for (uintptr_t rva : kCandidateInGameRVAs) {
            uintptr_t candRoot = 0;
            if (m_reader.ReadValue<uintptr_t>(m_mainBase + rva, candRoot) && candRoot >= 0x10000) {
                uintptr_t rootOffset = (m_entityMapOffset != 0) ? m_entityMapOffset : game_layout::kEntityMapRootOffset;
                uint64_t mapPtr = 0;
                if (m_reader.ReadValue<uint64_t>(candRoot + rootOffset, mapPtr) && mapPtr >= 0x10000) {
                    uint32_t cap = 0, cnt = 0;
                    if (m_reader.Read(mapPtr, &cap, sizeof(cap)) && m_reader.Read(mapPtr + sizeof(uint32_t), &cnt, sizeof(cnt))) {
                        if (cap >= 8 && cap <= 8192 && cnt <= cap) {
                            m_inGameAddress = candRoot;
                            outInGame = candRoot;
                            return true;
                        }
                    }
                }
            }
        }
    }

    return false;
}

bool EntityManager::ReadPlayer(uintptr_t inGame) {
    // InGameState + 0x10 -> PlayerState
    uint64_t playerPtr = 0;
    if (!m_reader.ReadValue<uint64_t>(inGame + game_layout::kPlayerStatePtrOffset, playerPtr) || playerPtr < 0x10000) {
        return false;
    }

    return m_reader.Read(playerPtr, &m_player, sizeof(m_player));
}

bool EntityManager::ReadEntities(uintptr_t inGame) {
    uintptr_t rootOffset = (m_entityMapOffset != 0) ? m_entityMapOffset : game_layout::kEntityMapRootOffset;
    uint64_t mapPtr = 0;

    // Thử đọc theo rootOffset cấu hình trước
    bool foundValidMap = false;
    if (m_reader.ReadValue<uint64_t>(inGame + rootOffset, mapPtr) && mapPtr >= 0x10000) {
        uint32_t cap = 0, cnt = 0;
        if (m_reader.Read(mapPtr, &cap, sizeof(cap)) && m_reader.Read(mapPtr + sizeof(uint32_t), &cnt, sizeof(cnt))) {
            if (cap > 0 && cnt <= cap && cap <= 8192) {
                foundValidMap = true;
            }
        }
    }

    // Nếu rootOffset hiện tại không hợp lệ: tự động quét tìm EntityMap trong dải rộng [0x10, 0x800]
    if (!foundValidMap && !m_isSimulated) {
        for (uintptr_t candOffset = 0x10; candOffset <= 0x1200; candOffset += 8) {
            uint64_t candMapPtr = 0;
            if (m_reader.ReadValue<uint64_t>(inGame + candOffset, candMapPtr) && candMapPtr >= 0x10000) {
                uint32_t cap = 0, cnt = 0;
                if (m_reader.Read(candMapPtr, &cap, sizeof(cap)) && m_reader.Read(candMapPtr + sizeof(uint32_t), &cnt, sizeof(cnt))) {
                    if (cap >= 8 && cap <= 8192 && cnt <= cap) {
                        uint64_t firstEnt = 0;
                        if (cnt == 0 || (m_reader.ReadValue<uint64_t>(candMapPtr + 8, firstEnt) && (firstEnt == 0 || firstEnt >= 0x10000))) {
                            mapPtr = candMapPtr;
                            m_entityMapOffset = candOffset;
                            foundValidMap = true;
                            break;
                        }
                    }
                }
            }
        }
    }

    if (!foundValidMap || mapPtr == 0) return false;

    // Header EntityMap: capacity + count
    uint32_t capacity = 0, count = 0;
    if (!m_reader.Read(mapPtr, &capacity, sizeof(capacity))) return false;
    if (!m_reader.Read(mapPtr + sizeof(uint32_t), &count, sizeof(count))) return false;
    if (capacity == 0 || count > capacity) return false;

    m_totalInMap = count;
    m_entities.clear();
    m_totalFiltered = 0;

    // Duyệt từng entry con trỏ
    for (uint32_t i = 0; i < capacity && i < game_layout::kMaxEntitySlots; ++i) {
        if (m_entities.size() >= m_config.maxEntities) break;

        uint64_t entityPtr = 0;
        if (!m_reader.ReadValue<uint64_t>(mapPtr + 8 + i * sizeof(uint64_t), entityPtr)) {
            continue;
        }
        if (entityPtr < 0x10000) continue;

        game_layout::EntityMemory ent{};
        if (!m_reader.Read(entityPtr, &ent, sizeof(ent))) continue; // bỏ qua slot lỗi
        if (m_isSimulated && ent.magic != game_layout::kEntityMagic) continue; // slot đã được giải phóng trên sim
        if (ent.extraFlags & game_layout::kEntityFlagDead) {        // chết: bỏ qua
            ++m_totalFiltered;
            continue;
        }

        // Tính toán khoảng cách thực tế tới người chơi
        float dist = ent.distanceToPlayer;
        if (!m_isSimulated && (m_playerX != 0.0f || m_playerY != 0.0f)) {
            float dx = ent.posX - m_playerX;
            float dy = ent.posY - m_playerY;
            dist = std::sqrt(dx * dx + dy * dy);
            ent.distanceToPlayer = dist;
        }

        if (dist > m_config.maxEntityDistance) {    // ngoài tầm tối đa
            ++m_totalFiltered;
            continue;
        }

        EntityTelemetryData out{};
        out.id = ent.id ? ent.id : (i + 1);
        out.type = ent.type;
        out.rarity = ent.rarity;
        out.posX = ent.posX;
        out.posY = ent.posY;
        out.posZ = ent.posZ;
        out.distanceToPlayer = dist;
        out.currentHP = ent.currentHP;
        out.maxHP = ent.maxHP;
        out.staggerProgress = ent.staggerProgress;
        out.currentAnimationId = ent.animationId;
        out.extraFlags = ent.extraFlags;
        static_assert(sizeof(out.name) == sizeof(ent.name), "Kích thước name phải khớp");
        std::memcpy(out.name, ent.name, sizeof(out.name));

        // Phân loại mở rộng Entity Taxonomy (Doc 29 mục 7.2)
        out.type = game_layout::ClassifyEntityType(out.name, out.type, out.maxHP, out.extraFlags);

        m_entities.push_back(out);
    }
    return true;
}

bool EntityManager::Update() {
    uintptr_t inGame = 0;
    if (ReadInGameState(inGame)) {
        // Thông tin khu vực + tick
        m_reader.ReadValue<uint32_t>(inGame + game_layout::kAreaIdOffset, m_areaId);
        m_reader.ReadValue<uint32_t>(inGame + game_layout::kAreaSeedOffset, m_areaSeed);
        m_reader.ReadValue<uint32_t>(inGame + game_layout::kGameTickOffset, m_gameTick);

        ReadPlayer(inGame); // Không bắt buộc thành công vì GameSession tự đọc
        if (ReadEntities(inGame)) {
            return true;
        }
    }

    // Fallback trên game thật: Quét các thực thể quái vật lân cận trong bộ nhớ heap
    if (!m_isSimulated) {
        if (ScanProximityMonsters()) {
            return true;
        }
    }

    m_entities.clear();
    return false;
}

bool EntityManager::ScanProximityMonsters() {
    if (m_isSimulated) return false;

    // Lấy thời gian hiện tại
    FILETIME ft;
    GetSystemTimeAsFileTime(&ft);
    ULARGE_INTEGER uli;
    uli.LowPart = ft.dwLowDateTime;
    uli.HighPart = ft.dwHighDateTime;
    const uint64_t nowMs = (uli.QuadPart - 116444736000000000ULL) / 10000;

    // Cần tọa độ người chơi thực tế
    if (m_playerX == 0.0f && m_playerY == 0.0f) {
        m_entities.clear();
        m_totalInMap = 0;
        return false;
    }

    // Cập nhật lại khoảng cách tới người chơi và loại bỏ quái chết tức thời (0ms latency)
    if (!m_entities.empty()) {
        const uintptr_t statOff = m_entityStatOffset ? m_entityStatOffset : 0x3B8;
        for (auto it = m_entities.begin(); it != m_entities.end(); ) {
            auto mapIt = m_entPtrMap.find(it->id);
            if (mapIt != m_entPtrMap.end()) {
                uint32_t liveHp = 0, liveMaxHp = 0;
                if (!m_reader.ReadValue<uint32_t>(mapIt->second + statOff, liveHp) ||
                    !m_reader.ReadValue<uint32_t>(mapIt->second + statOff + 4, liveMaxHp) ||
                    liveHp == 0 || liveHp > liveMaxHp) {
                    // Quái đã bị tiêu diệt hoặc đối tượng RAM đã bị hủy -> XÓA TỨC THÌ!
                    m_entPtrMap.erase(mapIt);
                    it = m_entities.erase(it);
                    continue;
                }
                it->currentHP = liveHp;
            }
            float dx = it->posX - m_playerX;
            float dy = it->posY - m_playerY;
            it->distanceToPlayer = std::sqrt(dx * dx + dy * dy);
            ++it;
        }
        m_totalInMap = m_entities.size();

        // Tái quét toàn diện mỗi 60ms (tăng tốc gấp 6 lần) để quái mới xuất hiện ngay lập tức
        if (nowMs - m_lastProximityScanMs < 60) {
            return !m_entities.empty();
        }
    }

    m_lastProximityScanMs = nowMs;

    std::vector<EntityTelemetryData> newEntities;
    newEntities.reserve(64);

    // 0. Quét Entity Pointer Array (ưu tiên số 1 - chính xác 100% dựa trên entscan thực địa)
    if (m_playerAddr != 0) {
        constexpr uintptr_t kDefaultStatOff = 0x3B8;
        const uintptr_t playerEntityBase = (m_playerAddr > kDefaultStatOff) ? (m_playerAddr - kDefaultStatOff) : 0;

        // Nếu chưa có slot mảng, tìm kiếm slot chứa con trỏ playerEntityBase
        if (m_entityArraySlot == 0 && playerEntityBase != 0) {
            for (intptr_t off = -0x200; off <= 0x200; off += 8) {
                uintptr_t checkSlot = playerEntityBase + off;
                uint64_t val = 0;
                if (m_reader.ReadValue<uint64_t>(checkSlot, val) && val == playerEntityBase) {
                    m_entityArraySlot = checkSlot;
                    m_entityStatOffset = kDefaultStatOff;
                    break;
                }
            }

            if (m_entityArraySlot == 0) {
                PlayerFinder finder(m_reader);
                PlayerFinder::EntityArrayInfo arrInfo;
                if (finder.FindEntityArray(m_playerAddr, arrInfo)) {
                    m_entityArraySlot = arrInfo.slotAddr;
                    m_entityStatOffset = arrInfo.statOff;
                }
            }
        }

        // Duyệt các slot trong Entity Pointer Array qua 1 lần đọc bộ nhớ gộp (Batch RPM)
        if (m_entityArraySlot != 0) {
            const uintptr_t statOff = m_entityStatOffset ? m_entityStatOffset : kDefaultStatOff;
            int validEntitiesFound = 0;

            constexpr int kMinSlotIdx = -64;
            constexpr int kMaxSlotIdx = 128;
            constexpr size_t kSlotCount = kMaxSlotIdx - kMinSlotIdx + 1; // 193 slots
            std::vector<uint64_t> slotArray(kSlotCount, 0);
            const uintptr_t batchBase = m_entityArraySlot + static_cast<intptr_t>(kMinSlotIdx) * 8;
            m_reader.Read(batchBase, slotArray.data(), kSlotCount * sizeof(uint64_t));

            for (size_t sIdx = 0; sIdx < kSlotCount; ++sIdx) {
                uint64_t entPtr = slotArray[sIdx];
                if (entPtr < 0x10000 || entPtr > 0x7FFFFFFEFFFFULL) {
                    continue;
                }
                if (playerEntityBase != 0 && entPtr == playerEntityBase) {
                    continue; // Bỏ qua người chơi
                }

                // Đọc HP và MaxHP tại LifeComponent (+statOff)
                uint32_t mHp = 0, mMaxHp = 0;
                if (!m_reader.ReadValue<uint32_t>(entPtr + statOff, mHp) ||
                    !m_reader.ReadValue<uint32_t>(entPtr + statOff + 4, mMaxHp)) {
                    continue;
                }

                if (mMaxHp == 0 || mMaxHp > 20000000 || mHp == 0 || mHp > mMaxHp) {
                    continue;
                }

                validEntitiesFound++;

                // Tìm tọa độ quái trong các Component (Tối ưu qua Cache)
                float ex = 0.0f, ey = 0.0f, ez = 0.0f;
                bool coordsFound = false;

                // 1. Thử đọc nhanh qua cache nếu đã tìm thấy trước đó (chỉ 2 syscall thay vì 56 syscall)
                if (m_cachedCompOff != 0 && m_cachedPosOff != 0) {
                    uint64_t compPtr = 0;
                    if (m_reader.ReadValue<uint64_t>(entPtr + m_cachedCompOff, compPtr) && compPtr >= 0x10000 && compPtr <= 0x7FFFFFFEFFFFULL) {
                        float cx = 0, cy = 0, cz = 0;
                        if (m_reader.Read(compPtr + m_cachedPosOff, &cx, 4) &&
                            m_reader.Read(compPtr + m_cachedPosOff + 4, &cy, 4) &&
                            m_reader.Read(compPtr + m_cachedPosOff + 8, &cz, 4)) {
                            if (PlayerFinder::IsValidXYZ(cx, cy, cz)) {
                                if (m_playerX != 0.0f || m_playerY != 0.0f) {
                                    float cdx = cx - m_playerX;
                                    float cdy = cy - m_playerY;
                                    float cdz = cz - m_playerZ;
                                    if (std::fabs(cdx) <= 1500.0f && std::fabs(cdy) <= 1500.0f && std::fabs(cdz) <= 500.0f) {
                                        ex = cx; ey = cy; ez = cz;
                                        coordsFound = true;
                                    }
                                } else {
                                    ex = cx; ey = cy; ez = cz;
                                    coordsFound = true;
                                }
                            }
                        }
                    }
                }

                // 2. Fallback: Duyệt tìm và ghi nhớ compOff/posOff vào cache
                if (!coordsFound) {
                    static const uintptr_t kCompOffsets[] = { 0x18, 0x20, 0x28, 0x30, 0x38, 0x40, 0x48, 0x50 };
                    for (uintptr_t compOff : kCompOffsets) {
                        uint64_t compPtr = 0;
                        if (m_reader.ReadValue<uint64_t>(entPtr + compOff, compPtr) && compPtr >= 0x10000 && compPtr <= 0x7FFFFFFEFFFFULL) {
                            static const uintptr_t kPosOffsets[] = { 0x28, 0x2C, 0x30, 0x38, 0x40, 0x44, 0x48 };
                            for (uintptr_t posOff : kPosOffsets) {
                                float cx = 0, cy = 0, cz = 0;
                                if (m_reader.Read(compPtr + posOff, &cx, 4) &&
                                    m_reader.Read(compPtr + posOff + 4, &cy, 4) &&
                                    m_reader.Read(compPtr + posOff + 8, &cz, 4)) {
                                    if (PlayerFinder::IsValidXYZ(cx, cy, cz)) {
                                        if (m_playerX != 0.0f || m_playerY != 0.0f) {
                                            float cdx = cx - m_playerX;
                                            float cdy = cy - m_playerY;
                                            float cdz = cz - m_playerZ;
                                            if (std::fabs(cdx) <= 1500.0f && std::fabs(cdy) <= 1500.0f && std::fabs(cdz) <= 500.0f) {
                                                ex = cx; ey = cy; ez = cz;
                                                coordsFound = true;
                                                m_cachedCompOff = compOff;
                                                m_cachedPosOff = posOff;
                                                break;
                                            }
                                        } else {
                                            ex = cx; ey = cy; ez = cz;
                                            coordsFound = true;
                                            m_cachedCompOff = compOff;
                                            m_cachedPosOff = posOff;
                                            break;
                                        }
                                    }
                                }
                            }
                        }
                        if (coordsFound) break;
                    }
                }

                // Nếu không tìm thấy tọa độ thật hợp lệ của quái vật quanh người chơi: BỎ QUA!
                if (!coordsFound) {
                    continue;
                }

                float dist = 50.0f;
                if (m_playerX != 0.0f || m_playerY != 0.0f) {
                    float dx = ex - m_playerX;
                    float dy = ey - m_playerY;
                    dist = std::sqrt(dx * dx + dy * dy);
                }

                if (dist > m_config.maxEntityDistance) {
                    continue; // Bỏ qua quái vật ở xa ngoài tầm radar
                }

                const uint32_t entId = static_cast<uint32_t>(entPtr & 0xFFFFFFFF);
                bool dup = false;
                for (const auto& existing : newEntities) {
                    if (existing.id == entId) {
                        dup = true;
                        break;
                    }
                }
                if (dup) continue;

                EntityTelemetryData ent{};
                ent.id = entId;
                ent.type = 1;
                for (const auto& prev : m_entities) {
                    if (prev.id == entId && prev.type != 1) {
                        ent.type = prev.type;
                        std::memcpy(ent.name, prev.name, sizeof(ent.name));
                        ent.rarity = prev.rarity;
                        ent.extraFlags = prev.extraFlags;
                        break;
                    }
                }
                if (ent.type == 1 && ent.name[0] == '\0') {
                    // Tuân thủ Rule 8 & Rule 14: Không tự chế cờ Boss hay Rarity giả lập từ ngưỡng HP
                    std::snprintf(ent.name, sizeof(ent.name), "Monster");
                }
                ent.posX = ex;
                ent.posY = ey;
                ent.posZ = ez;
                ent.distanceToPlayer = dist;
                ent.currentHP = mHp;
                ent.maxHP = mMaxHp;
                ent.staggerProgress = 0;

                newEntities.push_back(ent);
                m_entPtrMap[entId] = entPtr;
                if (newEntities.size() >= m_config.maxEntities) break;
            }

            if (validEntitiesFound == 0) {
                m_entityArraySlot = 0;
            }
        }
    }

    // Fail-Safe / Fail-Silent: Khi chưa calibrate được mảng Entity Array (m_entityArraySlot == 0),
    // chấp nhận danh sách newEntities rỗng và chờ cơ chế giải mã con trỏ tất định.
    // Tuyệt đối CẤM quét raw float để tự tạo quái ảo Hostile Monster HP 100/100 (Triệt tiêu lỗi đánh vào khoảng không theo Rule 8).

    // 2. Fail-Safe / Fail-Silent (Rule 8 & Rule 14):
    // Tuyệt đối không quét mò heap để tạo quái ảo (Phantom Monsters).
    // Nếu Entity Pointer Array chưa có quái, giữ newEntities rỗng và chờ chu kỳ kế tiếp.

    if (!newEntities.empty()) {
        for (const auto& prev : m_entities) {
            if (prev.type == 1) continue;
            bool already = false;
            for (const auto& n : newEntities) {
                if (n.id == prev.id) {
                    already = true;
                    break;
                }
            }
            if (!already) {
                newEntities.push_back(prev);
            }
        }
        std::sort(newEntities.begin(), newEntities.end(), [](const EntityTelemetryData& a, const EntityTelemetryData& b) {
            return a.distanceToPlayer < b.distanceToPlayer;
        });
        m_entities = std::move(newEntities);
        m_totalInMap = m_entities.size();
        return true;
    }

    std::vector<EntityTelemetryData> keepNonMonsters;
    keepNonMonsters.reserve(m_entities.size());
    for (const auto& prev : m_entities) {
        if (prev.type != 1) {
            keepNonMonsters.push_back(prev);
        }
    }
    m_entities = std::move(keepNonMonsters);
    m_entPtrMap.clear();
    m_totalInMap = m_entities.size();
    return !m_entities.empty();
}

void EntityManager::FillTelemetry(TelemetryPacket& pkt) const {
    if (m_isSimulated) {
        const auto& ps = m_player;
        pkt.player.posX = ps.posX;
        pkt.player.posY = ps.posY;
        pkt.player.posZ = ps.posZ;
        pkt.player.yaw = ps.yaw;
        pkt.player.pitch = 0.0f;
        pkt.player.currentHP = ps.currentHP;
        pkt.player.maxHP = ps.maxHP;
        pkt.player.currentMana = ps.currentMana;
        pkt.player.maxMana = ps.maxMana;
        pkt.player.currentES = ps.currentES;
        pkt.player.maxES = ps.maxES;
        pkt.player.currentWard = ps.currentWard;
        pkt.player.maxWard = ps.maxWard;
        pkt.player.currentSpirit = ps.currentSpirit;
        pkt.player.maxSpirit = ps.maxSpirit;
        pkt.player.activeWeaponSet = 1;
        pkt.player.movementFlags = ps.movementFlags;
        pkt.player.debuffMask = ps.debuffMask;
    }

    pkt.entityCount = static_cast<uint32_t>(m_entities.size());
    for (size_t i = 0; i < m_entities.size() && i < MAX_ENTITIES_PER_SNAPSHOT; ++i) {
        pkt.entities[i] = m_entities[i];
    }
}