#ifndef NOMINMAX
#define NOMINMAX
#endif

#include "combat/kiting_engine.hpp"

#include <cmath>
#include <algorithm>

namespace combat {

KitingEngine::KitingEngine(const KitingConfig& config)
    : m_config(config) {
}

bool KitingEngine::DetectWallNormal(
    float playerX,
    float playerY,
    const navigation::TerrainGrid& grid,
    float probeDist,
    float& outNormalX,
    float& outNormalY
) const {
    // 8 hướng cardinal + diagonal
    constexpr float kDirs[8][2] = {
        {  1.0f,  0.0f }, { -1.0f,  0.0f }, {  0.0f,  1.0f }, {  0.0f, -1.0f },
        {  0.7071f,  0.7071f }, { -0.7071f,  0.7071f },
        {  0.7071f, -0.7071f }, { -0.7071f, -0.7071f }
    };

    float blockedSumX = 0.0f;
    float blockedSumY = 0.0f;
    int blockedCount = 0;

    for (int i = 0; i < 8; ++i) {
        float sampleX = playerX + kDirs[i][0] * probeDist;
        float sampleY = playerY + kDirs[i][1] * probeDist;
        if (!grid.IsWalkable(sampleX, sampleY) || !grid.HasLineOfSight(playerX, playerY, sampleX, sampleY)) {
            blockedSumX += kDirs[i][0];
            blockedSumY += kDirs[i][1];
            ++blockedCount;
        }
    }

    if (blockedCount == 0) {
        return false;
    }

    float len = std::sqrt(blockedSumX * blockedSumX + blockedSumY * blockedSumY);
    if (len < 0.001f) {
        return false;
    }

    // Pháp tuyến chỉ vào không gian mở (hướng thoát ra khỏi tường):
    outNormalX = -blockedSumX / len;
    outNormalY = -blockedSumY / len;
    return true;
}

float KitingEngine::EvaluateTangentThreat(
    const ThreatItem* threats,
    uint32_t count,
    float tanX,
    float tanY
) const {
    float totalThreat = 0.0f;
    for (uint32_t i = 0; i < count; ++i) {
        // Vector từ player tới monster: mx = -dx, my = -dy
        float mx = -threats[i].dx;
        float my = -threats[i].dy;
        float dot = mx * tanX + my * tanY;
        if (dot > 0.0f && threats[i].dist > 0.1f) {
            // Quái vật nằm về phía nửa mặt phẳng của vector tiếp tuyến này
            totalThreat += (dot / threats[i].dist) / (threats[i].dist * threats[i].dist);
        }
    }
    return totalThreat;
}

bool KitingEngine::ComputeKiteStep(
    const TelemetryPacket& packet,
    const navigation::TerrainGrid& grid,
    uint64_t nowMs,
    navigation::Vec2& outStepTarget
) {
    if (!m_config.enabled) return false;

    const auto& player = packet.player;
    if (player.maxHP > 0 && player.currentHP == 0) return false;

    // Giới hạn tần suất bước lùi (Stutter-step interval)
    if (nowMs - m_state.lastKiteStepMs < m_config.stepIntervalMs) {
        return false;
    }

    float repulseX = 0.0f;
    float repulseY = 0.0f;
    uint32_t closeThreats = 0;
    uint32_t surroundThreats = 0;
    float closestDist = 9999.0f;

    // Mảng tĩnh stack POD thu thập tối đa 32 quái gần nhất (Zero Dynamic Allocation)
    ThreatItem threats[32];
    uint32_t threatCount = 0;

    const uint32_t maxEntitiesToScan = (packet.entityCount < 64u) ? packet.entityCount : 64u;
    for (uint32_t i = 0; i < maxEntitiesToScan; ++i) {
        const auto& ent = packet.entities[i];
        if (ent.type == 1 && !(ent.extraFlags & 4)) { // Monster còn sống
            float dx = player.posX - ent.posX;
            float dy = player.posY - ent.posY;
            float dist = (player.posX != 0.0f || player.posY != 0.0f) ? std::sqrt(dx * dx + dy * dy) : ent.distanceToPlayer;

            if (dist < closestDist) closestDist = dist;

            if (dist <= m_config.dangerRadius && dist > 0.1f) {
                // Lực đẩy tỷ lệ nghịch với bình phương khoảng cách (Vector Repulsion)
                float weight = 1.0f / (dist * dist);
                repulseX += (dx / dist) * weight;
                repulseY += (dy / dist) * weight;
                ++closeThreats;

                if (threatCount < 32) {
                    threats[threatCount++] = ThreatItem{ dx, dy, dist };
                }
            }

            if (dist <= m_config.surroundRadius && dist > 0.1f) {
                ++surroundThreats;
            }
        }
    }

    if (closeThreats == 0) {
        m_state.isKiting = false;
        m_state.isTangentSlide = false;
        return false;
    }

    float repulseLen = std::sqrt(repulseX * repulseX + repulseY * repulseY);
    if (repulseLen < 0.0001f) {
        // Nếu quái vây đều 4 hướng khiến tổng vector triệt tiêu -> lùi mặc định về sau (theo hướng ngược trục nhìn)
        repulseX = 0.0f;
        repulseY = -1.0f;
        repulseLen = 1.0f;
    }

    float dirX = repulseX / repulseLen;
    float dirY = repulseY / repulseLen;

    // Kiểm tra va chạm biên tường hoặc bị quái vật bao vây (> 8 con trong bán kính 25u)
    bool directBackBlocked = false;
    if (m_config.enableTangentSliding) {
        float probeX = player.posX + dirX * m_config.wallProbeDistance;
        float probeY = player.posY + dirY * m_config.wallProbeDistance;
        if (!grid.IsWalkable(probeX, probeY) || !grid.HasLineOfSight(player.posX, player.posY, probeX, probeY)) {
            directBackBlocked = true;
        }
    }

    const bool isSurrounded = (surroundThreats >= m_config.surroundThreatThreshold);
    const bool triggerTangent = m_config.enableTangentSliding && (directBackBlocked || isSurrounded);

    float tanX = 0.0f;
    float tanY = 0.0f;

    if (triggerTangent) {
        float wallNormX = 0.0f, wallNormY = 0.0f;
        if (directBackBlocked && DetectWallNormal(player.posX, player.posY, grid, m_config.wallProbeDistance * 0.75f, wallNormX, wallNormY)) {
            // Trường hợp 1: Chạm tường chắn phía sau -> Trượt tiếp tuyến dọc theo mặt tường (Wall Sliding)
            float tX = -wallNormY;
            float tY =  wallNormX;

            float dotRepulse = dirX * tX + dirY * tY;
            float threatPos = EvaluateTangentThreat(threats, threatCount,  tX,  tY);
            float threatNeg = EvaluateTangentThreat(threats, threatCount, -tX, -tY);

            float sign = 1.0f;
            if (std::abs(dotRepulse) > 0.2f) {
                sign = (dotRepulse >= 0.0f) ? 1.0f : -1.0f;
            } else {
                sign = (threatPos <= threatNeg) ? 1.0f : -1.0f;
            }

            // Trượt dọc mặt tường đồng thời bẻ góc nhẹ 15 độ hướng ra ngoài tường để chống cọ xát
            tanX = tX * sign + wallNormX * 0.25f;
            tanY = tY * sign + wallNormY * 0.25f;
            float tanLen = std::sqrt(tanX * tanX + tanY * tanY);
            if (tanLen > 0.001f) {
                tanX /= tanLen;
                tanY /= tanLen;
            }
        } else {
            // Trường hợp 2: Bị bao vây (> 8 quái) hoặc kẹt góc -> Tính 2 vector vuông góc với repulsion (Left / Right Flank)
            float tLeftX  = -dirY;
            float tLeftY  =  dirX;
            float tRightX =  dirY;
            float tRightY = -dirX;

            float threatLeft  = EvaluateTangentThreat(threats, threatCount, tLeftX, tLeftY);
            float threatRight = EvaluateTangentThreat(threats, threatCount, tRightX, tRightY);

            // Ưu tiên sườn quái có mật độ thưa hơn để thoát hiểm
            if (threatLeft <= threatRight) {
                tanX = tLeftX;
                tanY = tLeftY;
            } else {
                tanX = tRightX;
                tanY = tRightY;
            }
        }
    }

    bool foundWalkableEscape = false;
    navigation::Vec2 bestTarget{ 0.0f, 0.0f };
    bool usedTangent = false;

    // Nếu kích hoạt Tangent Sliding: Ưu tiên thử các vector tiếp tuyến & chéo trước
    if (triggerTangent) {
        const float candDirs[4][2] = {
            { tanX, tanY },
            { (tanX + dirX) * 0.7071f, (tanY + dirY) * 0.7071f },
            { -tanX, -tanY },
            { (-tanX + dirX) * 0.7071f, (-tanY + dirY) * 0.7071f }
        };

        for (int i = 0; i < 4; ++i) {
            float candX = player.posX + candDirs[i][0] * m_config.kiteStepDistance;
            float candY = player.posY + candDirs[i][1] * m_config.kiteStepDistance;

            if (grid.IsWalkable(candX, candY) && grid.HasLineOfSight(player.posX, player.posY, candX, candY)) {
                bestTarget = navigation::Vec2(candX, candY);
                foundWalkableEscape = true;
                usedTangent = true;
                break;
            }
        }
    }

    // Nếu không kích hoạt Tangent hoặc các hướng tiếp tuyến đều bị cản:
    // Thử 7 góc thoát hiểm tiêu chuẩn: 0, 30, -30, 60, -60, 90, -90 độ
    if (!foundWalkableEscape) {
        const float testAngles[7] = { 0.0f, 0.5235f, -0.5235f, 1.0472f, -1.0472f, 1.5708f, -1.5708f };
        for (float angle : testAngles) {
            float cosA = std::cos(angle);
            float sinA = std::sin(angle);
            float rotX = dirX * cosA - dirY * sinA;
            float rotY = dirX * sinA + dirY * cosA;

            float candX = player.posX + rotX * m_config.kiteStepDistance;
            float candY = player.posY + rotY * m_config.kiteStepDistance;

            if (grid.IsWalkable(candX, candY) && grid.HasLineOfSight(player.posX, player.posY, candX, candY)) {
                bestTarget = navigation::Vec2(candX, candY);
                foundWalkableEscape = true;
                usedTangent = false;
                break;
            }
        }
    }

    if (!foundWalkableEscape) {
        // Fallback: Tìm ô đi được gần nhất quanh hướng lùi hoặc tiếp tuyến
        float fbDirX = triggerTangent ? tanX : dirX;
        float fbDirY = triggerTangent ? tanY : dirY;
        float fallbackX = player.posX + fbDirX * (m_config.kiteStepDistance * 0.6f);
        float fallbackY = player.posY + fbDirY * (m_config.kiteStepDistance * 0.6f);
        float nearX, nearY;
        if (grid.FindNearestWalkable(fallbackX, fallbackY, nearX, nearY, 25.0f)) {
            bestTarget = navigation::Vec2(nearX, nearY);
            foundWalkableEscape = true;
            usedTangent = triggerTangent;
        }
    }

    if (foundWalkableEscape) {
        m_state.lastKiteStepMs = nowMs;
        m_state.isKiting = true;
        m_state.isTangentSlide = usedTangent;
        m_state.lastKiteTarget = bestTarget;
        ++m_state.totalKiteSteps;
        if (usedTangent) {
            ++m_state.totalTangentSlides;
        }
        outStepTarget = bestTarget;
        return true;
    }

    m_state.isKiting = false;
    m_state.isTangentSlide = false;
    return false;
}

} // namespace combat
