#include "combat/monster_pack_clusterer.hpp"
#include "navigation/terrain_grid.hpp"

#include <cmath>
#include <algorithm>

namespace combat {

MonsterPackClusterer::MonsterPackClusterer(const MonsterPackConfig& config)
    : m_config(config) {
}

MonsterPackClusterResult MonsterPackClusterer::ClusterPacks(
    const TelemetryPacket& packet,
    const navigation::TerrainGrid* grid) const {

    MonsterPackClusterResult result{};
    const auto& player = packet.player;

    const float pX = player.posX;
    const float pY = player.posY;
    const float maxScanSq = m_config.maxScanRadius * m_config.maxScanRadius;
    const float clusterRadSq = m_config.clusterRadius * m_config.clusterRadius;

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

        const float dx = ent.posX - pX;
        const float dy = ent.posY - pY;
        const float distSq = dx * dx + dy * dy;
        if (distSq > maxScanSq) continue;

        ++result.totalMonstersNear;

        const bool isBoss = (ent.rarity == 3) || (ent.extraFlags & 1);
        const bool isRare = (ent.rarity == 2);
        const bool isStaggered = (ent.staggerProgress >= 8000) || (ent.extraFlags & (1 << 7));

        // 1. Tìm cụm gần nhất hiện có (Leader Clustering)
        int bestMatchingCluster = -1;
        float minClusterDistSq = clusterRadSq;

        for (size_t c = 0; c < result.clusterCount; ++c) {
            const auto& cl = result.clusters[c];
            const float cdx = ent.posX - cl.centroidX;
            const float cdy = ent.posY - cl.centroidY;
            const float dSq = cdx * cdx + cdy * cdy;
            if (dSq <= minClusterDistSq) {
                minClusterDistSq = dSq;
                bestMatchingCluster = static_cast<int>(c);
            }
        }

        if (bestMatchingCluster >= 0) {
            // Gom vào cụm có sẵn
            auto& cl = result.clusters[static_cast<size_t>(bestMatchingCluster)];
            const float oldCount = static_cast<float>(cl.monsterCount);
            const float newCount = oldCount + 1.0f;

            // Cập nhật tọa độ trọng tâm (Centroid update)
            cl.centroidX = (cl.centroidX * oldCount + ent.posX) / newCount;
            cl.centroidY = (cl.centroidY * oldCount + ent.posY) / newCount;
            cl.monsterCount = static_cast<uint16_t>(newCount);

            if (isBoss) {
                cl.hasBoss = true;
                ++cl.rareOrBossCount;
                cl.primaryTargetId = ent.id; // Boss luôn là hạt nhân
            } else if (isRare) {
                ++cl.rareOrBossCount;
                if (!cl.hasBoss) cl.primaryTargetId = ent.id;
            }

            if (isStaggered) {
                ++cl.staggeredCount;
            }

            const float spreadDx = ent.posX - cl.centroidX;
            const float spreadDy = ent.posY - cl.centroidY;
            const float spread = std::sqrt(spreadDx * spreadDx + spreadDy * spreadDy);
            if (spread > cl.spreadRadius) {
                cl.spreadRadius = spread;
            }
        } else if (result.clusterCount < MonsterPackClusterResult::kMaxClusters) {
            // Tạo cụm mới
            auto& cl = result.clusters[result.clusterCount];
            cl.centroidX = ent.posX;
            cl.centroidY = ent.posY;
            cl.spreadRadius = 15.0f;
            cl.monsterCount = 1;
            cl.hasBoss = isBoss;
            cl.rareOrBossCount = (isBoss || isRare) ? 1 : 0;
            cl.staggeredCount = isStaggered ? 1 : 0;
            cl.primaryTargetId = ent.id;

            ++result.clusterCount;
        }
    }

    // 2. Đánh giá khoảng cách, tầm nhìn (Line-of-Sight) và chấm điểm ưu tiên từng cụm
    float bestScore = -999999.0f;

    for (size_t c = 0; c < result.clusterCount; ++c) {
        auto& cl = result.clusters[c];
        const float cdx = cl.centroidX - pX;
        const float cdy = cl.centroidY - pY;
        cl.distanceToPlayer = std::sqrt(cdx * cdx + cdy * cdy);

        cl.hasLineOfSight = grid ? grid->HasLineOfSight(pX, pY, cl.centroidX, cl.centroidY) : true;

        float score = (cl.hasLineOfSight ? m_config.losBonus : 0.0f)
            - (cl.distanceToPlayer * m_config.distanceWeight)
            + (static_cast<float>(cl.monsterCount) * m_config.densityWeight);

        if (cl.hasBoss) {
            score += m_config.threatWeightBoss;
        }
        if (cl.rareOrBossCount > 0 && !cl.hasBoss) {
            score += static_cast<float>(cl.rareOrBossCount) * m_config.threatWeightRare;
        }
        if (cl.staggeredCount > 0) {
            score += static_cast<float>(cl.staggeredCount) * 1500.0f; // Ưu tiên dứt điểm quái Stagger
        }

        cl.priorityScore = score;

        if (score > bestScore) {
            bestScore = score;
            result.bestClusterIndex = static_cast<int>(c);
        }
    }

    return result;
}

} // namespace combat
