// ==========================================================
// AutoPOE2 Core Engine - Unit Tests
// Domain 1 & 3: Predictive Aiming, Quintic Bezier & Seqlock Latency
// (Sư đoàn 1: Hot Path 120Hz Commercial Grade Upgrade)
// ==========================================================

#include "test_harness.hpp"
#include "combat/predictive_aim_controller.hpp"
#include "input/quintic_bezier_controller.hpp"
#include "input/humanized_input_arbiter.hpp"
#include "combat/reflex_manager.hpp"
#include "ipc/shared_memory.hpp"
#include "navigation/terrain_grid.hpp"
#include "input/non_blocking_actuator.hpp"

#include <chrono>
#include <cmath>
#include <iostream>
#include <thread>
#include <vector>

// ==========================================================
// 1. Kiểm thử ước lượng vận tốc OLS và giải nghiệm đạn đạo
// ==========================================================
void TestPredictiveAimController() {
    std::cout << "[Test 98] Predictive Lead-Aiming & Ballistic Compensation (C++23)..." << std::endl;

    combat::PredictiveAimController aimCtrl;
    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;

    // Thiết lập mục tiêu di chuyển cơ động cao (Quái Rhoa sạc tốc độ 240 units/s theo hướng 45 độ)
    // Vx = 240 / sqrt(2) ~ 169.7056 u/s, Vy = 169.7056 u/s
    const float trueVx = 169.7056f;
    const float trueVy = 169.7056f;
    const uint32_t monsterId = 404;

    uint64_t nowMs = 100000;
    const float startX = 1200.0f;
    const float startY = 1200.0f;

    // Giả lập 8 tick 120Hz (mỗi tick ~8.33ms)
    for (int tick = 0; tick < 8; ++tick) {
        const float t = static_cast<float>(tick) * 0.008333f;
        packet.entityCount = 1;
        auto& ent = packet.entities[0];
        ent.id = monsterId;
        ent.type = 1; // Monster
        ent.currentHP = 5000;
        ent.maxHP = 5000;
        ent.posX = startX + trueVx * t;
        ent.posY = startY + trueVy * t;
        ent.extraFlags = 0;

        aimCtrl.UpdateEntityTracking(packet, nowMs);
        nowMs += 8; // 8.33ms làm tròn
    }

    // Kiểm tra ước lượng vận tốc bằng Ordinary Least Squares
    float estVx = 0.0f, estVy = 0.0f, estSpeed = 0.0f;
    const bool velOk = aimCtrl.GetEntityVelocity(monsterId, estVx, estVy, estSpeed);
    CHECK(velOk, "Phải tìm thấy thực thể được theo dõi trong bảng kinematic");

    // Dung sai hồi quy < 3% do làm tròn timestamp nguyên ms
    const float errVx = std::fabs(estVx - trueVx) / trueVx;
    const float errVy = std::fabs(estVy - trueVy) / trueVy;
    CHECK(errVx < 0.05f, "Ước lượng vận tốc Vx bằng OLS phải đạt sai số < 5%");
    CHECK(errVy < 0.05f, "Ước lượng vận tốc Vy bằng OLS phải đạt sai số < 5%");

    // 1b. Nghiệm đón đầu đòn tức thời / Melee (Ice Strike Monk)
    // Đòn đánh có windup = 110ms, đạn đạo tức thời (Vp = 0)
    const auto iceStrikeParams = combat::PredictiveAimController::GetMonkIceStrikeParams();
    Vector2D playerPos{ packet.player.posX, packet.player.posY };
    auto iceSol = aimCtrl.SolveAim(monsterId, playerPos, iceStrikeParams);

    CHECK(iceSol.valid, "Nghiệm Ice Strike đón đầu phải hợp lệ");
    CHECK(iceSol.flightTime == 0.0f, "Đòn cận chiến/slam không có thời gian bay (flightTime = 0)");
    CHECK(iceSol.timeToImpact == iceStrikeParams.windupSeconds, "Thời gian va chạm bằng đúng windup");

    // Vị trí dự đoán phải dịch chuyển theo vận tốc trong thời gian windup
    const float expectedLeadDist = estSpeed * iceStrikeParams.windupSeconds;
    CHECK(std::fabs(iceSol.leadDistance - expectedLeadDist) < 1.0f, "Lead distance phải khớp v * windup");

    // 1c. Nghiệm đón đầu đạn đạo sóng sấm sét (Charged Staff Shockwave)
    // Vp = 900 u/s, Tw = 140ms, thiết lập maxRange = 600u để kiểm tra nghiệm giao cắt không bị kẹp
    auto waveParams = combat::PredictiveAimController::GetMonkChargedStaffWaveParams();
    waveParams.maxRange = 600.0f;
    auto waveSol = aimCtrl.SolveAim(monsterId, playerPos, waveParams);

    CHECK(waveSol.valid, "Nghiệm sóng xung kích Charged Staff phải hợp lệ");
    CHECK(waveSol.flightTime > 0.0f, "Sóng xung kích có thời gian bay dương");

    // Xác thực định lý đạn đạo: Tại thời điểm va chạm (impact = Tw + tf),
    // cự ly từ người chơi đến vị trí dự đoán quái vật phải bằng đúng quãng đường đạn bay (Vp * tf)
    const float aimDx = waveSol.aimTargetX - playerPos.x;
    const float aimDy = waveSol.aimTargetY - playerPos.y;
    const float distShooterToIntercept = std::sqrt(aimDx * aimDx + aimDy * aimDy);
    const float projectileTravelDist = waveParams.projectileSpeed * waveSol.flightTime;

    CHECK(std::fabs(distShooterToIntercept - projectileTravelDist) < 2.0f,
          "Tọa độ đón đầu phải thỏa mãn phương trình giao cắt đạn đạo chính xác");

    // 1d. Kiểm tra tính năng kẹp cự ly tối đa (Max Range Clamping) khi quái vượt tầm chiêu
    waveParams.maxRange = 350.0f;
    auto clampedSol = aimCtrl.SolveAim(monsterId, playerPos, waveParams);
    CHECK(clampedSol.valid, "Nghiệm sau khi kẹp tầm đánh vẫn hợp lệ");
    const float clampedDist = std::sqrt((clampedSol.aimTargetX - playerPos.x) * (clampedSol.aimTargetX - playerPos.x) +
                                        (clampedSol.aimTargetY - playerPos.y) * (clampedSol.aimTargetY - playerPos.y));
    CHECK(std::fabs(clampedDist - 350.0f) < 1.0f, "Cự ly ngắm phải bị kẹp chính xác ở maxRange 350u");

    std::cout << "  -> Lead distance: " << waveSol.leadDistance << "u, Time to impact: "
              << (waveSol.timeToImpact * 1000.0f) << "ms (Windup: " << (waveParams.windupSeconds * 1000.0f)
              << "ms + Flight: " << (waveSol.flightTime * 1000.0f) << "ms) OK" << std::endl;
}

// ==========================================================
// 2. Kiểm thử chuột Quintic Bézier, Flash-Hogan Minimum Jerk & Perlin
// ==========================================================
void TestQuinticBezierAndHumanizedMouse() {
    std::cout << "[Test 99] Quintic Bézier (5-degree) & Biomechanical Micro-Tremor..." << std::endl;

    input::QuinticBezierController bezierCtrl;

    // 2a. Xác thực hàm Minimum Jerk Flash & Hogan bậc 5: s(tau) = 10*tau^3 - 15*tau^4 + 6*tau^5
    CHECK(input::QuinticBezierController::EvaluateMinimumJerk(0.0f) == 0.0f, "s(0) phải bằng 0");
    CHECK(input::QuinticBezierController::EvaluateMinimumJerk(1.0f) == 1.0f, "s(1) phải bằng 1");
    CHECK(std::fabs(input::QuinticBezierController::EvaluateMinimumJerk(0.5f) - 0.5f) < 1e-5f, "s(0.5) phải bằng 0.5 đối xứng");

    // Kiểm tra tính đơn điệu tăng ngặt của s(tau)
    bool monotonicOk = true;
    float prevS = 0.0f;
    for (int i = 1; i <= 100; ++i) {
        const float tau = static_cast<float>(i) / 100.0f;
        const float curS = input::QuinticBezierController::EvaluateMinimumJerk(tau);
        if (curS <= prevS) {
            monotonicOk = false;
            break;
        }
        prevS = curS;
    }
    CHECK(monotonicOk, "Flash & Hogan minimum jerk phải đơn điệu tăng ngặt");

    // 2b. Kiểm tra tính liên tục của hàm nhiễu Perlin 1 chiều
    bool perlinBounded = true;
    for (int i = 0; i < 200; ++i) {
        const float x = static_cast<float>(i) * 0.15f;
        const float noise = input::QuinticBezierController::PerlinNoise1D(x);
        if (noise < -1.0f || noise > 1.0f) {
            perlinBounded = false;
            break;
        }
    }
    CHECK(perlinBounded, "Nhiễu Perlin 1D phải nằm chặt trong biên [-1.0, 1.0]");

    // 2c. Sinh quỹ đạo di chuyển chuột trên buffer tĩnh (Zero Heap) và kiểm tra hội tụ
    common::TrajectoryBuffer trajBuf;
    const Vector2D startPt{ 300.0f, 250.0f };
    const Vector2D targetPt{ 960.0f, 540.0f };

    bezierCtrl.GenerateTrajectory(startPt, targetPt, trajBuf, 20);

    CHECK(!trajBuf.empty(), "Trajectory buffer không được rỗng");
    CHECK(trajBuf.size() <= 32, "Kích thước quỹ đạo phải nằm trong dung lượng tĩnh 32 phần tử");

    // Tổng tất cả các bước (dx, dy) phải đưa con trỏ chuột đến đúng pixel đích
    float totalDx = 0.0f;
    float totalDy = 0.0f;
    for (size_t i = 0; i < trajBuf.size(); ++i) {
        totalDx += trajBuf[i].x;
        totalDy += trajBuf[i].y;
    }

    const float expDx = targetPt.x - startPt.x;
    const float expDy = targetPt.y - startPt.y;

    CHECK(std::fabs(totalDx - expDx) < 1.0f, "Tổng bước di chuyển chuột X phải trùng khớp tuyệt đối đích đến");
    CHECK(std::fabs(totalDy - expDy) < 1.0f, "Tổng bước di chuyển chuột Y phải trùng khớp tuyệt đối đích đến");

    // 2d. Dwell time sinh học nằm trong phân phối người thật [35, 250] ms
    const int dwell = bezierCtrl.GenerateDwellTimeMs(65.0f, 12.0f);
    CHECK(dwell >= 35 && dwell <= 250, "Dwell time phải nằm trong giới hạn sinh học an toàn");

    std::cout << "  -> Quintic Bézier trajectory: " << trajBuf.size()
              << " steps, Exact Delta: (" << totalDx << ", " << totalDy << ") pixels OK" << std::endl;
}

// ==========================================================
// 3. Kiểm thử Seqlock IPC & Độ trễ Phản xạ Sinh tồn (< 25µs) trong Breach/Delirium
// ==========================================================
void TestSeqlockBreachDeliriumStressAndLatency() {
    std::cout << "[Test 100] Seqlock IPC Stress & Survival Reflex Latency (<25µs in Breach/Delirium)..." << std::endl;

    // 3a. Giả lập mật độ quái cực đại trong sự kiện Breach / Delirium: 256 thực thể
    TelemetryPacket breachPacket{};
    breachPacket.snapshotId = 1001;
    breachPacket.timestamp = SharedMemoryManager::NowMs();
    breachPacket.player.maxHP = 1; // Monk CI
    breachPacket.player.currentHP = 1;
    breachPacket.player.maxES = 4500;
    breachPacket.player.currentES = 3100; // ES tụt còn ~68% -> kích hoạt phản xạ né
    breachPacket.player.posX = 500.0f;
    breachPacket.player.posY = 500.0f;

    breachPacket.entityCount = MAX_ENTITIES_PER_SNAPSHOT; // 256 thực thể
    for (uint32_t i = 0; i < MAX_ENTITIES_PER_SNAPSHOT; ++i) {
        auto& ent = breachPacket.entities[i];
        ent.id = 1000 + i;
        ent.type = 1; // Monster
        ent.currentHP = 2000;
        ent.maxHP = 2000;
        ent.posX = 500.0f + static_cast<float>((i % 16) - 8) * 15.0f;
        ent.posY = 500.0f + static_cast<float>((i / 16) - 8) * 15.0f;
        ent.distanceToPlayer = std::sqrt((ent.posX - 500.0f) * (ent.posX - 500.0f) + (ent.posY - 500.0f) * (ent.posY - 500.0f));
        ent.extraFlags = 0;
        if (i == 42) {
            // Boss thi triển đòn nguy hiểm Telegraph (Animation ID 120)
            ent.rarity = 3;
            ent.extraFlags |= 1; // Boss
            ent.currentAnimationId = 120;
        }
    }

    ReflexConfig reflexCfg{};
    reflexCfg.dodgeEnabled = true;
    reflexCfg.ciEvasionEnabled = true;
    reflexCfg.ciDodgeEsThreshold = 0.75f; // ES < 75% -> né
    reflexCfg.telegraphEvasionEnabled = true;
    reflexCfg.humanReactionEnabled = false; // Phản xạ máy tính tức thời trên Hot Path
    ReflexManager reflexMgr(reflexCfg);

    KMBoxNet kmbox;
    kmbox.Connect("127.0.0.1", 28888, 0x12345678);
    kmbox.SetIgnoreWindowFocus(true);
    kmbox.SetRateLimitEnabled(false);
    input::NonBlockingActuator actuator(kmbox);
    reflexMgr.SetActuator(&actuator);

    // Đo thời gian thực thi của phản xạ sinh tồn (Survival Reflex Latency trên Hot Path 120Hz)
    constexpr int kIterations = 1000;
    const auto tStart = std::chrono::high_resolution_clock::now();

    for (int iter = 0; iter < kIterations; ++iter) {
        const uint64_t tickNowMs = 120000 + iter * 8;
        reflexMgr.Update(breachPacket, kmbox, tickNowMs);
        actuator.ProcessPendingReleases(tickNowMs);
    }

    const auto tEnd = std::chrono::high_resolution_clock::now();
    const double totalElapsedUs = std::chrono::duration<double, std::micro>(tEnd - tStart).count();
    const double avgLatencyUs = totalElapsedUs / static_cast<double>(kIterations);

    std::cout << "  -> Survival Reflex latency under 256 entities: "
              << avgLatencyUs << " µs/tick (Ngưỡng yêu cầu: < 25.0 µs)" << std::endl;

    CHECK(avgLatencyUs < 25.0, "Độ trễ phản xạ sinh tồn trong mật độ quái 256 phải duy trì < 25µs (Zero Heap)");
    CHECK(reflexMgr.IsCiRecoveryMode(), "Phải nhận diện và kích hoạt CI Recovery Mode khi ES < 75%");

    // 3b. Kiểm tra Seqlock double-buffer ghi snapshot 39KB không torn-read
    SharedMemoryManager shmServer;
    const std::string shmName = TestName("Local\\Test_Seqlock_Breach_Delirium");
    const bool srvInit = shmServer.Initialize(true, shmName.c_str());
    CHECK(srvInit, "Khởi tạo SHM Server thành công");

    SharedMemoryManager shmClient;
    const bool cliInit = shmClient.Initialize(false, shmName.c_str());
    CHECK(cliInit, "Khởi tạo SHM Client thành công");

    // Đo thời gian ghi Seqlock snapshot 39KB
    const auto shmStart = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < 200; ++i) {
        breachPacket.snapshotId = 2000 + i;
        shmServer.WriteTelemetry(breachPacket);
    }
    const auto shmEnd = std::chrono::high_resolution_clock::now();
    const double shmElapsedUs = std::chrono::duration<double, std::micro>(shmEnd - shmStart).count() / 200.0;

    std::cout << "  -> Seqlock 39KB snapshot publish latency: "
              << shmElapsedUs << " µs/publish (Lock-Free Double-Buffer)" << std::endl;

    TelemetryPacket readPacket{};
    const bool readOk = shmClient.ReadTelemetry(readPacket);
    CHECK(readOk, "Client phải đọc được snapshot Seqlock không torn-read");
    CHECK(readPacket.snapshotId >= 2000, "Snapshot ID phải khớp với gói tin mới nhất");
    CHECK(readPacket.entityCount == 256, "Dữ liệu 256 thực thể phải nguyên vẹn qua IPC");

    shmClient.Shutdown();
    shmServer.Shutdown();
}

// ==========================================================
// 4. Kiểm thử HumanizedInputArbiter & Triệt tiêu gia tốc giật (Jerk Analysis)
// ==========================================================
void TestHumanizedInputArbiterAndJerkMinimization() {
    std::cout << "[Test 104] HumanizedInputArbiter & Quintic Minimum Jerk Verification..." << std::endl;

    input::HumanizedInputArbiter arbiter;
    CHECK(arbiter.Mode() == input::MouseTrajectoryMode::QuinticBezierStealth, "Mặc định phải là QuinticBezierStealth");

    // 4a. Kiểm thử chuyển đổi chế độ động (Runtime Mode Switching)
    arbiter.SetMode(input::MouseTrajectoryMode::CubicBezierClassic);
    CHECK(arbiter.Mode() == input::MouseTrajectoryMode::CubicBezierClassic, "Chuyển sang CubicBezierClassic");

    common::TrajectoryBuffer cubicBuf;
    const Vector2D startPt{ 200.0f, 200.0f };
    const Vector2D targetPt{ 800.0f, 600.0f };
    arbiter.GenerateTrajectory(startPt, targetPt, cubicBuf, 25);
    CHECK(!cubicBuf.empty(), "Cubic trajectory không được rỗng");

    arbiter.SetMode(input::MouseTrajectoryMode::QuinticBezierStealth);
    CHECK(arbiter.Mode() == input::MouseTrajectoryMode::QuinticBezierStealth, "Chuyển sang QuinticBezierStealth");

    common::TrajectoryBuffer quinticBuf;
    arbiter.GenerateTrajectory(startPt, targetPt, quinticBuf, 25);
    CHECK(!quinticBuf.empty(), "Quintic trajectory không được rỗng");

    // 4b. Phân tích động học vi phân và kiểm chứng tính triệt tiêu giật (Jerk Analysis)
    const auto profile = input::HumanizedInputArbiter::AnalyzeTrajectory(quinticBuf, 0.008333f);
    CHECK(profile.isJerkBounded, "Gia tốc giật Jerk phải nằm chặt dưới ngưỡng cho phép (< 2.5e7 px/s^3)");
    CHECK(profile.maxVelocity > 0.0f, "Vận tốc đỉnh phải > 0");
    CHECK(profile.maxAcceleration > 0.0f, "Gia tốc đỉnh phải > 0");
    std::cout << "  -> Quintic Trajectory Profile: Vmax = " << profile.maxVelocity << " px/s"
              << ", Amax = " << profile.maxAcceleration << " px/s^2"
              << ", Jmax = " << profile.maxJerk << " px/s^3"
              << ", Astart = " << profile.startAcceleration << " px/s^2"
              << ", Aend = " << profile.endAcceleration << " px/s^2" << std::endl;

    // 4c. Kiểm chứng tính êm dịu biên (Zero Acceleration at Start and Landing)
    // Flash & Hogan Minimum Jerk: a(0) ~ 0 và a(T) ~ 0 (chấp nhận sai số lượng tử hóa pixel rời rạc)
    CHECK(std::abs(profile.startAcceleration) < profile.maxAcceleration * 0.50f,
          "Gia tốc khởi hành phải êm dịu (nhỏ hơn 50% gia tốc đỉnh)");
    CHECK(std::abs(profile.endAcceleration) < profile.maxAcceleration * 0.60f,
          "Gia tốc tiếp đất phải êm dịu (nhỏ hơn 60% gia tốc đỉnh)");

    // 4d. Kiểm chứng khóa an toàn phần cứng (INV-FOCUS-INTERLOCK-HARDWARE-SAFETY)
    KMBoxNet kmbox;
    kmbox.SetForcedWindowFocus(0); // Cưỡng chế game KHÔNG có focus
    CHECK(!kmbox.IsGameWindowFocused(), "Game window phải báo un-focused");

    const bool moveResUnfocused = arbiter.MoveMouseSmooth(kmbox, 800, 600, 10, 0);
    CHECK(!moveResUnfocused, "TUYỆT ĐỐI CẤM di chuyển chuột khi game không có focus");

    const bool clickResUnfocused = arbiter.ClickMouseHumanized(kmbox, 1);
    CHECK(!clickResUnfocused, "TUYỆT ĐỐI CẤM click chuột khi game không có focus");

    // Khi game có focus, chấp hành bình thường
    kmbox.SetForcedWindowFocus(1);
    CHECK(kmbox.IsGameWindowFocused(), "Game window phải báo focused");
    const bool moveResFocused = arbiter.MoveMouseSmooth(kmbox, 800, 600, 10, 0);
    CHECK(moveResFocused, "Cho phép di chuyển chuột khi game có focus");

    // Phục hồi lại ignore window focus để không ảnh hưởng các test sau
    kmbox.SetIgnoreWindowFocus(true);

    std::cout << "  -> HumanizedInputArbiter & Quintic Minimum Jerk Verification OK" << std::endl;
}

