// ==========================================================
// Division 2: Perception, Spatial NavMesh Bitset & SIMD Scanner Tests
// ==========================================================

#include "test_harness.hpp"
#include "navigation/navmesh_bitset_fusion.hpp"
#include "memory/simd_signature_scanner.hpp"
#include "looting/loot_density_clusterer.hpp"
#include "common/protocol.hpp"

#include <chrono>
#include <vector>
#include <cmath>

void TestNavMeshBitsetFusion() {
    std::cout << "[Test 98] NavMesh Bitset Fusion & Sub-Millisecond Global Routing..." << std::endl;

    // 1. Tạo NativeTerrainData giả lập một bản đồ kích thước 150x150 ô (~1630x1630 units)
    memory::NativeTerrainData terrain;
    terrain.cols = 150;
    terrain.rows = 150;
    terrain.bytesPerRow = 150;
    terrain.gridToWorldScale = 10.875f;
    terrain.worldOriginX = 0.0f;
    terrain.worldOriginY = 0.0f;
    terrain.walkability.assign(150 * 150, 1); // Ban đầu toàn bộ là đất đi được

    // Dựng một bức tường thẳng đứng ngăn đôi bản đồ từ y=20 đến y=130 tại x=75, chừa 1 khe cửa tại y=75
    for (uint32_t y = 20; y <= 130; ++y) {
        if (y != 75) {
            terrain.walkability[y * 150 + 75] = 0; // Tường
        }
    }

    navigation::NavMeshBitsetFusion fusion;
    bool initOk = fusion.InitializeFromNativeTerrain(terrain);
    CHECK(initOk, "InitializeFromNativeTerrain thanh cong");
    CHECK(fusion.Cols() == 150, "Cols phai la 150");
    CHECK(fusion.Rows() == 150, "Rows phai la 150");
    CHECK(fusion.IsWalkable(10, 10), "O (10, 10) phai di duoc");
    CHECK(!fusion.IsWalkable(75, 50), "O tren tuong (75, 50) phai bi chan");
    CHECK(fusion.IsWalkable(75, 75), "Khe cua (75, 75) phai di duoc");

    // 2. Kiểm thử Fog of War và Frontier Extraction
    CHECK(!fusion.IsExplored(10, 10), "Ban dau chua co suong mu duoc mo");
    fusion.RevealFogAroundWorldPos(10.0f * 10.875f, 10.0f * 10.875f, 80.0f);
    CHECK(fusion.IsExplored(10, 10), "Sau khi reveal, o (10, 10) phai da duoc mo");
    CHECK(fusion.ExplorationProgress() > 0.0f, "Tien trinh kham pha phai > 0");

    auto frontiers = fusion.ExtractFrontierCells(64);
    CHECK(!frontiers.empty(), "Phai trich xuat duoc cac o bien Frontier tiep giap suong mu");

    navigation::Vec2 bestTarget{ 0.0f, 0.0f };
    float utilityScore = 0.0f;
    bool hasTarget = fusion.FindBestFrontierTarget({ 10.0f * 10.875f, 10.0f * 10.875f }, bestTarget, &utilityScore);
    CHECK(hasTarget, "Phai tim duoc muc tieu Frontier tiep theo");
    CHECK(utilityScore > 0.0f, "Diem tien ich Frontier phai duong");

    // 3. Đo đạc tốc độ A* / JPS trên Bitset xuyên suốt bản đồ (Start ở nửa trái x=20, Goal ở nửa phải x=120)
    // Đường đi bắt buộc phải lượn qua khe cửa (75, 75)
    navigation::Vec2 startWorld{ 20.0f * 10.875f, 50.0f * 10.875f };
    navigation::Vec2 goalWorld{ 120.0f * 10.875f, 50.0f * 10.875f };

    float computeTimeMs = 0.0f;
    auto path = fusion.FindGlobalPath(startWorld, goalWorld, &computeTimeMs);

    CHECK(!path.empty(), "Phai tim duoc duong di toan ban do vuot qua khe cua");
    CHECK(computeTimeMs < 1.0f, "Thoi gian tinh toan duong di tren Bitset phai < 1.0 ms");
    std::cout << "  -> Bitset Global Path Length: " << path.size() 
              << " waypoints | Compute Time: " << computeTimeMs << " ms" << std::endl;

    std::cout << "  -> NavMesh Bitset Fusion OK" << std::endl;
}

void TestSimdSignatureScanner() {
    std::cout << "[Test 99] SIMD AVX2 Signature Scanner & 16MB Float Calibration..." << std::endl;

    bool avx2 = memory::SimdSignatureScanner::IsAvx2Supported();
    std::cout << "  -> CPU AVX2 Support: " << (avx2 ? "YES (Hardware Accelerated)" : "NO (Scalar Fallback)") << std::endl;

    // 1. Kiểm thử AOB Pattern Matching bằng AVX2
    std::vector<uint8_t> dummyCode(1024 * 1024, 0x90); // 1 MB NOPs
    // Đặt pattern "48 8B 0D ? ? ? ? F3 0F 11 05" tại vị trí 543,210
    const size_t targetOffset = 543210;
    dummyCode[targetOffset + 0] = 0x48;
    dummyCode[targetOffset + 1] = 0x8B;
    dummyCode[targetOffset + 2] = 0x0D;
    dummyCode[targetOffset + 3] = 0xAA; // Wildcard
    dummyCode[targetOffset + 4] = 0xBB; // Wildcard
    dummyCode[targetOffset + 5] = 0xCC; // Wildcard
    dummyCode[targetOffset + 6] = 0xDD; // Wildcard
    dummyCode[targetOffset + 7] = 0xF3;
    dummyCode[targetOffset + 8] = 0x0F;
    dummyCode[targetOffset + 9] = 0x11;
    dummyCode[targetOffset + 10] = 0x05;

    auto pat = AobPattern::Parse("48 8B 0D ? ? ? ? F3 0F 11 05");
    CHECK(pat.Valid(), "Pattern phai hop le");

    intptr_t hit = memory::SimdSignatureScanner::FindPatternAVX2(dummyCode.data(), dummyCode.size(), pat);
    CHECK(hit == static_cast<intptr_t>(targetOffset), "AVX2 phai tim chinh xac offset 543210");

    // 2. Kiểm thử quét cửa sổ Float 16MB PlayerFinder (< 3.0 ms)
    // 16 MB = 4,194,304 floats
    const size_t numFloats = (16 * 1024 * 1024) / sizeof(float);
    std::vector<float> floatBuffer(numFloats, 99999.0f); // Default ngoai khoang [-150, 100]

    // Chèn 5 cặp XYZ hợp lệ tại các vị trí ngẫu nhiên
    floatBuffer[100000] = 45.5f;
    floatBuffer[500000] = -120.0f;
    floatBuffer[1200000] = 88.2f;
    floatBuffer[2500000] = 15.0f;
    floatBuffer[3900000] = -64.7f;

    std::vector<std::pair<uintptr_t, float>> foundFloats;
    foundFloats.reserve(128);
    const auto t0 = std::chrono::high_resolution_clock::now();
    size_t count = memory::SimdSignatureScanner::ScanFloatRangeAVX2(
        reinterpret_cast<const uint8_t*>(floatBuffer.data()),
        floatBuffer.size() * sizeof(float),
        0x10000000,
        -150.0f,
        100.0f,
        foundFloats
    );
    const auto t1 = std::chrono::high_resolution_clock::now();
    const float durationMs = std::chrono::duration<float, std::milli>(t1 - t0).count();

    CHECK(count >= 5, "Phai tim thay it nhat 5 float da chen");
    CHECK(durationMs < 6.0f, "Thoi gian quet Float 16MB bang AVX2 phai < 6.0 ms (chống nhiễu do tải CPU song song)");
    std::cout << "  -> 16MB Float AVX2 Scan: " << count << " candidates found in "
              << durationMs << " ms (Limit: < 6.0 ms)" << std::endl;

    // 3. Kiểm thử Batch Scanner cho 8 Static Roots (< 3.0 ms)
    std::vector<memory::SignatureRoot> roots = {
        { "GameLayout", AobPattern::Parse("48 8B 0D ? ? ? ? 48 85 C9 74 ? 48 8B 01"), 3, 0 },
        { "AreaInstance", AobPattern::Parse("48 8B 05 ? ? ? ? 48 8B 88 ? ? 00 00"), 3, 0 },
        { "EntityList", AobPattern::Parse("48 89 05 ? ? ? ? 48 85 C0 74 ? 48 8B 50"), 3, 0 },
        { "CameraManager", AobPattern::Parse("48 8B 05 ? ? ? ? 0F 28 05 ? ? ? ?"), 3, 0 },
        { "InGameState", AobPattern::Parse("48 8B 05 ? ? ? ? 48 8B 48 08 48 85 C9"), 3, 0 },
        { "FileController", AobPattern::Parse("48 8B 0D ? ? ? ? E8 ? ? ? ? 48 8B C8"), 3, 0 },
        { "TerrainManager", AobPattern::Parse("48 8B 15 ? ? ? ? 48 85 D2 74 ? 48 8B 02"), 3, 0 },
        { "UIElements", AobPattern::Parse("48 8B 0D ? ? ? ? 48 8B 01 FF 50 ? 48 8B F8"), 3, 0 }
    };

    std::vector<uint8_t> peCode(4 * 1024 * 1024, 0xCC); // 4 MB .text section giả lập
    // Cài đặt 8 patterns vào các vị trí khác nhau
    for (size_t r = 0; r < roots.size(); ++r) {
        const size_t pos = 200000 * (r + 1);
        const auto& p = roots[r].pattern;
        for (size_t b = 0; b < p.Size(); ++b) {
            peCode[pos + b] = p.wildcard[b] ? 0x12 : p.bytes[b];
        }
        *reinterpret_cast<int32_t*>(&peCode[pos + roots[r].ripOffset]) = static_cast<int32_t>(0x1000 * (r + 1));
    }

    float batchDurationMs = 0.0f;
    bool allResolved = memory::SimdSignatureScanner::ScanStaticRootsBatched(
        peCode.data(),
        peCode.size(),
        0x140000000,
        roots,
        &batchDurationMs
    );

    CHECK(allResolved, "Toan bo 8 static roots phai duoc giai ma thanh cong");
    CHECK(batchDurationMs < 3.0f, "Thoi gian quet 8 static roots phai < 3.0 ms");
    std::cout << "  -> 8 Static Roots Batched Scan Duration: " << batchDurationMs << " ms" << std::endl;

    std::cout << "  -> SIMD AVX2 Signature Scanner OK" << std::endl;
}

void TestLootDensityClustering() {
    std::cout << "[Test 100] Dynamic Loot Value Density Clustering & Anti-Backtracking..." << std::endl;

    looting::LootClusterConfig config;
    config.clusterRadius = 25.0f;
    config.playerMovementSpeed = 38.0f;
    config.minDensityThreshold = 0.35f;   // 0.35 Chaos / sec
    config.backtrackPenaltyFactor = 2.5f; // Phạt nặng nếu đi giật lùi
    config.minBacktrackChaosValue = 1.0f; // Nếu đi ngược đường, tổng giá trị phải >= 1.0 Chaos

    looting::LootDensityClusterer clusterer(config);

    // Xây dựng TelemetryPacket giả lập với các vật phẩm thực tế
    TelemetryPacket packet{};
    packet.snapshotId = 1;
    packet.timestamp = 1788993806553;
    packet.entityCount = 6;

    // Item 0: Divine Orb (150c) ở phía trước theo hướng tiến trình (x=30, y=30)
    packet.entities[0].id = 101;
    packet.entities[0].type = 2; // ItemDrop
    packet.entities[0].posX = 30.0f;
    packet.entities[0].posY = 30.0f;
    strcpy_s(packet.entities[0].name, sizeof(packet.entities[0].name), "Divine Orb");

    // Item 1: Chaos Orb (1c) ở gần Divine Orb (x=35, y=32) -> Cùng cụm phía trước
    packet.entities[1].id = 102;
    packet.entities[1].type = 2;
    packet.entities[1].posX = 35.0f;
    packet.entities[1].posY = 32.0f;
    strcpy_s(packet.entities[1].name, sizeof(packet.entities[1].name), "Chaos Orb");

    // Item 2: Scroll of Wisdom (0.005c) rơi rớt ở phía sau lưng (x=-70, y=-70)
    packet.entities[2].id = 103;
    packet.entities[2].type = 2;
    packet.entities[2].posX = -70.0f;
    packet.entities[2].posY = -70.0f;
    strcpy_s(packet.entities[2].name, sizeof(packet.entities[2].name), "Scroll of Wisdom");

    // Item 3: White Boots (0.001c) rơi rớt ở phía sau lưng (x=-75, y=-72) -> Cùng cụm rác phía sau
    packet.entities[3].id = 104;
    packet.entities[3].type = 2;
    packet.entities[3].posX = -75.0f;
    packet.entities[3].posY = -72.0f;
    strcpy_s(packet.entities[3].name, sizeof(packet.entities[3].name), "Iron Greaves");

    // Item 4: Waystone Tier 15 (Critical, 2.5c, Priority 92) ở phía sau lưng (x=-50, y=-50)
    packet.entities[4].id = 105;
    packet.entities[4].type = 2;
    packet.entities[4].posX = -50.0f;
    packet.entities[4].posY = -50.0f;
    strcpy_s(packet.entities[4].name, sizeof(packet.entities[4].name), "Waystone (Tier 15)");

    // Item 5: Monster (Type 1, không được gom vào loot)
    packet.entities[5].id = 999;
    packet.entities[5].type = 1;

    // Nhân vật đang ở (0, 0), đang di chuyển về phía trước theo hướng (1.0, 1.0) (Frontier Vector)
    navigation::Vec2 playerPos{ 0.0f, 0.0f };
    navigation::Vec2 mapProgDir{ 1.0f, 1.0f };
    std::unordered_set<uint32_t> ignored;

    auto clusters = clusterer.ClusterAndEvaluate(packet, playerPos, mapProgDir, ignored);
    CHECK(clusters.size() >= 3, "Phai tao thanh it nhat 3 cum vat pham rieng biet");

    const looting::LootCluster* forwardCluster = nullptr;
    const looting::LootCluster* trashBacktrackCluster = nullptr;
    const looting::LootCluster* criticalBacktrackCluster = nullptr;

    for (const auto& cl : clusters) {
        for (uint32_t id : cl.entityIds) {
            if (id == 101) forwardCluster = &cl;
            if (id == 103) trashBacktrackCluster = &cl;
            if (id == 105) criticalBacktrackCluster = &cl;
        }
    }

    CHECK(forwardCluster != nullptr, "Phai tim thay cum Divine phia truoc");
    CHECK(trashBacktrackCluster != nullptr, "Phai tim thay cum rac phia sau");
    CHECK(criticalBacktrackCluster != nullptr, "Phai tim thay cum Waystone T15 phia sau");

    // 1. Cụm phía trước (Divine + Chaos): Cùng hướng tiến trình, giá trị cao -> BẮT BUỘC NHẶT
    CHECK(!forwardCluster->isBacktracking, "Cum Divine phia truoc khong phai backtracking");
    CHECK(forwardCluster->shouldPickup, "Cum Divine phia truoc phai duoc chap nhan nhat");
    CHECK(forwardCluster->valueDensity > 50.0f, "Mat do gia tri cum Divine phai rat cao (>50 c/s)");

    // 2. Cụm rác phía sau (Scroll + Greaves): Đi ngược đường, giá trị cực thấp (< 0.01c) -> TỪ CHỐI NHẶT!
    CHECK(trashBacktrackCluster->isBacktracking, "Cum rac phia sau phai duoc danh dau la backtracking");
    CHECK(!trashBacktrackCluster->shouldPickup, "Cum rac nguoc duong TUYET DOI KHONG DUOC NHAT (Chong quay dau)");
    CHECK(trashBacktrackCluster->valueDensity < 0.01f, "Mat do gia tri cum rac phai cuc thap");

    // 3. Cụm Waystone Tier 15: Mặc dù đi ngược đường, nhưng có item Priority 92 (Critical Item) -> BẮT BUỘC NHẶT
    CHECK(criticalBacktrackCluster->isBacktracking, "Cum Waystone T15 o phia sau la backtracking");
    CHECK(criticalBacktrackCluster->hasCriticalItem, "Cum phai chua item critical (Waystone T15)");
    CHECK(criticalBacktrackCluster->shouldPickup, "Item critical du nguoc duong van phai duoc bao ve nhat");

    // 4. Chọn cụm tốt nhất
    const auto* bestCl = clusterer.SelectBestCluster(clusters);
    CHECK(bestCl != nullptr, "Phai chon duoc cum tot nhat");
    CHECK(bestCl == forwardCluster, "Cum tot nhat phai la cum Divine phia truoc");

    // 5. Kiểm thử thứ tự nhặt trong cụm
    auto order = clusterer.OrderItemsWithinCluster(*forwardCluster, packet, playerPos);
    CHECK(order.size() == 2, "Thu tu nhat phai du 2 item trong cum Divine");
    CHECK(order[0] == 101, "Item gan nhat phai duoc nhat truoc");

    std::cout << "  -> Dynamic Loot Value Density Clustering OK" << std::endl;
}
