#ifndef TACTICAL_SHOOTER_SIMULATION_SERVER_H
#define TACTICAL_SHOOTER_SIMULATION_SERVER_H

#include "entity.h"
#include "hit_detection.h"
#include "movement_component.h"
#include "state_serializer.h"

#include <godot_cpp/classes/object.hpp>
#include <godot_cpp/classes/ref.hpp>
#include <godot_cpp/variant/dictionary.hpp>
#include <godot_cpp/variant/packed_byte_array.hpp>
#include <godot_cpp/variant/vector3.hpp>

#include <array>
#include <cstdint>
#include <unordered_map>
#include <vector>

namespace tactical_shooter {

/**
 * Position history ring-buffer entry — stores entity positions at a given tick.
 * Used for lag compensation: we can rewind targets to where they were when a
 * shot was fired, even if they've since moved.
 */
struct TickPositionSnapshot {
    uint32_t tick = 0;
    struct Entry {
        uint16_t entity_id;
        godot::Vector3 position;
        uint16_t flags;
    };
    std::vector<Entry> entries;
};

/**
 * A pending weapon fire with time-of-fire information for lag compensation.
 */
struct PendingFire {
    uint16_t shooter_id = 0xFFFF;
    uint32_t fire_tick = 0;       // Server tick at which the fire was queued
    uint32_t input_sequence = 0;  // Client's input sequence (for reconciliation)
};

/**
 * SimulationServer — the heart of the tactical shooter simulation.
 *
 * This singleton manages the entire 128Hz game simulation loop from C++,
 * entirely avoiding GDScript VM overhead in the hot path.
 *
 * GDScript integration:
 *   var server = SimulationServer.new()
 *   server.set_tick_rate(128)
 *   server.start()
 *   # In _process():
 *   while server.can_tick(delta):
 *       var serialized = server.tick()
 *       # send serialized to network layer
 *
 * Architecture:
 *   - All entity simulation (movement, hit detection) runs in C++
 *   - Serialized state is handed to GDScript for network transport
 *   - Player input arrives from GDScript, gets applied per-entity
 *   - Lag compensation stores a ring buffer of pre-move positions each tick
 *   - Benchmark hooks for the 128Hz load test (t_f671f48a)
 */
class SimulationServer : public godot::Object {
    GDCLASS(SimulationServer, godot::Object)

public:
    SimulationServer();
    ~SimulationServer();

    // ---- Lifecycle (GDScript API) ------------------------------------------

    /**
     * Set the simulation tick rate (calls per second).
     * Default: 128 (for 128Hz tick).
     */
    void set_tick_rate(uint32_t hz);

    uint32_t get_tick_rate() const { return tick_hz_; }

    /**
     * Start the simulation. Creates initial entities if count > 0.
     */
    void start();

    /**
     * Stop the simulation. Clears all entities and resets state.
     */
    void stop();

    /**
     * Check if the simulation is running.
     */
    bool is_running() const { return running_; }

    /**
     * Accrue time and check if a tick is due.
     * Call this in _process(delta) and call tick() while this returns true.
     *
     * Example (GDScript):
     *   while server.can_tick(delta):
     *       input = server.tick()
     */
    bool can_tick(float delta);

    /**
     * Advance one simulation tick.
     * Returns serialized snapshot as a PackedByteArray (or empty if no tick due).
     */
    godot::PackedByteArray tick();

    // ---- Entity Management (GDScript API) -----------------------------------

    /**
     * Spawn a new entity at the given position.
     * Returns the entity ID (0..65535) or UINT16_MAX on failure.
     */
    uint16_t spawn_entity(const godot::Vector3 &position);

    /**
     * Despawn an entity by ID.
     */
    void despawn_entity(uint16_t entity_id);

    /**
     * Get an entity by ID. Returns null if not found.
     */
    godot::Ref<Entity> get_entity(uint16_t entity_id);

    /**
     * Get number of active entities.
     */
    uint16_t get_entity_count() const;

    /**
     * Get entity IDs of all active entities as an array.
     */
    godot::Array get_entity_ids() const;

    // ---- Input (GDScript API) -----------------------------------------------

    /**
     * Apply input for a specific entity on the next tick.
     * Call this from _process() as input arrives.
     *
     * @param entity_id  Target entity
     * @param input_dict Dictionary with keys: "move_direction" (Vector3),
     *   "look_yaw" (float), "look_pitch" (float), "jump" (bool),
     *   "crouch" (bool), "sprint" (bool), "shoot" (bool), "aim" (bool),
     *   "input_sequence" (int)
     */
    void apply_input(uint16_t entity_id, const godot::Dictionary &input_dict);

    /**
     * Queue a weapon fire from an entity.
     * Fires on the next tick.
     */
    void fire_weapon(uint16_t entity_id);

    // ---- Movement Configuration (GDScript API) -------------------------------

    void set_movement_walk_speed(float speed);
    void set_movement_sprint_speed(float speed);
    void set_movement_crouch_speed(float speed);
    void set_movement_acceleration(float accel);
    void set_movement_jump_velocity(float vel);
    void set_movement_gravity(float gravity);

    void set_movement_config(const godot::Dictionary &config);

    // ---- Weapon Configuration (GDScript API) ----------------------------------

    /**
     * Set the weapon damage profile for hitscan weapons.
     * Called from GDScript to define weapon stats.
     *
     * Dictionary keys:
     *   base_damage (float)       — base damage per shot
     *   head_multiplier (float)   — damage multiplier for head hits
     *   body_multiplier (float)   — damage multiplier for body hits
     *   arm_multiplier (float)    — damage multiplier for arm hits
     *   leg_multiplier (float)    — damage multiplier for leg hits
     *   max_range (float)         — maximum shot distance in units
     *   spread_degrees (float)    — random spread per shot (degrees)
     *   fire_rate_hz (float)      — max shots per second
     */
    void set_weapon_config(const godot::Dictionary &config);

    // ---- Lag Compensation Configuration (GDScript API) -------------------------

    /**
     * Set the depth of the position history ring buffer (in ticks).
     * More ticks = can compensate for higher latency but uses more memory.
     * Default: 64 ticks (~500ms at 128Hz, enough for any reasonable ping).
     */
    void set_history_depth(uint32_t ticks);

    // ---- Damaged / Death Signal (GDScript API via Dictionary return) -----------

    /**
     * Get the most recent hit result from processing combat.
     * Returns Dictionary with: hit (bool), entity_id (int), damage (float),
     *   distance (float), hitbox_id (int), killed (bool).
     * Empty if no shot was processed this tick.
     */
    godot::Dictionary get_last_hit_result() const;

    // ---- Benchmark / Stats (GDScript API) ------------------------------------

    /**
     * Get tick timing statistics for benchmarking.
     * Returns Dictionary with: "last_tick_usec" (int), "avg_tick_usec" (float),
     *   "peak_tick_usec" (float), "tick_count" (int), "entity_count" (int)
     */
    godot::Dictionary get_stats() const;

    /**
     * Reset benchmark statistics.
     */
    void reset_stats();

    /**
     * Populate simulation with N bots for load testing.
     * Places them in a grid pattern.
     */
    void populate_bots(uint16_t count);

protected:
    static void _bind_methods();

private:
    // ---- Internal tick logic ------------------------------------------------

    /**
     * Advance the simulation by one tick (fixed timestep).
     */
    void process_tick();

    /**
     * Update all entity movement.
     */
    void update_movement();

    /**
     * Process all queued weapon fires.
     */
    void update_combat();

    /**
     * Collect and serialize the current simulation state.
     */
    godot::PackedByteArray serialize_state();

    // ---- Helpers ------------------------------------------------------------

    /**
     * Find the lowest available entity ID.
     */
    uint16_t allocate_entity_id();

    // ---- Lag Compensation (Position History) ----------------------------------

    /**
     * Record pre-move positions of all living entities for the given tick.
     * Called at the start of process_tick(), BEFORE update_movement().
     */
    void record_position_history(uint32_t tick);

    /**
     * Rewind target entities to positions stored at a given tick for hit
     * detection, then restore them back.
     */
    struct RewindState {
        std::vector<TickPositionSnapshot::Entry> saved;
    };
    RewindState begin_rewind(uint32_t target_tick);
    void end_rewind(const RewindState &state);

    /**
     * Process a single compensated fire: rewind, check hit, restore.
     */
    void process_compensated_fire(const PendingFire &fire);

    // ---- State --------------------------------------------------------------

    bool running_ = false;
    uint32_t tick_hz_ = 128;
    float tick_interval_ = 1.0f / 128.0f;
    float time_accumulator_ = 0.0f;

    // Tick counter
    uint32_t current_tick_ = 0;

    // Entity storage (by ID)
    std::vector<godot::Ref<Entity>> entities_by_id_;
    std::vector<Entity *> living_entity_ptrs_;  // raw ptrs for hot loop

    // Systems
    MovementComponent movement_;
    HitDetection hit_detection_;
    StateSerializer serializer_;

    // Queued input (applied on next tick, cleared after)
    struct QueuedInput {
        EntityInput input;
        bool pending = false;
    };
    std::vector<QueuedInput> queued_inputs_;

    // Queued weapon fires with lag-compensation info
    std::vector<PendingFire> pending_fires_;
    WeaponDamage weapon_config_;
    float weapon_fire_rate_hz_ = 10.0f;

    // Last hit result (queried by GDScript for feedback)
    struct LastHitResult {
        bool hit = false;
        uint16_t entity_id = 0xFFFF;
        float damage = 0.0f;
        float distance = 0.0f;
        uint8_t hitbox_id = 0;
        bool killed = false;
    };
    LastHitResult last_hit_result_;

    // Position history ring buffer (for lag compensation)
    std::vector<TickPositionSnapshot> position_history_;
    uint32_t history_depth_ = 64;
    uint32_t history_write_index_ = 0;

    // Last serialized snapshot per entity (for delta compression)
    std::unordered_map<uint16_t, EntitySnapshot> last_snapshots_;

    // Benchmark stats
    uint64_t tick_count_ = 0;
    uint64_t last_tick_usec_ = 0;
    uint64_t total_tick_usec_ = 0;
    uint64_t max_tick_usec_ = 0;
};

} // namespace tactical_shooter

#endif // TACTICAL_SHOOTER_SIMULATION_SERVER_H
