#include "combat/telegraph_evasion_arbiter.hpp"
#include "navigation/terrain_grid.hpp"
#include <algorithm>
#include <cmath>

namespace combat {

constexpr float kPi = 3.14159265358979323846f;
constexpr float kTwoPi = 2.0f * kPi;

float TelegraphEvasionArbiter::WrapAngle(float a) {
    while (a > kPi) a -= kTwoPi;
    while (a < -kPi) a += kTwoPi;
    return a;
}

TelegraphEvasionArbiter::TelegraphEvasionArbiter(const TelegraphArbiterConfig& config)
    : m_config(config) {}

void TelegraphEvasionArbiter::RecordDodgeExecuted(uint64_t nowMs) {
    m_lastRollMs = nowMs;
    ++m_totalRolls;
}

bool TelegraphEvasionArbiter::IsPointInDangerZone(Vector2D pt, const TelegraphThreat& threat) {
    float dummyDirX = 0.0f, dummyDirY = 0.0f, dummyDist = 0.0f;
    switch (threat.shape) {
        case TelegraphShape::Circle:
            return CheckCircleThreat(pt, threat, dummyDirX, dummyDirY, dummyDist, 0.0f);
        case TelegraphShape::FrontalCone:
            return CheckConeThreat(pt, threat, dummyDirX, dummyDirY, dummyDist, 0.0f);
        case TelegraphShape::Rectangle:
            return CheckRectThreat(pt, threat, dummyDirX, dummyDirY, dummyDist, 0.0f);
        default:
            return false;
    }
}

bool TelegraphEvasionArbiter::CheckCircleThreat(
    Vector2D player,
    const TelegraphThreat& t,
    float& outDirX,
    float& outDirY,
    float& outDist,
    float margin
) {
    const float dx = player.x - t.originX;
    const float dy = player.y - t.originY;
    const float distSq = dx * dx + dy * dy;

    if (distSq >= t.radius * t.radius) {
        return false; // Ngoài tầm sát thương tròn
    }

    const float dist = std::sqrt(distSq);
    outDist = (t.radius - dist) + margin;

    if (dist > 0.001f) {
        // Hướng thoát hiểm nhanh nhất: tịnh tiến ly tâm ra xa tâm dập
        outDirX = dx / dist;
        outDirY = dy / dist;
    } else {
        // Trùng tâm tuyệt đối: lùi ngược hướng mặt quái
        outDirX = std::cos(t.directionRad + kPi);
        outDirY = std::sin(t.directionRad + kPi);
    }
    return true;
}

bool TelegraphEvasionArbiter::CheckConeThreat(
    Vector2D player,
    const TelegraphThreat& t,
    float& outDirX,
    float& outDirY,
    float& outDist,
    float margin
) {
    const float dx = player.x - t.originX;
    const float dy = player.y - t.originY;
    const float distSq = dx * dx + dy * dy;

    if (distSq >= t.radius * t.radius) {
        return false;
    }

    const float dist = std::sqrt(distSq);
    if (dist < 0.001f) {
        // Quá gần tâm nón: thoát ngược ra phía sau
        outDirX = std::cos(t.directionRad + kPi);
        outDirY = std::sin(t.directionRad + kPi);
        outDist = margin + 15.0f;
        return true;
    }

    const float playerAngle = std::atan2(dy, dx);
    const float deltaAngle = WrapAngle(playerAngle - t.directionRad);
    const float halfAngle = (t.widthOrHalfAngle > 0.01f) ? t.widthOrHalfAngle : 0.7854f; // 45 độ mặc định

    if (std::fabs(deltaAngle) >= halfAngle) {
        return false; // Nằm ngoài góc quét hình nón
    }

    // Nhân vật nằm trong nón. Có 2 đường thoát:
    // 1. Thoát ngang tiếp tuyến (Lateral exit): ngắn hơn rất nhiều khi ở gần tâm
    const float angleToEdge = halfAngle - std::fabs(deltaAngle);
    const float lateralDist = dist * angleToEdge;
    const float radialDist = t.radius - dist;

    if (lateralDist <= radialDist) {
        // Thoát theo hướng tiếp tuyến vuông góc với tia bán kính
        if (deltaAngle >= 0.0f) {
            // Nằm lệch bên trái hướng đòn -> thoát chếch sang trái (+90 độ)
            outDirX = -dy / dist;
            outDirY =  dx / dist;
        } else {
            // Nằm lệch bên phải hướng đòn -> thoát chếch sang phải (-90 độ)
            outDirX =  dy / dist;
            outDirY = -dx / dist;
        }
        outDist = lateralDist + margin;
    } else {
        // Chạy thẳng ra ngoài bán kính
        outDirX = dx / dist;
        outDirY = dy / dist;
        outDist = radialDist + margin;
    }
    return true;
}

bool TelegraphEvasionArbiter::CheckRectThreat(
    Vector2D player,
    const TelegraphThreat& t,
    float& outDirX,
    float& outDirY,
    float& outDist,
    float margin
) {
    const float dirCos = std::cos(t.directionRad);
    const float dirSin = std::sin(t.directionRad);

    // Vector chỉ phương trục dọc và trục ngang của hình chữ nhật
    const float forwardX = dirCos;
    const float forwardY = dirSin;
    const float lateralX = -dirSin;
    const float lateralY =  dirCos;

    const float dx = player.x - t.originX;
    const float dy = player.y - t.originY;

    // Chiếu tọa độ người chơi lên hệ trục của đòn đánh
    const float projForward = dx * forwardX + dy * forwardY;
    const float projLateral = dx * lateralX + dy * lateralY;

    const float halfWidth = (t.widthOrHalfAngle > 0.01f) ? (t.widthOrHalfAngle * 0.5f) : 35.0f;

    if (projForward < 0.0f || projForward >= t.radius || std::fabs(projLateral) >= halfWidth) {
        return false; // Nằm ngoài vùng quét hình chữ nhật
    }

    // Thoát ngang vuông góc với trục đòn quét (đường ngắn nhất)
    if (projLateral >= 0.0f) {
        outDirX = lateralX;
        outDirY = lateralY;
        outDist = (halfWidth - projLateral) + margin;
    } else {
        outDirX = -lateralX;
        outDirY = -lateralY;
        outDist = (halfWidth + projLateral) + margin;
    }
    return true;
}

bool TelegraphEvasionArbiter::EvaluateSingleThreat(
    Vector2D playerPos,
    const TelegraphThreat& threat,
    float& outEscapeDirX,
    float& outEscapeDirY,
    float& outEscapeDist
) const {
    switch (threat.shape) {
        case TelegraphShape::Circle:
            return CheckCircleThreat(playerPos, threat, outEscapeDirX, outEscapeDirY, outEscapeDist, m_config.safetyMargin);
        case TelegraphShape::FrontalCone:
            return CheckConeThreat(playerPos, threat, outEscapeDirX, outEscapeDirY, outEscapeDist, m_config.safetyMargin);
        case TelegraphShape::Rectangle:
            return CheckRectThreat(playerPos, threat, outEscapeDirX, outEscapeDirY, outEscapeDist, m_config.safetyMargin);
        default:
            return false;
    }
}

EvasionDecision TelegraphEvasionArbiter::EvaluateThreats(
    const TelemetryPacket& packet,
    uint64_t nowMs,
    const navigation::TerrainGrid* grid
) {
    EvasionDecision decision{};
    if (!m_config.enabled) return decision;

    const auto& player = packet.player;
    const Vector2D playerPos{ player.posX, player.posY };

    // Tìm mối đe dọa nguy hiểm nhất (ưu tiên đòn đánh mà nhân vật đang đứng trong vùng sát thương)
    float minTimeRemainingMs = 999999.0f;
    bool foundInsideDanger = false;
    TelegraphThreat criticalThreat{};

    const uint32_t maxScan = (packet.entityCount < 64u) ? packet.entityCount : 64u;

    for (uint32_t i = 0; i < maxScan; ++i) {
        const auto& ent = packet.entities[i];
        if (ent.type != 1) continue; // Chỉ quét quái vật
        if (ent.extraFlags & 4) continue; // Bỏ qua quái chết
        if (ent.currentHP == 0 || ent.maxHP == 0) continue;

        const bool isBossOrRare = (ent.rarity >= 2) || (ent.extraFlags & 1);
        if (!isBossOrRare) continue;

        // Nhận diện hoạt ảnh Telegraph gồng đòn
        const bool isTelegraph = (ent.extraFlags & (1 << 6)) ||
            (ent.currentAnimationId >= 100 && ent.currentAnimationId <= 150 && ent.currentAnimationId != 0);

        if (!isTelegraph) continue;

        TelegraphThreat threat{};
        threat.sourceEntityId = ent.id;
        threat.originX = ent.posX;
        threat.originY = ent.posY;

        // Phân loại hình thái chiêu thức theo Animation ID hoặc tên quái
        if (ent.currentAnimationId >= 100 && ent.currentAnimationId <= 115) {
            // Nhóm chiêu dập sàn tròn (Circle Slam)
            threat.shape = TelegraphShape::Circle;
            threat.radius = m_config.defaultSlamRadius;
            threat.timeRemainingMs = 320.0f;
        } else if (ent.currentAnimationId >= 116 && ent.currentAnimationId <= 130) {
            // Nhóm chiêu quét nón phía trước (Frontal Cone)
            threat.shape = TelegraphShape::FrontalCone;
            threat.radius = m_config.defaultConeRadius;
            threat.widthOrHalfAngle = m_config.defaultConeAngleRad;
            // Hướng mặt quái ước tính theo vector tới người chơi lúc bắt đầu gồng
            const float dx = player.posX - ent.posX;
            const float dy = player.posY - ent.posY;
            threat.directionRad = std::atan2(dy, dx);
            threat.timeRemainingMs = 380.0f;
        } else {
            // Nhóm chiêu dập tia thẳng / vệt sóng (Rectangle Beam)
            threat.shape = TelegraphShape::Rectangle;
            threat.radius = m_config.defaultRectLength;
            threat.widthOrHalfAngle = m_config.defaultRectWidth;
            const float dx = player.posX - ent.posX;
            const float dy = player.posY - ent.posY;
            threat.directionRad = std::atan2(dy, dx);
            threat.timeRemainingMs = 300.0f;
        }

        // Đánh giá xem người chơi có nằm trong vùng sát thương không
        float dirX = 0.0f, dirY = 0.0f, dist = 0.0f;
        if (EvaluateSingleThreat(playerPos, threat, dirX, dirY, dist)) {
            foundInsideDanger = true;
            if (threat.timeRemainingMs < minTimeRemainingMs) {
                minTimeRemainingMs = threat.timeRemainingMs;
                criticalThreat = threat;
                decision.evasionDirX = dirX;
                decision.evasionDirY = dirY;
                decision.escapeDistance = dist;
                decision.threatEntityId = threat.sourceEntityId;
                decision.timeRemainingMs = threat.timeRemainingMs;
            }
        }
    }

    if (!foundInsideDanger) {
        decision.action = EvasionActionKind::None;
        decision.reason = "Clear of danger zones";
        return decision;
    }

    // Kẹp kiểm tra địa hình để tránh lăn vào góc kẹt / tường
    if (m_config.checkTerrain && grid != nullptr) {
        const float probeDist = (decision.escapeDistance > 20.0f) ? decision.escapeDistance : 25.0f;
        const float targetX = playerPos.x + decision.evasionDirX * probeDist;
        const float targetY = playerPos.y + decision.evasionDirY * probeDist;

        if (!grid->IsWalkable(targetX, targetY)) {
            // Nếu hướng né trực diện đâm vào tường, thử xoay ±45 độ tiếp tuyến
            constexpr float kAngles[4] = { 0.7854f, -0.7854f, 1.5708f, -1.5708f };
            for (float dAngle : kAngles) {
                const float c = std::cos(dAngle);
                const float s = std::sin(dAngle);
                const float altDirX = decision.evasionDirX * c - decision.evasionDirY * s;
                const float altDirY = decision.evasionDirX * s + decision.evasionDirY * c;
                if (grid->IsWalkable(playerPos.x + altDirX * probeDist, playerPos.y + altDirY * probeDist)) {
                    decision.evasionDirX = altDirX;
                    decision.evasionDirY = altDirY;
                    break;
                }
            }
        }
    }

    // Ra quyết định hành động: Iframe Roll (Space) hay Stutter-step
    const bool onRollCooldown = IsRollOnCooldown(nowMs);

    if (decision.timeRemainingMs <= m_config.rollUrgencyWindowMs && !onRollCooldown) {
        decision.action = EvasionActionKind::IframeRoll;
        decision.reason = "CRITICAL TELEGRAPH: Immediate Spacebar Roll";
        RecordDodgeExecuted(nowMs);
    } else {
        decision.action = EvasionActionKind::StutterStep;
        decision.reason = onRollCooldown ? "Roll on cooldown: Stutter-step evasion" : "Early telegraph: Stutter-step evasion";
        ++m_totalStutterSteps;
    }

    return decision;
}

} // namespace combat
