// ==========================================================
// AutoPOE2 Core Engine - Unit Tests
// Domain 3 & 5: Continuous Pack Clearing & Legitimate Map Clear V2
// (Sư đoàn 1: Hot Path 120Hz Commercial Grade Upgrade)
// ==========================================================

#include "test_harness.hpp"
#include "combat/monster_pack_clusterer.hpp"
#include "combat/combo_manager.hpp"
#include "brain/character_fsm.hpp"
#include "common/protocol.hpp"
#include "navigation/terrain_grid.hpp"

#include <chrono>
#include <cmath>
#include <iostream>

// ==========================================================
// 1. Kiểm thử Phân Cụm Bãi Quái (Monster Pack Clustering 120Hz DOD)
// ==========================================================
void TestMonsterPackClusteringAndContinuousClearing() {
    std::cout << "[Test 102] Monster Pack Clustering & Continuous Clearing (C++23 DOD)..." << std::endl;

    combat::MonsterPackClusterer clusterer;
    TelemetryPacket packet{};
    packet.player.posX = 1000.0f;
    packet.player.posY = 1000.0f;
    packet.player.currentHP = 1;
    packet.player.maxHP = 1;
    packet.player.currentES = 3500;
    packet.player.maxES = 3500;

    // Giả lập bãi quái 1 (Pack A): 5 quái thường tụ tập quanh (1100, 1000)
    // Cự ly ~ 100u so với người chơi
    for (int i = 0; i < 5; ++i) {
        auto& ent = packet.entities[packet.entityCount++];
        ent.id = 100 + i;
        ent.type = 1; // Monster
        ent.currentHP = 2000;
        ent.maxHP = 2000;
        ent.posX = 1100.0f + static_cast<float>(i * 5 - 10);
        ent.posY = 1000.0f + static_cast<float>(i * 4 - 8);
        ent.extraFlags = 0;
        ent.rarity = 0; // Normal
    }

    // Giả lập bãi quái 2 (Pack B): 3 quái có 1 quái Rare quanh (1000, 1250)
    // Cự ly ~ 250u so với người chơi
    for (int i = 0; i < 3; ++i) {
        auto& ent = packet.entities[packet.entityCount++];
        ent.id = 200 + i;
        ent.type = 1;
        ent.currentHP = 5000;
        ent.maxHP = 5000;
        ent.posX = 1000.0f + static_cast<float>(i * 8 - 8);
        ent.posY = 1250.0f + static_cast<float>(i * 6 - 6);
        ent.extraFlags = 0;
        ent.rarity = (i == 0) ? 2 : 0; // i=0 là Rare
    }

    // 1 con quái rác lẻ loi cách xa 300u (900, 700)
    {
        auto& ent = packet.entities[packet.entityCount++];
        ent.id = 301;
        ent.type = 1;
        ent.currentHP = 500;
        ent.maxHP = 500;
        ent.posX = 900.0f;
        ent.posY = 700.0f;
        ent.extraFlags = 0;
        ent.rarity = 0;
    }

    const auto result = clusterer.ClusterPacks(packet, nullptr);
    CHECK(result.clusterCount >= 2, "Phai gom duoc it nhat 2 cum bai quai");
    CHECK(result.totalMonstersNear == 9, "Tong so quai song xung quanh phai la 9");

    const auto* best = result.BestCluster();
    CHECK(best != nullptr, "Phai chon duoc cum quai toi uu nhat");
    if (best) {
        // Pack A ở cự ly ~100u có 5 quái, điểm số mật độ phải rất cao
        CHECK(best->monsterCount >= 3, "Cum toi uu phai co mat do cao (>= 3 quai)");
        CHECK(std::abs(best->centroidX - 1100.0f) < 20.0f, "Toa do trong tam Centroid X phai ~ 1100u");
        CHECK(std::abs(best->centroidY - 1000.0f) < 20.0f, "Toa do trong tam Centroid Y phai ~ 1000u");
        CHECK(best->distanceToPlayer < 120.0f, "Khoang cach toi tam cum phai < 120u");
    }

    // Kiểm thử hiệu năng Hot Path 120Hz (128 thực thể quái)
    TelemetryPacket heavyPacket{};
    heavyPacket.player.posX = 1000.0f;
    heavyPacket.player.posY = 1000.0f;
    for (uint32_t i = 0; i < 128; ++i) {
        auto& ent = heavyPacket.entities[i];
        ent.id = 1000 + i;
        ent.type = 1;
        ent.currentHP = 1000;
        ent.maxHP = 1000;
        ent.posX = 1000.0f + static_cast<float>((i % 16) * 20 - 150);
        ent.posY = 1000.0f + static_cast<float>((i / 16) * 20 - 150);
    }
    heavyPacket.entityCount = 128;

    const auto tStart = std::chrono::high_resolution_clock::now();
    for (int iter = 0; iter < 100; ++iter) {
        auto res = clusterer.ClusterPacks(heavyPacket, nullptr);
        (void)res;
    }
    const auto tEnd = std::chrono::high_resolution_clock::now();
    const double elapsedUs = std::chrono::duration<double, std::micro>(tEnd - tStart).count() / 100.0;
    std::cout << "  -> 128 Monsters Clustering Latency: " << elapsedUs << " us (Tieu chuan < 50 us)" << std::endl;
    CHECK(elapsedUs < 50.0, "ClusterPacks cho 128 quai phai duoi 50 microseconds");

    // Kiểm thử tích hợp ComboManager::Propose chọn bãi quái
    ComboConfig cfg;
    cfg.enabled = true;
    cfg.engageRange = 380.0f;
    cfg.monstersAttackRange = 150.0f;
    ComboManager comboMgr(cfg);

    ActionProposal prop = comboMgr.Propose(packet, 10000);
    CHECK(prop.kind == BotActionKind::Combat, "De xuat phai la Combat");
    CHECK(std::string(prop.priorityName).find("PACK") != std::string::npos, "Phai de xuat danh PACK quai");
    CHECK(std::abs(prop.targetX - 1100.0f) < 20.0f, "Muc tieu targetX phai huong vao trong tam cum (Centroid)");
    std::cout << "  -> Monster Pack Clustering & Centroid AoE Targeting OK" << std::endl;
}

// ==========================================================
// 2. Kiểm thử Tiêu Chí Hoàn Tất Map Chuẩn Thực Tế (INV-MAP-LEGITIMATE-CLEAR-V2)
// ==========================================================
void TestCharacterFsmMapClearLegitimacyV2() {
    std::cout << "[Test 103] CharacterFSM Map Clear Legitimacy V2 (Anti-Premature-Exit)..." << std::endl;

    CharacterFSM fsm;
    TelemetryPacket packet{};
    packet.area.areaSeed = 777;
    std::snprintf(packet.area.areaName, sizeof(packet.area.areaName), "MapWorldsBeach");
    packet.player.posX = 500.0f;
    packet.player.posY = 500.0f;
    packet.player.currentHP = 1;
    packet.player.maxHP = 1;

    uint64_t nowMs = 10000;
    fsm.Update(packet, nowMs); // Khởi tạo vào InMapExploring
    CHECK(fsm.State() == CharacterState::InMapExploring, "FSM phai vao InMapExploring");

    // TÌNH HUỐNG 1: Vừa vào map 20s, không thấy quái trong 30s (Trường hợp đánh 1 con quái xong đứng yên)
    // Trước đây: lỗi hệ thống kết luận map clear và thoát sớm!
    // Hiện tại: Bị chặn bởi Rào chắn 1 (Duration < 90s) và Rào chắn 2 (Coverage < 80%, Traverse < 1200u)
    nowMs += 35000; // Đã trôi qua 35s từ khi vào map
    fsm.Update(packet, nowMs);
    CHECK(!fsm.CheckMapCompleted(packet, nowMs), "TUYET DOI CAM thoat map khi moi vao 35s du vung yen ang");
    CHECK(fsm.State() == CharacterState::InMapExploring, "FSM phai tiep tuc InMapExploring");

    // TÌNH HUỐNG 2: Đã ở trong map 100s (> 90s), nhưng mới chỉ đứng yên tại chỗ (Traverse < 1200u, Coverage = 10%)
    nowMs = 10000 + 100000; // 110s
    fsm.SetHeatmapCoverage(0.10f);
    CHECK(!fsm.CheckMapCompleted(packet, nowMs), "TUYET DOI CAM thoat map khi do bao phu moi dat 10% du da o map 110s");

    // TÌNH HUỐNG 3: Map Boss bị tiêu diệt VÀ không còn quái nguy hiểm -> Cho phép hoàn tất!
    fsm.SetBossSlain(true);
    // Cho nhân vật di chuyển tích lũy quãng đường 1300u (10 bước 130u hợp lệ < 250u)
    for (int step = 0; step < 10; ++step) {
        packet.player.posX += 130.0f;
        fsm.Update(packet, nowMs);
    }
    CHECK(fsm.GetDistanceTraversed() >= 1200.0f, "Quang duong di chuyen phai duoc tich luy >= 1200u");
    CHECK(fsm.CheckMapCompleted(packet, nowMs), "Map phai duoc phep hoan tat khi Boss Slain + da tuan tra > 1200u sau 110s");

    // TÌNH HUỐNG 4: Chưa gặp Boss nhưng đã mở sương toàn diện (Coverage >= 85%) và khu vực yên ắng liên tục 30s
    CharacterFSM fsm2;
    fsm2.Update(packet, 10000);
    fsm2.SetHeatmapCoverage(0.88f);
    nowMs = 10000 + 95000; // 95s trong map
    // Cho nhân vật di chuyển 1400u
    packet.player.posX = 1900.0f;
    fsm2.Update(packet, nowMs);

    // Kích hoạt yên ắng liên tục 30s
    nowMs += 31000;
    fsm2.Update(packet, nowMs);
    CHECK(fsm2.CheckMapCompleted(packet, nowMs), "Map phai hoan tat khi Coverage >= 85% va da yen ang 30s");

    std::cout << "  -> CharacterFSM Map Clear Legitimacy V2 (INV-MAP-LEGITIMATE-CLEAR-V2) OK" << std::endl;
}
