#pragma once

#include <cstdint>
#include <vector>
#include <chrono>
#include <iostream>
#include <cmath>
#include <memory>
#include "spatial/SpatialGrid.hpp"
#include "combat/CombatEngine.hpp"

namespace FreeExile {

constexpr int32_t MAX_ENTITIES = 100000;
constexpr int32_t ZONE_CAPACITY = MAX_ENTITIES;

struct EntityPoolSoA {
    int32_t entity_ids[MAX_ENTITIES];
    float pos_x[MAX_ENTITIES];
    float pos_y[MAX_ENTITIES];
    float pos_z[MAX_ENTITIES];
    float vel_x[MAX_ENTITIES];
    float vel_y[MAX_ENTITIES];
    float move_speed[MAX_ENTITIES];
    float collision_radius[MAX_ENTITIES];
    float hp[MAX_ENTITIES];
    FiveElements element[MAX_ENTITIES];
    uint32_t flags[MAX_ENTITIES]; // 1: Active, 2: iFrame, 4: Dirty
    int32_t next_in_cell[MAX_ENTITIES];
    int32_t count = 0;
};

using NativeEntityPool = EntityPoolSoA;

class ZoneServer {
private:
    std::unique_ptr<EntityPoolSoA> pool;
    FlatSpatialGrid grid;
    uint64_t current_tick = 0;

public:
    explicit ZoneServer(float cell_size = 64.0f)
        : pool(std::make_unique<EntityPoolSoA>()), grid(cell_size) {
        pool->count = 0;
    }

    void reset(float cell_size = 64.0f) {
        if (!pool) pool = std::make_unique<EntityPoolSoA>();
        pool->count = 0;
        current_tick = 0;
        grid.cell_size = cell_size > 0.0f ? cell_size : 64.0f;
        grid.clear();
    }

    int32_t add_entity(
        int32_t entity_id,
        float x,
        float y,
        float z = 0.0f,
        float move_speed = 6.0f,
        float collision_radius = 0.5f,
        float hp = 1000.0f,
        FiveElements elem = FiveElements::KIM
    ) {
        if (!pool || pool->count < 0 || pool->count >= MAX_ENTITIES) return -1;
        int32_t idx = pool->count++;
        pool->entity_ids[idx] = entity_id;
        pool->pos_x[idx] = x;
        pool->pos_y[idx] = y;
        pool->pos_z[idx] = z;
        pool->vel_x[idx] = 0.0f;
        pool->vel_y[idx] = 0.0f;
        pool->move_speed[idx] = move_speed;
        pool->collision_radius[idx] = collision_radius;
        pool->hp[idx] = hp;
        pool->element[idx] = elem;
        pool->flags[idx] = 1;
        pool->next_in_cell[idx] = -1;
        // Insert into spatial grid immediately so query_aoi works before the first tick
        grid.insert_entity(idx, x, y, pool->next_in_cell);
        return idx;
    }

    int32_t spawn_player(int32_t entity_id, float x, float y, float z, FiveElements elem) {
        return add_entity(entity_id, x, y, z, 6.0f, 0.5f, 1000.0f, elem);
    }

    void set_move_vector(int32_t idx, float vx, float vy) {
        if (pool && idx >= 0 && idx < pool->count) {
            if (std::isnan(vx) || std::isinf(vx)) vx = 0.0f;
            if (std::isnan(vy) || std::isinf(vy)) vy = 0.0f;
            pool->vel_x[idx] = vx;
            pool->vel_y[idx] = vy;
        }
    }

    void trigger_phantom_evasion(int32_t idx, bool active) {
        if (pool && idx >= 0 && idx < pool->count) {
            if (active) pool->flags[idx] |= 2;
            else pool->flags[idx] &= ~2;
        }
    }

    void step_tick(float dt) {
        if (std::isnan(dt) || dt <= 0.0f) return;
        if (dt > 1.0f) dt = 1.0f; // Prevent simulation explosion on lag spikes
        if (!pool) return;

        current_tick++;
        grid.clear();

        const int32_t total = pool->count;

        // 1. Vectorized SIMD Displacement Update
        #pragma omp simd
        for (int32_t i = 0; i < total; ++i) {
            if (!(pool->flags[i] & 1)) continue;

            float vx = pool->vel_x[i];
            float vy = pool->vel_y[i];
            float mag = std::sqrt(vx * vx + vy * vy);

            if (mag > 0.0001f) {
                float norm_x = vx / mag;
                float norm_y = vy / mag;
                float dist = pool->move_speed[i] * dt;
                pool->pos_x[i] += norm_x * dist;
                pool->pos_y[i] += norm_y * dist;
                pool->flags[i] |= 4; // Set dirty
            }
        }

        // 2. Spatial Grid Insertion (Zero-Allocation)
        for (int32_t i = 0; i < total; ++i) {
            if (!(pool->flags[i] & 1)) continue;
            grid.insert_entity(i, pool->pos_x[i], pool->pos_y[i], pool->next_in_cell);
        }
    }

    int32_t query_aoi(
        float cx,
        float cy,
        int32_t radius_cells,
        int32_t* out_ids,
        int32_t max_results
    ) const {
        if (!pool || !out_ids || max_results <= 0) return 0;
        return grid.query_aoi(cx, cy, radius_cells, pool->next_in_cell, pool->entity_ids, out_ids, max_results);
    }

    int32_t query_aoi(float cx, float cy, int32_t* out_ids, int32_t max_results) const {
        return query_aoi(cx, cy, 1, out_ids, max_results);
    }

    bool get_entity_pos(int32_t idx, float* out_x, float* out_y, float* out_z) const {
        if (pool && idx >= 0 && idx < pool->count) {
            if (out_x) *out_x = pool->pos_x[idx];
            if (out_y) *out_y = pool->pos_y[idx];
            if (out_z) *out_z = pool->pos_z[idx];
            return true;
        }
        return false;
    }

    uint64_t get_tick() const { return current_tick; }
    int32_t get_count() const { return pool ? pool->count : 0; }
    EntityPoolSoA& get_pool() { return *pool; }
    const EntityPoolSoA& get_pool() const { return *pool; }
    FlatSpatialGrid& get_grid() { return grid; }
    const FlatSpatialGrid& get_grid() const { return grid; }
};

} // namespace FreeExile
