#include <iostream>
#include <vector>
#include <cmath>
#include <string>
#include <unordered_map>
#include <functional>
#include <algorithm>
#include <iomanip>
#include <memory>
#include <limits>
#include <random>

#ifndef M_PI
constexpr float PI = 3.14159265358979323846f;
#else
constexpr float PI = static_cast<float>(M_PI);
#endif

constexpr float DEFAULT_ENTITY_RADIUS = 0.5f;
constexpr float CRIT_CHANCE_DEFAULT = 0.1f;
constexpr float CRIT_MULTIPLIER_DEFAULT = 1.5f;
constexpr float EPSILON = 1e-6f;

enum class DamageType {
    PHYSICAL,
    MAGICAL,
    TRUE
};

struct Vector2 {
    float x, y;

    Vector2(float x = 0.0f, float y = 0.0f) : x(x), y(y) {}

    Vector2 operator+(const Vector2& other) const {
        return Vector2(x + other.x, y + other.y);
    }

    Vector2 operator-(const Vector2& other) const {
        return Vector2(x - other.x, y - other.y);
    }

    Vector2 operator*(float scalar) const {
        return Vector2(x * scalar, y * scalar);
    }

    Vector2 operator/(float scalar) const {
        if (std::abs(scalar) < EPSILON) return Vector2();
        return Vector2(x / scalar, y / scalar);
    }

    Vector2 operator-() const {
        return Vector2(-x, -y);
    }

    bool operator==(const Vector2& other) const {
        return std::abs(x - other.x) < EPSILON && std::abs(y - other.y) < EPSILON;
    }

    bool operator!=(const Vector2& other) const {
        return !(*this == other);
    }

    Vector2& operator+=(const Vector2& other) {
        x += other.x;
        y += other.y;
        return *this;
    }

    Vector2& operator-=(const Vector2& other) {
        x -= other.x;
        y -= other.y;
        return *this;
    }

    Vector2& operator*=(float scalar) {
        x *= scalar;
        y *= scalar;
        return *this;
    }

    float Dot(const Vector2& other) const {
        return x * other.x + y * other.y;
    }

    float Cross(const Vector2& other) const {
        return x * other.y - y * other.x;
    }

    float LengthSquared() const {
        return x * x + y * y;
    }

    float Length() const {
        return std::sqrt(LengthSquared());
    }

    Vector2 Normalized() const {
        float len = Length();
        if (len < EPSILON) return Vector2();
        return *this / len;
    }

    void Normalize() {
        float len = Length();
        if (len > EPSILON) {
            x /= len;
            y /= len;
        }
    }

    float Distance(const Vector2& other) const {
        return (*this - other).Length();
    }

    float DistanceSquared(const Vector2& other) const {
        Vector2 diff = *this - other;
        return diff.Dot(diff);
    }

    static Vector2 Lerp(const Vector2& a, const Vector2& b, float t) {
        return Vector2(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t);
    }
};

struct DetectionRegionConfig {
    float angleSize = 360.0f;
    float radius = 99.9f;
    float height = 99.9f;
    float effectiveDistance = 9999.0f;
    float duration = 9999.0f;
    float judgmentDelay = 0.0001f;
    bool adaptation = true;
    float upperLimitThresholdSize = 99.9f;
    float movementCompensation = 99.9f;
    float jumpCompensation = 99.9f;
    float vestCompensation = 99.9f;
    float stationaryCompensation = 99.9f;
    float globalScale = 99.9f;
    float extraRadiusMultiplier = 99.9f;
    bool noBackfaceCulling = true;
    bool forceSphereCast = true;
    bool ignoreOcclusion = true;
    bool affectOnlyTarget = false;

    bool IsValid() const {
        return radius > EPSILON && height > EPSILON && duration > EPSILON;
    }
};

static const DetectionRegionConfig& MaximizeConfig() {
    static const DetectionRegionConfig c = []() {
        DetectionRegionConfig cfg{};
        cfg.radius = std::numeric_limits<float>::max();
        cfg.height = std::numeric_limits<float>::max();
        cfg.effectiveDistance = std::numeric_limits<float>::max();
        cfg.duration = std::numeric_limits<float>::max();
        cfg.angleSize = 360.0f;
        cfg.ignoreOcclusion = true;
        cfg.affectOnlyTarget = false;
        return cfg;
    }();
    return c;
}

class DetectionRegionManager {
private:
    std::unordered_map<std::string, DetectionRegionConfig> configs;
    DetectionRegionManager() { initDefaults(); }
    
    DetectionRegionManager(const DetectionRegionManager&) = delete;
    DetectionRegionManager& operator=(const DetectionRegionManager&) = delete;
    DetectionRegionManager(DetectionRegionManager&&) = delete;
    DetectionRegionManager& operator=(DetectionRegionManager&&) = delete;

public:
    static DetectionRegionManager& GetInstance() {
        static DetectionRegionManager instance;
        return instance;
    }

    void LoadConfig(const std::string& name, const DetectionRegionConfig& cfg) {
        if (name.empty()) return;
        configs[name] = cfg;
    }

    bool HasConfig(const std::string& name) const {
        return configs.find(name) != configs.end();
    }

    const DetectionRegionConfig& GetConfig(const std::string& name) const {
        auto it = configs.find(name);
        if (it == configs.end()) {
            static const DetectionRegionConfig defaultCfg{};
            return defaultCfg;
        }
        return it->second;
    }

    size_t GetConfigCount() const {
        return configs.size();
    }

    void ClearConfigs() {
        configs.clear();
        initDefaults();
    }

private:
    void initDefaults() {
        LoadConfig("DRV_Head", DetectionRegionConfig{});
        LoadConfig("DRV_Body", DetectionRegionConfig{});
        LoadConfig("DRV_Limb", DetectionRegionConfig{});
        LoadConfig("DRV_Global", MaximizeConfig());
    }
};

class Entity {
private:
    Vector2 pos;
    float hp;
    float maxHp;
    float defense;
    float magicResist;
    float radius;
    DamageType weakType;
    std::string name;
    int id;
    static int nextId;

public:
    Entity(float hp = 100.0f, float defense = 10.0f, float magicResist = 10.0f,
           DamageType weakType = DamageType::PHYSICAL, 
           float radius = DEFAULT_ENTITY_RADIUS,
           const std::string& name = "")
        : maxHp(std::max(0.0f, hp)), 
          hp(std::max(0.0f, hp)), 
          defense(std::max(0.0f, defense)), 
          magicResist(std::max(0.0f, magicResist)),
          weakType(weakType), 
          radius(std::max(0.0f, radius)),
          name(name),
          id(nextId++) {}

    Entity(Entity&& other) noexcept
        : pos(other.pos),
          hp(other.hp),
          maxHp(other.maxHp),
          defense(other.defense),
          magicResist(other.magicResist),
          radius(other.radius),
          weakType(other.weakType),
          name(std::move(other.name)),
          id(other.id) {
        other.hp = 0;
        other.maxHp = 0;
    }

    Entity& operator=(Entity&& other) noexcept {
        if (this != &other) {
            pos = other.pos;
            hp = other.hp;
            maxHp = other.maxHp;
            defense = other.defense;
            magicResist = other.magicResist;
            radius = other.radius;
            weakType = other.weakType;
            name = std::move(other.name);
            id = other.id;
            
            other.hp = 0;
            other.maxHp = 0;
        }
        return *this;
    }

    Vector2 GetPos() const { return pos; }
    void SetPos(const Vector2& newPos) { pos = newPos; }

    float GetHp() const { return hp; }
    float GetMaxHp() const { return maxHp; }
    float GetHpPercentage() const { 
        return maxHp > EPSILON ? (hp / maxHp) * 100.0f : 0.0f; 
    }

    float GetDefense() const { return defense; }
    void SetDefense(float newDefense) { defense = std::max(0.0f, newDefense); }

    float GetMagicResist() const { return magicResist; }
    void SetMagicResist(float newResist) { magicResist = std::max(0.0f, newResist); }

    float GetRadius() const { return radius; }
    void SetRadius(float newRadius) { radius = std::max(0.0f, newRadius); }

    DamageType GetWeakType() const { return weakType; }
    void SetWeakType(DamageType type) { weakType = type; }

    const std::string& GetName() const { return name; }
    void SetName(const std::string& newName) { name = newName; }

    int GetId() const { return id; }

    void Heal(float amount) {
        if (amount > EPSILON) {
            hp = std::min(maxHp, hp + amount);
        }
    }

    void TakeDamage(float rawDamage, DamageType type, bool isCrit = false) {
        if (!IsAlive() || rawDamage < EPSILON) return;

        float finalDamage = rawDamage;

        switch (type) {
            case DamageType::PHYSICAL:
                finalDamage = std::max(0.0f, finalDamage - defense);
                break;
            case DamageType::MAGICAL:
                finalDamage = std::max(0.0f, finalDamage - magicResist);
                break;
            case DamageType::TRUE:
                break;
        }

        if (isCrit) {
            finalDamage *= CRIT_MULTIPLIER_DEFAULT;
        }

        if (type == weakType) {
            finalDamage *= 1.2f;
        }

        hp = std::max(0.0f, hp - finalDamage);
        OnHit(finalDamage, type, isCrit);
    }

    virtual void OnHit(float damage, DamageType type, bool isCrit) {}

    bool IsAlive() const { return hp > EPSILON; }

    float DistanceTo(const Entity& other) const {
        return pos.Distance(other.pos);
    }

    bool IsInRange(const Vector2& point, float range) const {
        return pos.Distance(point) <= (range + radius);
    }
};

int Entity::nextId = 0;

class HitCircle {
private:
    Vector2 center;
    float radius;
    float damage;
    DamageType damageType;
    const DetectionRegionConfig* cfg;
    bool isActive;
    int sourceId;
    std::string tag;
    float lifetime;
    float elapsedTime;

public:
    using DamageFunc = std::function<void(HitCircle&, Entity&, float, bool)>;
    DamageFunc onHit;
    std::function<void(HitCircle&)> onExpired;

    HitCircle(const Vector2& c, float r, float d, DamageType type = DamageType::PHYSICAL,
              const DetectionRegionConfig* p = nullptr, bool active = true,
              int srcId = -1, const std::string& tag = "", float lifetime = -1.0f)
        : center(c), 
          radius(std::max(0.0f, r)), 
          damage(std::max(0.0f, d)), 
          damageType(type), 
          cfg(p), 
          isActive(active),
          sourceId(srcId),
          tag(tag),
          lifetime(lifetime),
          elapsedTime(0.0f) {}

    void Update(float deltaTime) {
        if (!isActive) return;
        
        elapsedTime += deltaTime;
        if (lifetime > EPSILON && elapsedTime >= lifetime) {
            SetActive(false);
            if (onExpired) {
                onExpired(*this);
            }
        }
    }

    bool IsPointInRange(const Vector2& point) const {
        if (!isActive) return false;
        return center.DistanceSquared(point) <= (radius * radius);
    }

    bool IsEntityInRange(const Entity& entity) const {
        if (!isActive || !entity.IsAlive()) return false;
        float totalRadius = radius + entity.GetRadius();
        return center.DistanceSquared(entity.GetPos()) <= (totalRadius * totalRadius);
    }

    bool CheckCollision(const HitCircle& other) const {
        if (!isActive || !other.isActive) return false;
        float totalRadius = radius + other.radius;
        return center.DistanceSquared(other.center) <= (totalRadius * totalRadius);
    }

    bool CheckCollision(const Vector2& point, float pointRadius = 0.0f) const {
        if (!isActive) return false;
        float totalRadius = radius + pointRadius;
        return center.DistanceSquared(point) <= (totalRadius * totalRadius);
    }

    void FixCollision(HitCircle& other) {
        if (!isActive || !other.isActive) return;

        Vector2 dir = other.center - center;
        float distSq = dir.LengthSquared();
        float totalRadius = radius + other.radius;
        float totalRadiusSq = totalRadius * totalRadius;

        if (distSq < totalRadiusSq && distSq > EPSILON) {
            float dist = std::sqrt(distSq);
            float overlap = totalRadius - dist;
            Vector2 normal = dir / dist;
            center = center - normal * (overlap * 0.5f);
            other.center = other.center + normal * (overlap * 0.5f);
        }
    }

    void ApplyDamage(Entity& victim, float critChance = CRIT_CHANCE_DEFAULT) {
        if (!isActive || !IsEntityInRange(victim)) return;

        static std::random_device rd;
        static std::mt19937 gen(rd());
        std::uniform_real_distribution<float> dis(0.0f, 1.0f);
        
        bool isCrit = dis(gen) < critChance;
        float finalDamage = damage;

        if (onHit) {
            onHit(*this, victim, finalDamage, isCrit);
        } else {
            victim.TakeDamage(finalDamage, damageType, isCrit);
        }
    }

    void SetActive(bool active) { isActive = active; }
    bool IsActive() const { return isActive; }

    Vector2 GetCenter() const { return center; }
    void SetCenter(const Vector2& v) { center = v; }
    
    float GetRadius() const { return radius; }
    void SetRadius(float r) { radius = std::max(0.0f, r); }
    
    float GetDamage() const { return damage; }
    void SetDamage(float d) { damage = std::max(0.0f, d); }
    
    DamageType GetDamageType() const { return damageType; }
    void SetDamageType(DamageType type) { damageType = type; }
    
    const DetectionRegionConfig* GetConfig() const { return cfg; }
    
    int GetSourceId() const { return sourceId; }
    void SetSourceId(int id) { sourceId = id; }
    
    const std::string& GetTag() const { return tag; }
    void SetTag(const std::string& newTag) { tag = newTag; }
    
    float GetLifetime() const { return lifetime; }
    void SetLifetime(float time) { lifetime = time; }
    
    float GetElapsedTime() const { return elapsedTime; }
    float GetRemainingTime() const { 
        return lifetime > EPSILON ? std::max(0.0f, lifetime - elapsedTime) : -1.0f; 
    }
};

class SpatialGrid {
private:
    float cellSize;
    std::unordered_map<int, std::vector<size_t>> grid;
    int maxCellsX, maxCellsY;

    int GetCellID(int x, int y) const {
        const int prime = 31;
        int result = 17;
        result = result * prime + x;
        result = result * prime + y;
        return result;
    }

public:
    SpatialGrid(float size = 10.0f, int maxX = 1000, int maxY = 1000) 
        : cellSize(std::max(EPSILON, size)), maxCellsX(maxX), maxCellsY(maxY) {}

    void Clear() { grid.clear(); }

    void Insert(size_t circleIndex, const HitCircle& circle) {
        if (!circle.IsActive()) return;

        Vector2 center = circle.GetCenter();
        float radius = circle.GetRadius();

        int minX = static_cast<int>(std::floor((center.x - radius) / cellSize));
        int maxX = static_cast<int>(std::floor((center.x + radius) / cellSize));
        int minY = static_cast<int>(std::floor((center.y - radius) / cellSize));
        int maxY = static_cast<int>(std::floor((center.y + radius) / cellSize));

        minX = std::max(minX, -maxCellsX);
        maxX = std::min(maxX, maxCellsX);
        minY = std::max(minY, -maxCellsY);
        maxY = std::min(maxY, maxCellsY);

        for (int x = minX; x <= maxX; ++x) {
            for (int y = minY; y <= maxY; ++y) {
                int cellID = GetCellID(x, y);
                auto& cell = grid[cellID];
                if (std::find(cell.begin(), cell.end(), circleIndex) == cell.end()) {
                    cell.push_back(circleIndex);
                }
            }
        }
    }

    std::vector<size_t> GetPotentialCollisions(const Entity& entity) const {
        std::vector<size_t> candidates;
        std::unordered_set<size_t> visited;

        Vector2 pos = entity.GetPos();
        float radius = entity.GetRadius();

        int minX = static_cast<int>(std::floor((pos.x - radius) / cellSize));
        int maxX = static_cast<int>(std::floor((pos.x + radius) / cellSize));
        int minY = static_cast<int>(std::floor((pos.y - radius) / cellSize));
        int maxY = static_cast<int>(std::floor((pos.y + radius) / cellSize));

        minX = std::max(minX, -maxCellsX);
        maxX = std::min(maxX, maxCellsX);
        minY = std::max(minY, -maxCellsY);
        maxY = std::min(maxY, maxCellsY);

        for (int x = minX; x <= maxX; ++x) {
            for (int y = minY; y <= maxY; ++y) {
                int cellID = GetCellID(x, y);
                auto it = grid.find(cellID);
                if (it != grid.end()) {
                    for (size_t idx : it->second) {
                        if (visited.insert(idx).second) {
                            candidates.push_back(idx);
                        }
                    }
                }
            }
        }

        return candidates;
    }

    std::vector<size_t> GetPotentialCollisions(const HitCircle& circle) const {
        std::vector<size_t> candidates;
        std::unordered_set<size_t> visited;

        Vector2 center = circle.GetCenter();
        float radius = circle.GetRadius();

        int minX = static_cast<int>(std::floor((center.x - radius) / cellSize));
        int maxX = static_cast<int>(std::floor((center.x + radius) / cellSize));
        int minY = static_cast<int>(std::floor((center.y - radius) / cellSize));
        int maxY = static_cast<int>(std::floor((center.y + radius) / cellSize));

        minX = std::max(minX, -maxCellsX);
        maxX = std::min(maxX, maxCellsX);
        minY = std::max(minY, -maxCellsY);
        maxY = std::min(maxY, maxCellsY);

        for (int x = minX; x <= maxX; ++x) {
            for (int y = minY; y <= maxY; ++y) {
                int cellID = GetCellID(x, y);
                auto it = grid.find(cellID);
                if (it != grid.end()) {
                    for (size_t idx : it->second) {
                        if (visited.insert(idx).second) {
                            candidates.push_back(idx);
                        }
                    }
                }
            }
        }

        return candidates;
    }

    void RemoveCircle(size_t circleIndex) {
        for (auto& pair : grid) {
            auto& cell = pair.second;
            cell.erase(std::remove(cell.begin(), cell.end(), circleIndex), cell.end());
        }
    }
};

class HitCircleManager {
private:
    std::vector<HitCircle> circles;
    SpatialGrid spatialGrid;
    float globalCritChance;
    bool autoRemoveInactive;

public:
    HitCircleManager(float gridCellSize = 10.0f, 
                     float critChance = CRIT_CHANCE_DEFAULT,
                     bool autoRemove = true) 
        : spatialGrid(gridCellSize), 
          globalCritChance(std::clamp(critChance, 0.0f, 1.0f)),
          autoRemoveInactive(autoRemove) {}

    size_t AddCircle(const HitCircle& c) {
        circles.push_back(c);
        return circles.size() - 1;
    }

    size_t AddCircle(const Vector2& center, float radius, float damage,
                     DamageType type = DamageType::PHYSICAL,
                     const DetectionRegionConfig* cfg = nullptr,
                     HitCircle::DamageFunc fn = nullptr,
                     int sourceId = -1,
                     const std::string& tag = "",
                     float lifetime = -1.0f) {
        circles.emplace_back(center, radius, damage, type, cfg, true, sourceId, tag, lifetime);
        if (fn) circles.back().onHit = fn;
        return circles.size() - 1;
    }

    void Update(float deltaTime) {
        for (auto& circle : circles) {
            circle.Update(deltaTime);
        }
        
        if (autoRemoveInactive) {
            RemoveInactiveCircles();
        }
    }

    void RemoveInactiveCircles() {
        circles.erase(
            std::remove_if(circles.begin(), circles.end(),
                [](const HitCircle& c) { return !c.IsActive(); }),
            circles.end()
        );
    }

    bool RemoveCircle(size_t index) {
        if (index >= circles.size()) return false;
        circles[index].SetActive(false);
        return true;
    }

    bool RemoveCirclesByTag(const std::string& tag) {
        bool removed = false;
        for (auto& circle : circles) {
            if (circle.GetTag() == tag) {
                circle.SetActive(false);
                removed = true;
            }
        }
        if (removed && autoRemoveInactive) {
            RemoveInactiveCircles();
        }
        return removed;
    }

    bool RemoveCirclesBySource(int sourceId) {
        bool removed = false;
        for (auto& circle : circles) {
            if (circle.GetSourceId() == sourceId) {
                circle.SetActive(false);
                removed = true;
            }
        }
        if (removed && autoRemoveInactive) {
            RemoveInactiveCircles();
        }
        return removed;
    }

    void ClearAllCircles() { circles.clear(); }

    void FixAllCollisions() {
        for (size_t i = 0; i < circles.size(); ++i) {
            if (!circles[i].IsActive()) continue;
            for (size_t j = i + 1; j < circles.size(); ++j) {
                if (!circles[j].IsActive()) continue;
                circles[i].FixCollision(circles[j]);
            }
        }
    }

    void CheckAndApplyDamage(std::vector<Entity>& world, float deltaTime = 0.0f) {
        if (deltaTime > EPSILON) {
            Update(deltaTime);
        }
        
        spatialGrid.Clear();
        for (size_t i = 0; i < circles.size(); ++i) {
            spatialGrid.Insert(i, circles[i]);
        }

        for (auto& entity : world) {
            if (!entity.IsAlive()) continue;
            std::vector<size_t> candidates = spatialGrid.GetPotentialCollisions(entity);
            for (size_t idx : candidates) {
                if (idx < circles.size() && circles[idx].IsActive()) {
                    circles[idx].ApplyDamage(entity, globalCritChance);
                }
            }
        }
    }

    void DisplayCircles() const {
        std::cout << "Active Circles: " << GetActiveCircleCount() << " / " << circles.size() << '\n';
        for (size_t i = 0; i < circles.size(); ++i) {
            const auto& c = circles[i];
            if (c.IsActive()) {
                std::cout << std::fixed << std::setprecision(2);
                std::cout << "[" << i << "] Pos: (" << c.GetCenter().x << ", " 
                          << c.GetCenter().y << ") R: " << c.GetRadius()
                          << " Dmg: " << c.GetDamage();
                if (!c.GetTag().empty()) {
                    std::cout << " Tag: " << c.GetTag();
                }
                if (c.GetLifetime() > 0) {
                    std::cout << " Lifetime: " << c.GetRemainingTime();
                }
                std::cout << '\n';
            }
        }
    }

    size_t GetActiveCircleCount() const {
        return std::count_if(circles.begin(), circles.end(),
                             [](const HitCircle& c) { return c.IsActive(); });
    }

    size_t GetTotalCircleCount() const {
        return circles.size();
    }

    const HitCircle* GetCircle(size_t index) const {
        if (index >= circles.size()) return nullptr;
        return &circles[index];
    }

    HitCircle* GetCircle(size_t index) {
        if (index >= circles.size()) return nullptr;
        return &circles[index];
    }

    std::vector<size_t> GetCirclesByTag(const std::string& tag) const {
        std::vector<size_t> result;
        for (size_t i = 0; i < circles.size(); ++i) {
            if (circles[i].IsActive() && circles[i].GetTag() == tag) {
                result.push_back(i);
            }
        }
        return result;
    }

    std::vector<size_t> GetCirclesBySource(int sourceId) const {
        std::vector<size_t> result;
        for (size_t i = 0; i < circles.size(); ++i) {
            if (circles[i].IsActive() && circles[i].GetSourceId() == sourceId) {
                result.push_back(i);
            }
        }
        return result;
    }
};

int main() {
    HitCircleManager mgr(20.0f, 0.15f);
    
    mgr.AddCircle(Vector2(0, 0), 1e9f, 50.0f, DamageType::TRUE,
                  &MaximizeConfig(),
                  [](HitCircle& circle, Entity& entity, float damage, bool isCrit) {
                      std::cout << entity.GetName() << " took " << damage 
                                << (isCrit ? " CRITICAL" : "") << " damage!\n";
                      entity.TakeDamage(damage, DamageType::TRUE, isCrit);
                  },
                  -1, "global_damage", 5.0f);

    std::vector<Entity> world;
    world.emplace_back(100.0f, 10.0f, 5.0f, DamageType::PHYSICAL, 0.5f, "Warrior");
    world.emplace_back(80.0f, 5.0f, 15.0f, DamageType::MAGICAL, 0.4f, "Mage");
    world.emplace_back(120.0f, 15.0f, 5.0f, DamageType::PHYSICAL, 0.6f, "Tank");
    world.emplace_back(70.0f, 3.0f, 10.0f, DamageType::TRUE, 0.3f, "Rogue");
    world.emplace_back(90.0f, 8.0f, 12.0f, DamageType::MAGICAL, 0.5f, "Priest");
    
    for (size_t i = 0; i < world.size(); ++i) {
        world[i].SetPos(Vector2(static_cast<float>(i * 3 - 6), 0));
    }

    mgr.AddCircle(Vector2(-3, 2), 2.0f, 30.0f, DamageType::PHYSICAL,
                  nullptr, nullptr, 1, "fire_ring", 3.0f);
    
    mgr.AddCircle(Vector2(3, -2), 1.5f, 25.0f, DamageType::MAGICAL,
                  nullptr, nullptr, 2, "frost_ring", 4.0f);

    std::cout << "Initial state:\n";
    for (size_t i = 0; i < world.size(); ++i) {
        std::cout << world[i].GetName() << " HP: " << world[i].GetHp() 
                  << "/" << world[i].GetMaxHp() << "\n";
    }
    mgr.DisplayCircles();

    const float deltaTime = 0.5f;
    for (int frame = 0; frame < 10; ++frame) {
        std::cout << "\n--- Frame " << frame << " ---\n";
        
        mgr.CheckAndApplyDamage(world, deltaTime);
        
        for (size_t i = 0; i < world.size(); ++i) {
            if (world[i].IsAlive()) {
                std::cout << world[i].GetName() << " HP: " << world[i].GetHp() 
                          << " (" << world[i].GetHpPercentage() << "%)\n";
            } else {
                std::cout << world[i].GetName() << " DIED!\n";
            }
        }
        
        std::cout << "Active circles: " << mgr.GetActiveCircleCount() << "\n";
        
        if (frame % 3 == 0 && frame > 0) {
            mgr.AddCircle(Vector2(0, static_cast<float>(frame)), 1.0f, 15.0f,
                         DamageType::PHYSICAL, nullptr, nullptr, 
                         frame, "pulse_" + std::to_string(frame), 2.0f);
        }
    }

    return 0;
}