#ifndef TACTICAL_SHOOTER_ENTITY_H
#define TACTICAL_SHOOTER_ENTITY_H

#include <cstdint>
#include <godot_cpp/classes/ref_counted.hpp>
#include <godot_cpp/variant/vector3.hpp>

namespace tactical_shooter {

/**
 * Maximum number of entities in a single simulation.
 * Hard cap prevents memory exhaustion and bounds snapshot sizes.
 */
static constexpr uint16_t kMaxEntities = 256;

/**
 * Per-entity flags packed into a single uint16 for compact serialization.
 */
enum EntityFlags : uint16_t {
    ENTITY_FLAG_NONE     = 0,
    ENTITY_FLAG_ALIVE    = 1 << 0,
    ENTITY_FLAG_GROUNDED = 1 << 1,
    ENTITY_FLAG_CROUCHING = 1 << 2,
    ENTITY_FLAG_SPRINTING = 1 << 3,
    ENTITY_FLAG_AIMING   = 1 << 4,
};

/**
 * Entity input state for a single tick — what the client sent this frame.
 * Compact enough to be sent at 128Hz.
 */
struct EntityInput {
    godot::Vector3 move_direction;  // normalized, quantized
    float look_yaw = 0.0f;         // degrees
    float look_pitch = 0.0f;       // degrees
    bool jump = false;
    bool crouch = false;
    bool sprint = false;
    bool shoot = false;
    bool aim = false;
    uint32_t input_sequence = 0;    // for client-authoritative input validation
};

/**
 * Full entity state snapshot at one point in time.
 * Used as the "base" for delta compression — serialize only the fields
 * that changed since the last acknowledged snapshot.
 */
struct EntitySnapshot {
    uint16_t entity_id = 0;
    uint16_t flags = ENTITY_FLAG_ALIVE;

    // Transform (quantized for network)
    godot::Vector3 position;
    godot::Vector3 velocity;
    float yaw = 0.0f;      // degrees, -180..180
    float pitch = 0.0f;    // degrees, -90..90

    // Game state
    float health = 100.0f;
    float armor = 0.0f;
    uint8_t weapon_id = 0;
    uint16_t ammo = 0;

    // Client-authoritative input (for reconciliation)
    uint32_t last_processed_input = 0;

    /// Bit-field that tells serializers which fields changed from last base
    enum ChangeMask : uint32_t {
        CHANGED_NONE        = 0,
        CHANGED_POSITION    = 1 << 0,
        CHANGED_VELOCITY    = 1 << 1,
        CHANGED_ROTATION    = 1 << 2,
        CHANGED_HEALTH      = 1 << 3,
        CHANGED_ARMOR       = 1 << 4,
        CHANGED_WEAPON      = 1 << 5,
        CHANGED_AMMO        = 1 << 6,
        CHANGED_FLAGS       = 1 << 7,
        CHANGED_INPUT       = 1 << 8,
        CHANGED_ALL         = 0xFFFFFFFF,
    };
};

/**
 * Simulation entity — internal game object managed by SimulationServer.
 *
 * Registered as a RefCounted so GDScript can hold references and pass
 * them around without manual memory management.
 */
class Entity : public godot::RefCounted {
    GDCLASS(Entity, godot::RefCounted)

public:
    Entity();
    ~Entity() = default;

    // ---- GDScript API ----------------------------------------------------

    void set_position(const godot::Vector3 &p_position);
    godot::Vector3 get_position() const;

    void set_velocity(const godot::Vector3 &p_velocity);
    godot::Vector3 get_velocity() const;

    void set_health(float p_health);
    float get_health() const;

    void set_armor(float p_armor);
    float get_armor() const;

    void set_yaw(float p_yaw);
    float get_yaw() const;

    void set_pitch(float p_pitch);
    float get_pitch() const;

    bool is_alive() const;
    void kill();

    uint16_t get_entity_id() const { return entity_id_; }
    void set_entity_id(uint16_t id) { entity_id_ = id; }

    uint16_t get_flags() const { return flags_; }
    void set_flags(uint16_t f) { flags_ = f; }

    // ---- Internal API (not exposed to GDScript) --------------------------

    /// Reset entity to spawn state
    void reset(const godot::Vector3 &spawn_position);

    /// Apply input for this tick (called by SimulationServer)
    void apply_input(const EntityInput &input);

    /// Capture current state into a snapshot
    EntitySnapshot capture_snapshot() const;

    /// Compute delta against a base snapshot for serialization
    EntitySnapshot::ChangeMask compute_change_mask(const EntitySnapshot &base) const;

    /// Internal state access (for movement/hit systems)
    const EntityInput &last_input() const { return last_input_; }
    float crouch_height() const { return is_crouching() ? 0.75f : 1.0f; }

protected:
    static void _bind_methods();

private:
    bool is_crouching() const { return flags_ & ENTITY_FLAG_CROUCHING; }
    bool is_sprinting() const { return flags_ & ENTITY_FLAG_SPRINTING; }
    bool is_aiming() const    { return flags_ & ENTITY_FLAG_AIMING; }

    uint16_t entity_id_ = 0;
    uint16_t flags_ = ENTITY_FLAG_ALIVE;

    // World state
    godot::Vector3 position_;
    godot::Vector3 velocity_;
    float yaw_ = 0.0f;
    float pitch_ = 0.0f;

    // Gameplay
    float health_ = 100.0f;
    float armor_ = 0.0f;
    uint8_t weapon_id_ = 0;
    uint16_t ammo_ = 0;

    // Input state
    EntityInput last_input_;
};

} // namespace tactical_shooter

#endif // TACTICAL_SHOOTER_ENTITY_H
