#include "memory/pointer_chain_resolver.hpp"
#include "memory/aob_scanner.hpp"

#include <cstring>
#include <cmath>
#include <algorithm>

PointerChainResolver::PointerChainResolver(IMemoryReader& reader, const PointerChainConfig& config)
    : m_reader(reader),
      m_mainBase(config.mainBase),
      m_moduleSize(config.moduleSize),
      m_staticRva(config.staticRva),
      m_entityMapOffset(config.entityMapOffset),
      m_playerOffset(config.playerOffset),
      m_statOffset(config.statOffset),
      m_posOffset(config.posOffset),
      m_isSimulated(config.isSimulated) {
    if (reader.BackendName() && std::strstr(reader.BackendName(), "Simulated") != nullptr) {
        m_isSimulated = true;
    }
    if (m_isSimulated && m_mainBase == 0) {
        m_mainBase = game_layout::kSimulatedBaseAddress;
        m_moduleSize = game_layout::kSimulatedMemorySize;
    }
}

void PointerChainResolver::SetMainBase(uintptr_t base, size_t size) {
    m_mainBase = base;
    if (size > 0) {
        m_moduleSize = size;
    }
}

void PointerChainResolver::InvalidateCache() {
    m_cachedInGame = 0;
}

bool PointerChainResolver::EnsureMainBase() {
    if (m_mainBase != 0) {
        return true;
    }

    if (m_isSimulated) {
        m_mainBase = game_layout::kSimulatedBaseAddress;
        m_moduleSize = game_layout::kSimulatedMemorySize;
        return true;
    }

    const uint32_t pid = m_reader.AttachedPid();
    if (pid == 0) {
        return false;
    }

    ModuleInfo mod{};
    if (AobScanner::FindModule(pid, L"PathOfExile.exe", mod) ||
        AobScanner::FindModule(pid, L"PathOfExileSteam.exe", mod) ||
        AobScanner::FindModule(pid, L"PathOfExile2.exe", mod) ||
        AobScanner::FindModule(pid, L"PathOfExile_x64.exe", mod) ||
        AobScanner::FindModule(pid, L"PathOfExile_x64Steam.exe", mod)) {
        m_mainBase = mod.base;
        m_moduleSize = mod.size;
        return true;
    }

    return false;
}

bool PointerChainResolver::ValidateInGameState(uintptr_t candRoot) const {
    if (!IsValidAddress(candRoot)) {
        return false;
    }

    if (m_isSimulated) {
        uint32_t magic = 0;
        if (m_reader.ReadValue<uint32_t>(candRoot + game_layout::kInGameMagicOffset, magic)) {
            return (magic == game_layout::kInGameMagic);
        }
        return false;
    }

    // 1. Kiểm tra EntityMap header tại rootOffset hoặc default 0x3E8
    const uintptr_t rootOffset = (m_entityMapOffset != 0) ? m_entityMapOffset : game_layout::kEntityMapRootOffset;
    uintptr_t mapPtr = 0;
    if (m_reader.ReadValue<uintptr_t>(candRoot + rootOffset, mapPtr) && IsValidAddress(mapPtr)) {
        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) {
                return true;
            }
        }
    }

    // 2. Kiểm tra EntityMap tại offset 0x18
    if (rootOffset != game_layout::kEntityMapPtrOffset) {
        uintptr_t mapPtr18 = 0;
        if (m_reader.ReadValue<uintptr_t>(candRoot + game_layout::kEntityMapPtrOffset, mapPtr18) && IsValidAddress(mapPtr18)) {
            uint32_t cap = 0, cnt = 0;
            if (m_reader.Read(mapPtr18, &cap, sizeof(cap)) &&
                m_reader.Read(mapPtr18 + sizeof(uint32_t), &cnt, sizeof(cnt))) {
                if (cap >= 8 && cap <= 8192 && cnt <= cap) {
                    return true;
                }
            }
        }
    }

    // 3. Kiểm tra PlayerEntity pointer tại playerOffset (0xBE0 trên live client 6AA2213C)
    const uintptr_t pOffset = (m_playerOffset != 0) ? m_playerOffset : 0xBE0;
    uintptr_t realPlayerPtr = 0;
    if (m_reader.ReadValue<uintptr_t>(candRoot + pOffset, realPlayerPtr) && IsValidAddress(realPlayerPtr)) {
        const uintptr_t sOffset = (m_statOffset != 0) ? m_statOffset : 0x3D8;
        uint32_t hp[2] = {0, 0};
        if (m_reader.Read(realPlayerPtr + sOffset, hp, sizeof(hp))) {
            if ((hp[1] == 1 && hp[0] <= 1) || (hp[1] > 0 && hp[1] <= 100000 && hp[0] <= hp[1])) {
                return true;
            }
        }
    }

    // 4. Kiểm tra PlayerState pointer tại offset 0x10 (legacy / simulated)
    uintptr_t playerPtr = 0;
    if (m_reader.ReadValue<uintptr_t>(candRoot + game_layout::kPlayerStatePtrOffset, playerPtr) && IsValidAddress(playerPtr)) {
        uint32_t maxHp = 0;
        if (m_reader.ReadValue<uint32_t>(playerPtr + offsetof(game_layout::PlayerStateMemory, maxHP), maxHp)) {
            if (maxHp > 0 && maxHp <= 100000) {
                return true;
            }
        }
    }

    return false;
}

bool PointerChainResolver::ResolveInGameState(uintptr_t& outInGame) {
    // ---- Hot Path O(1) (<0.001ms, 0 syscall, 0 heap alloc) ----
    if (m_cachedInGame != 0) {
        if (ValidateInGameState(m_cachedInGame)) {
            outInGame = m_cachedInGame;
            return true;
        }
        m_cachedInGame = 0;
    }

    // ---- Chế độ Backend Giả Lập (SimulatedMemoryReader) ----
    if (m_isSimulated) {
        const uintptr_t base = (m_mainBase != 0) ? m_mainBase : game_layout::kSimulatedBaseAddress;
        uintptr_t inGamePtr = 0;
        if (m_reader.ReadValue<uintptr_t>(base + game_layout::kInGameStatePtrStaticOffset, inGamePtr) &&
            IsValidAddress(inGamePtr)) {
            if (ValidateInGameState(inGamePtr)) {
                m_cachedInGame = inGamePtr;
                m_resolvedRva = game_layout::kInGameStatePtrStaticOffset;
                outInGame = inGamePtr;
                return true;
            }
        }
        return false;
    }

    // ---- Chế độ Game Thật (ReadProcessMemoryReader / DMA) ----
    if (!EnsureMainBase()) {
        return false;
    }

    // Tier 1: Static RVA cấu hình
    if (m_staticRva != 0) {
        uintptr_t candRoot = 0;
        if (m_reader.ReadValue<uintptr_t>(m_mainBase + m_staticRva, candRoot) && IsValidAddress(candRoot)) {
            if (ValidateInGameState(candRoot)) {
                m_cachedInGame = candRoot;
                m_resolvedRva = m_staticRva;
                outInGame = candRoot;
                return true;
            }
        }
    }

    // Tier 2a: Static roots do OffsetRegistry cung cấp (đã build-matched theo INV-BUILD-01)
    for (uintptr_t rva : m_registryRoots) {
        if (rva == 0) {
            continue;
        }
        uintptr_t candRoot = 0;
        if (m_reader.ReadValue<uintptr_t>(m_mainBase + rva, candRoot) && IsValidAddress(candRoot)) {
            if (ValidateInGameState(candRoot)) {
                m_cachedInGame = candRoot;
                m_resolvedRva = rva;
                outInGame = candRoot;
                return true;
            }
        }
    }

    // Tier 2b: Danh sách 8 static roots biên dịch sẵn trong .data
    // INV-BUILD-01: CHỈ được dùng khi build fingerprint khớp (hoặc gate chưa arm).
    // Các RVA này suy ra từ một build cụ thể; nếu client đã đổi build (section dịch
    // chuyển) thì chúng trỏ sai biến và KHÔNG được phép thử.
    if (CompiledStaticRootsAllowed()) {
        for (uintptr_t rva : kKnownStaticRoots) {
            uintptr_t candRoot = 0;
            if (m_reader.ReadValue<uintptr_t>(m_mainBase + rva, candRoot) && IsValidAddress(candRoot)) {
                if (ValidateInGameState(candRoot)) {
                    m_cachedInGame = candRoot;
                    m_resolvedRva = rva;
                    outInGame = candRoot;
                    return true;
                }
            }
        }
    }

    // Tier 3: AOB Pattern Scan (InGameStateBase)
    if (FindInGameStateViaAob(outInGame)) {
        return true;
    }

    return false;
}

bool PointerChainResolver::FindInGameStateViaAob(uintptr_t& outInGame) {
    if (!EnsureMainBase() || m_moduleSize == 0) {
        return false;
    }

    const AobPattern pattern = AobPattern::Parse(kInGameStatePattern);
    if (!pattern.Valid()) {
        return false;
    }

    const ModuleInfo mod{"PathOfExile.exe", m_mainBase, m_moduleSize};
    const uintptr_t match = AobScanner::ScanModule(m_reader, mod, pattern);
    if (match == 0) {
        return false;
    }

    // Pattern: 48 8B 05 [disp32]
    // Disp32 bắt đầu tại offset 3 từ matchAddress
    const uintptr_t ripTarget = AobScanner::ResolveRipTarget(m_reader, match, 3);
    if (!IsValidAddress(ripTarget)) {
        return false;
    }

    uintptr_t candRoot = 0;
    if (m_reader.ReadValue<uintptr_t>(ripTarget, candRoot) && IsValidAddress(candRoot)) {
        if (ValidateInGameState(candRoot)) {
            m_cachedInGame = candRoot;
            m_resolvedRva = (ripTarget >= m_mainBase) ? (ripTarget - m_mainBase) : 0;
            outInGame = candRoot;
            return true;
        }
    }

    return false;
}

bool PointerChainResolver::ResolvePlayer(uintptr_t inGame, ResolvedPlayerState& outPlayer) {
    if (!IsValidAddress(inGame)) {
        return false;
    }

    // 1. Chế độ Game Thật: InGameState + playerOffset (0xBE0) -> PlayerEntity
    //    PlayerEntity + statOffset (0x3D8) -> LifeComponent
    //    PlayerEntity + posOffset (0x290)  -> Position Data XYZ
    if (!m_isSimulated) {
        const uintptr_t pOffset = (m_playerOffset != 0) ? m_playerOffset : 0xBE0;
        uintptr_t playerPtr = 0;
        if (m_reader.ReadValue<uintptr_t>(inGame + pOffset, playerPtr) && IsValidAddress(playerPtr)) {
            const uintptr_t sOffset = (m_statOffset != 0) ? m_statOffset : 0x3D8;
            const uintptr_t posOff = (m_posOffset != 0) ? m_posOffset : 0x290;

            const uintptr_t lifeAddr = playerPtr + sOffset;
            const uintptr_t posAddr = playerPtr + posOff;

            // Đọc Vitals từ LifeComponent (đã đối chiếu ground-truth 12/09/2026 build 6AA2213C)
            // +0x00: curHP, maxHP
            // +0x0C: curES, maxES
            // +0x20: curWard, maxWard
            // +0x420: curMana, maxMana
            // +0x44C: curSpirit, maxSpirit
            uint32_t hp[2] = {0, 0};
            if (m_reader.Read(lifeAddr, hp, sizeof(hp))) {
                const bool isCi = (hp[1] == 1);
                const bool validHp = isCi || (hp[1] > 0 && hp[1] <= 100000 && hp[0] <= hp[1]);
                if (validHp) {
                    outPlayer.lifeAddr = lifeAddr;
                    outPlayer.curHP = hp[0];
                    outPlayer.maxHP = hp[1];

                    // ES (+0x0C)
                    uint32_t es[2] = {0, 0};
                    if (m_reader.Read(lifeAddr + 0x0C, es, sizeof(es))) {
                        outPlayer.curES = es[0];
                        outPlayer.maxES = es[1];
                    }

                    // Ward (+0x20)
                    uint32_t ward[2] = {0, 0};
                    if (m_reader.Read(lifeAddr + 0x20, ward, sizeof(ward))) {
                        outPlayer.curWard = ward[0];
                        outPlayer.maxWard = ward[1];
                    }

                    // Mana (+0x420)
                    uint32_t mana[2] = {0, 0};
                    if (m_reader.Read(lifeAddr + 0x420, mana, sizeof(mana))) {
                        outPlayer.curMana = mana[0];
                        outPlayer.maxMana = mana[1];
                    }

                    // Spirit (+0x44C)
                    uint32_t spirit[2] = {0, 0};
                    if (m_reader.Read(lifeAddr + 0x44C, spirit, sizeof(spirit))) {
                        outPlayer.curSpirit = spirit[0];
                        outPlayer.maxSpirit = spirit[1];
                    }

                    // INV-MEM-DYNAMIC-XYZ / Rule 8 (RCA Doc 46):
                    // Các offset nông trên PlayerEntity như 0x120, 0x290 (Hideout Spawn), 0x540 (Creek Spawn)
                    // là toạ độ Checkpoint/Anchor tĩnh của các instance cũ, KHÔNG PHẢI TỌA ĐỘ DYNAMIC THỜI GIAN THỰC!
                    // Tuyệt đối từ chối gán posAddr tại đây để nhường quyền tự động dò vi sai 16MB cho PlayerFinder.
                    const bool isStaticAnchor = (posOff == 0 || posOff == 0x290 || posOff == 0x540 || posOff == 0x120);
                    if (isStaticAnchor) {
                        outPlayer.posAddr = 0;
                        outPlayer.x = 0.0f;
                        outPlayer.y = 0.0f;
                        outPlayer.z = 0.0f;
                    } else {
                        float coords[3] = {0.0f, 0.0f, 0.0f};
                        if (m_reader.Read(posAddr, coords, sizeof(coords))) {
                            if (std::isfinite(coords[0]) && std::isfinite(coords[1]) && std::isfinite(coords[2])) {
                                outPlayer.posAddr = posAddr;
                                outPlayer.x = coords[0];
                                outPlayer.y = coords[1];
                                outPlayer.z = coords[2];
                            } else {
                                outPlayer.posAddr = 0;
                                outPlayer.x = 0.0f;
                                outPlayer.y = 0.0f;
                                outPlayer.z = 0.0f;
                            }
                        } else {
                            outPlayer.posAddr = 0;
                            outPlayer.x = 0.0f;
                            outPlayer.y = 0.0f;
                            outPlayer.z = 0.0f;
                        }
                    }

                    return true;
                }
            }
        }
    }

    // 2. Chế độ Mô Phỏng / Mock hoặc Fallback: InGameState + 0x10 -> PlayerState pointer
    uintptr_t playerPtr = 0;
    if (!m_reader.ReadValue<uintptr_t>(inGame + game_layout::kPlayerStatePtrOffset, playerPtr) ||
        !IsValidAddress(playerPtr)) {
        return false;
    }

    game_layout::PlayerStateMemory mem{};
    if (!m_reader.Read(playerPtr, &mem, sizeof(mem))) {
        return false;
    }

    // Sanity checks cho tọa độ không gian
    if (!std::isfinite(mem.posX) || !std::isfinite(mem.posY) || !std::isfinite(mem.posZ)) {
        return false;
    }

    outPlayer.lifeAddr = playerPtr + offsetof(game_layout::PlayerStateMemory, currentHP);
    outPlayer.posAddr = playerPtr + offsetof(game_layout::PlayerStateMemory, posX);

    outPlayer.curHP = mem.currentHP;
    outPlayer.maxHP = mem.maxHP;
    outPlayer.curMana = mem.currentMana;
    outPlayer.maxMana = mem.maxMana;
    outPlayer.curES = mem.currentES;
    outPlayer.maxES = mem.maxES;
    outPlayer.curSpirit = mem.currentSpirit;
    outPlayer.maxSpirit = mem.maxSpirit;
    outPlayer.curWard = mem.currentWard;
    outPlayer.maxWard = mem.maxWard;

    outPlayer.x = mem.posX;
    outPlayer.y = mem.posY;
    outPlayer.z = mem.posZ;

    return true;
}

bool PointerChainResolver::ResolvePlayer(ResolvedPlayerState& outPlayer) {
    uintptr_t inGame = 0;
    if (!ResolveInGameState(inGame)) {
        return false;
    }
    return ResolvePlayer(inGame, outPlayer);
}

bool PointerChainResolver::ResolveEntityMap(uintptr_t inGame, uintptr_t& outEntityMap, uint32_t& outCap, uint32_t& outCount) {
    if (!IsValidAddress(inGame)) {
        return false;
    }

    const uintptr_t rootOffset = (m_entityMapOffset != 0) ? m_entityMapOffset : game_layout::kEntityMapRootOffset;
    uintptr_t mapPtr = 0;
    bool found = false;

    // 1. Thử rootOffset (0x3E8 mặc định)
    if (m_reader.ReadValue<uintptr_t>(inGame + rootOffset, mapPtr) && IsValidAddress(mapPtr)) {
        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) {
                outEntityMap = mapPtr;
                outCap = cap;
                outCount = cnt;
                found = true;
            }
        }
    }

    // 2. Fallback thử offset 0x18
    if (!found && rootOffset != game_layout::kEntityMapPtrOffset) {
        if (m_reader.ReadValue<uintptr_t>(inGame + game_layout::kEntityMapPtrOffset, mapPtr) && IsValidAddress(mapPtr)) {
            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_entityMapOffset = game_layout::kEntityMapPtrOffset;
                    outEntityMap = mapPtr;
                    outCap = cap;
                    outCount = cnt;
                    found = true;
                }
            }
        }
    }

    // 3. Quét dải rộng [0x10, 0x800] cho game thật nếu chưa tìm thấy
    if (!found && !m_isSimulated) {
        for (uintptr_t candOffset = 0x10; candOffset <= 0x800; candOffset += 8) {
            uintptr_t candMapPtr = 0;
            if (m_reader.ReadValue<uintptr_t>(inGame + candOffset, candMapPtr) && IsValidAddress(candMapPtr)) {
                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) {
                        uintptr_t firstEnt = 0;
                        if (cnt == 0 || (m_reader.ReadValue<uintptr_t>(candMapPtr + 8, firstEnt) && (firstEnt == 0 || IsValidAddress(firstEnt)))) {
                            m_entityMapOffset = candOffset;
                            outEntityMap = candMapPtr;
                            outCap = cap;
                            outCount = cnt;
                            found = true;
                            break;
                        }
                    }
                }
            }
        }
    }

    return found;
}

bool PointerChainResolver::ResolveEntityMap(uintptr_t& outEntityMap, uint32_t& outCap, uint32_t& outCount) {
    uintptr_t inGame = 0;
    if (!ResolveInGameState(inGame)) {
        return false;
    }
    return ResolveEntityMap(inGame, outEntityMap, outCap, outCount);
}
