#pragma once

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#include <windows.h>

#include <memory>
#include <string>
#include <string_view>

#include "common/protocol.hpp"
#include "memory/imemory_reader.hpp"
#include "memory/entity_manager.hpp"
#include "memory/player_finder.hpp"
#include "memory/terrain_reader.hpp"
#include "memory/pointer_chain_resolver.hpp"

// ==========================================================
// GameSession - Điều phối backend đọc bộ nhớ + EntityManager
//
// backend:
//   "sim"  : game ảo (SimulatedMemoryReader) - test không cần game
//   "rpm"  : ReadProcessMemory vào PathOfExile.exe (cần quyền Admin)
//   "auto" : thử RPM trước, thất bại thì chuyển sang sim
// ==========================================================

class SimulatedMemoryReader; // forward declaration
class TownQuestEngine;
class KMBoxNet;

namespace navigation {
class TerrainGrid;
}

class GameSession {
public:
    GameSession() = default;
    ~GameSession();

    // Khởi tạo backend + EntityManager. Trả về false nếu cả hai backend đều hỏng
    // (chỉ xảy ra với "rpm" thuần). Thông tin chi tiết qua outStatus.
    bool Initialize(const std::string& backend, std::string& outStatus);

    // Gọi mỗi tick: tiến mô phỏng (nếu sim) + Update() + điền dữ liệu vào packet
    // (KHÔNG đụng vào vùng area - do LogSensor quản lý)
    void Update(TelemetryPacket& pkt);

    // ---- Auto-calibration PlayerState (pattern {HP, maxHP, maxHP}) ----
    // Phiên bản tương tác console: hỏi maxHP -> quét -> hỏi HP sau damage -> lọc
    bool CalibrateInteractive();

    // Phiên bản không tương tác (dùng cho test / API)
    bool Calibrate(uint32_t maxHP, uint32_t newHP);

    // ---- Calibration tọa độ XYZ (float differential, tự động theo prompt) ----
    bool CalibratePositionInteractive();

    // ---- Tự Động Quét Ngầm Tọa Độ XYZ từ LogSensor Event (Zero User Input) ----
    enum class AutoPositionState {
        IDLE = 0,
        WAITING_ZONE_LOAD = 1,
        CAPTURING_STILL_BASELINE = 2,
        WAITING_FIRST_STEP = 3,
        RESOLVED = 4
    };

    // ---- Entity Array calibration (đã verify thực địa) ----
    bool CalibrateEntities();
    bool DiscoverEntityPosOffset();
    // INV-BUILD-01: nạp OffsetRegistry build-matched và arm cổng build gate
    void ApplyOffsetRegistry();
    bool EntityArrayCalibrated() const { return m_entitySlot != 0 && m_statOff != 0; }
    bool PosOffsetCalibrated() const { return m_posOff != 0; }

    // Bộ lọc UI Overlay Scene (World Map / Act Tabs / Atlas / DNT)
    static bool IsUIOverlayScene(std::string_view areaName) noexcept {
        if (areaName.empty() || areaName == "(null)" || areaName == "(unknown)") return true;
        if (areaName == "Act 1" || areaName == "Act 2" || areaName == "Act 3" || areaName == "Act 4" || areaName == "Atlas") return true;
        if (areaName.starts_with("[DNT")) return true;
        return false;
    }

    void OnZoneSceneChanged(const std::string& areaName);
    void OnZoneLoadingFinished(const std::string& areaName);
    void OnPlayerRespawned();
    void OnInstanceTransition();
    bool IsInstanceTransitionPending() const { return m_instanceTransitionPending; }
    void SetInstanceTransitionPending(bool pending) { m_instanceTransitionPending = pending; }
    void ProcessAutoPositionScan();
    void TriggerManualAutoScan();
    // RCA 21:03 (07/09): XYZ trước đây CHỈ hiệu chuẩn khi đổi map — khởi động Core
    // giữa map thì không bao giờ có baseline -> XYZ mãi (0,0,0) -> bot tê liệt.
    // Hàm này kích hoạt baseline + micro-step ngay lúc khởi động khi đã khóa HP.
    void StartStartupXYZCalibration();
    AutoPositionState AutoScanState() const { return m_autoPosState; }

    // Gán trực tiếp địa chỉ PlayerState (từ MemProbe findplayer)
    void SetPlayerAddress(uintptr_t addr) { m_playerAddr = addr; }

    // Gán trực tiếp địa chỉ Mana block (từ MemProbe findplayer maxMana)
    void SetManaAddress(uintptr_t addr) { m_manaAddr = addr; }

    // Gán trực tiếp địa chỉ Position XYZ
    void SetPositionAddress(uintptr_t addr) { m_xyzAddr = addr; }

    // Gán chuỗi con trỏ tĩnh (static RVA + các offset)
    void SetPointerChain(uintptr_t staticRva, const std::vector<uintptr_t>& offsets) {
        m_staticRva = staticRva;
        m_offsets = offsets;
    }

    bool IsSimulated() const { return m_simulated; }
    void SetSimulated(bool sim) { m_simulated = sim; }
    void SetAutoScanState(AutoPositionState state) { m_autoPosState = state; }
    void SetMovementIntent(bool intent) { m_hasMovementIntent = intent; }
    bool HasMovementIntent() const { return m_hasMovementIntent; }
    bool HasData() const { return m_hasData; }
    bool PlayerCalibrated() const { return m_playerAddr != 0 || m_staticRva != 0; }
    bool ManaCalibrated() const { return m_manaAddr != 0; }
    void ResetManaAddress() { m_manaAddr = 0; }
    bool PositionCalibrated() const { return m_xyzAddr != 0; }
    uintptr_t PlayerAddress() const { return m_playerAddr; }
    uintptr_t ManaAddress() const { return m_manaAddr; }
    uintptr_t ShieldAddress() const { return m_shieldAddr; }
    uintptr_t SpiritAddress() const { return m_spiritAddr; }
    uintptr_t PositionAddress() const { return m_xyzAddr; }
    void SetShieldAddress(uintptr_t addr) { m_shieldAddr = addr; }
    void SetSpiritAddress(uintptr_t addr) { m_spiritAddr = addr; }
    void SetInGameAddress(uintptr_t addr) { m_inGameAddr = addr; }
    uintptr_t InGameAddress() const { return m_inGameAddr; }
    void SetEntityMapOffset(uintptr_t off) { m_entityMapOffset = off; }
    uintptr_t EntityMapOffset() const { return m_entityMapOffset; }
    void SetExpectedMaxMana(uint32_t val) { m_expectedMaxMana = val; }
    uint32_t ExpectedMaxMana() const { return m_expectedMaxMana; }
    void SetExpectedMaxHP(uint32_t val) { m_expectedMaxHP = val; }
    uint32_t ExpectedMaxHP() const { return m_expectedMaxHP; }
    void SetExpectedMaxES(uint32_t val) { m_expectedMaxES = val; }
    uint32_t ExpectedMaxES() const { return m_expectedMaxES; }
    const std::string& CurrentSceneName() const { return m_currentSceneName; }
    void SetExpectedMaxSpirit(uint32_t val) { m_expectedMaxSpirit = val; }
    uint32_t ExpectedMaxSpirit() const { return m_expectedMaxSpirit; }
    void SetExpectedMaxWard(uint32_t val) { m_expectedMaxWard = val; }
    uint32_t ExpectedMaxWard() const { return m_expectedMaxWard; }
    uintptr_t WardAddress() const { return m_wardAddr; }
    void SetWardAddress(uintptr_t addr) { m_wardAddr = addr; }
    IMemoryReader* Reader() const { return m_reader.get(); }
    PointerChainResolver* PointerResolver() const { return m_pointerResolver.get(); }
    bool ResolveViaPointerChain();
    bool IsValidLifeComponent(uintptr_t addr, bool requireFullHP = true) const;
    bool IsValidManaAddress(uintptr_t addr) const;
    bool IsValidShieldAddress(uintptr_t addr) const;
    bool IsValidWardAddress(uintptr_t addr) const;
    bool ShieldCalibrated() const { return m_shieldAddr != 0; }
    bool AutoDetectPlayerHP(uint32_t expectedMaxHP = 0);
    bool AutoDetectManaPool(uint32_t expectedMaxMana = 0);
    bool AutoDetectShield(uint32_t expectedMaxShield = 0);
    bool AutoDetectWard(uint32_t expectedMaxWard = 0);
    bool AutoDetectSpirit(uint32_t expectedMaxSpirit = 0);
    bool AutoDetectTerrain(navigation::TerrainGrid* pGrid = nullptr, float playerX = 0.0f, float playerY = 0.0f);
    bool HasNativeTerrain() const { return m_nativeTerrain.IsValid(); }
    const memory::NativeTerrainData& NativeTerrain() const { return m_nativeTerrain; }
    uintptr_t TerrainAddress() const { return m_terrainAddr; }
    void SetTerrainAddress(uintptr_t addr) { m_terrainAddr = addr; }
    void SaveOffsets();
    uint32_t EntityCount() const { return m_entityCount; }
    size_t FilteredCount() const { return m_filteredCount; }
    void SetTownQuestEngine(TownQuestEngine* engine) { m_townQuestEngine = engine; }
    TownQuestEngine* GetTownQuestEngine() const { return m_townQuestEngine; }
    void SetKMBox(KMBoxNet* kmbox) { m_kmbox = kmbox; }
    KMBoxNet* GetKMBox() const { return m_kmbox; }
    void SetAllowPhysicalMove(bool allow) { m_allowPhysicalMove = allow; }
    bool AllowPhysicalMove() const { return m_allowPhysicalMove; }

    // Chế độ di chuyển của game (WASD / Click-to-Move) - quyết định Micro-Step
    // gửi tap W hay click chuột khi giải phóng Grace Period (RCA 2026-09-07 19:26:
    // game chạy Click-to-Move nhưng Micro-Step bấm W -> nhân vật không di chuyển
    // -> XYZ kẹt vĩnh viễn ở WAITING_FIRST_STEP).
    void SetMovementMode(MovementMode mode) { m_moveMode = mode; }
    MovementMode GetMovementMode() const { return m_moveMode; }

    // Quản lý trạng thái lướt né (Iframe Dodge Roll) & Grace Period
    void NotifyRoll(uint64_t nowMs = 0);
    void SetLastRollTime(uint64_t nowMs) { m_lastRollMs = nowMs; }
    uint64_t LastRollTime() const { return m_lastRollMs; }
    bool InGracePeriod() const { return m_inGracePeriod; }
    void SetInGracePeriod(bool val) { m_inGracePeriod = val; }

private:
    KMBoxNet* m_kmbox = nullptr;
    bool m_allowPhysicalMove = false;
    MovementMode m_moveMode = MovementMode::MOUSE; // mặc định Click-to-Move (chuẩn POE2)
    std::unique_ptr<IMemoryReader> m_reader;      // sở hữu backend hiện tại
    SimulatedMemoryReader* m_simPtr = nullptr;    // non-owning, dùng để Tick()
    std::unique_ptr<EntityManager> m_entities;
    std::unique_ptr<PlayerFinder> m_finder;
    std::unique_ptr<PointerChainResolver> m_pointerResolver;
    TownQuestEngine* m_townQuestEngine = nullptr; // non-owning pointer tới TownQuestEngine
    uintptr_t m_playerAddr = 0;                   // 0 = chưa calibrate
    uintptr_t m_shieldAddr = 0;                   // 0 = chưa calibrate shield/ES
    uintptr_t m_wardAddr = 0;                     // 0 = chưa calibrate ward
    uintptr_t m_manaAddr = 0;                     // 0 = chưa calibrate mana
    uintptr_t m_spiritAddr = 0;                   // 0 = chưa calibrate spirit
    uintptr_t m_xyzAddr = 0;                      // 0 = chưa calibrate position
    uintptr_t m_staticRva = 0;                    // Static RVA trong PathOfExile.exe
    std::vector<uintptr_t> m_offsets;             // Danh sách offset chuỗi con trỏ
    uintptr_t m_mainBase = 0;                     // Base address của PathOfExile.exe
    uint32_t m_processPid = 0;                    // PID của game POE2 thật
    std::wstring m_processName;                   // Tên file exe thật (PathOfExile.exe hoặc PathOfExileSteam.exe)
    bool m_simulated = false;
    bool m_hasData = false;
    uint32_t m_entityCount = 0;
    size_t m_filteredCount = 0;

    AutoPositionState m_autoPosState = AutoPositionState::IDLE;
    uint64_t m_zoneLoadTimeMs = 0;
    uint64_t m_lastScanCheckMs = 0;
    std::string m_currentSceneName;
    bool m_instanceTransitionPending = false;
    uint32_t m_expectedMaxMana = 0;
    uint32_t m_expectedMaxHP = 0;
    uint32_t m_expectedMaxES = 0;
    uint32_t m_expectedMaxWard = 0;
    uint32_t m_expectedMaxSpirit = 0;

    // Auto-Scan XYZ: fallback mở rộng cửa sổ baseline khi kẹt (RCA 2026-09-07)
    bool m_baselineWidened = false;      // đã mở rộng 16MB -> 64MB chưa
    uint64_t m_firstStepStartMs = 0;     // lúc bắt đầu WAITING_FIRST_STEP
    uint32_t m_esStaleCount = 0;         // đếm tick đọc ES vô lý (đảo/rác) để reset địa chỉ
    uint64_t m_lastSpiritAttemptMs = 0;  // throttle AutoDetectSpirit 3s/lần

    // Entity Array thật (đã verify thực địa 2026-09-06)
    uintptr_t m_entitySlot = 0;   // ô chứa entry của player trong array
    uintptr_t m_statOff = 0;      // offset HP trong entity (0x3B8 thực địa)
    uintptr_t m_posOff = 0;       // offset XYZ trong entity (0 = chưa biết)
    uintptr_t m_inGameAddr = 0;
    uintptr_t m_entityMapOffset = 0;
    uintptr_t m_terrainAddr = 0;
    memory::NativeTerrainData m_nativeTerrain;

    // Trạng thái chuyển động thực tế (movementFlags)
    bool m_hasMovementIntent = false;
    float m_lastPosX = 0.0f;
    float m_lastPosY = 0.0f;
    bool m_inGracePeriod = true;
    uint64_t m_lastRollMs = 0;
};