#pragma once
#include <functional>
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>

#include "Core/Models.h"

// -------------------------------------------------------------------------
// EntityCategoryProjector
// Merges the three entity lists (near / far / slow) and projects them into
// typed category vectors.  All methods are static — no instance state.
// -------------------------------------------------------------------------

class EntityCategoryProjector {
public:
    // ------------------------------------------------------------------
    // Public projection methods
    // ------------------------------------------------------------------

    static std::vector<DayZPlayerEntry> BuildPlayers(
        const std::vector<DayZNearEntityEntry>& near,
        const std::vector<DayZFarEntityEntry>&  far,
        const std::vector<DayZSlowEntityEntry>& slow,
        const std::unordered_map<uint32_t, std::string>& scoreboardNames,
        std::function<bool(uint64_t)>        deadResolver,
        std::function<std::string(uint64_t)> heldItemResolver,
        std::function<float(uint64_t)>       healthResolver,
        std::function<bool(uint64_t)>        adminResolver = nullptr);

    static std::vector<DayZAnimalEntry> BuildAnimals(
        const std::vector<DayZNearEntityEntry>& near,
        const std::vector<DayZFarEntityEntry>&  far,
        const std::vector<DayZSlowEntityEntry>& slow);

    static std::vector<DayZZombieEntry> BuildZombies(
        const std::vector<DayZNearEntityEntry>& near,
        const std::vector<DayZFarEntityEntry>&  far,
        const std::vector<DayZSlowEntityEntry>& slow);

    static std::vector<DayZCarAndBoatEntry> BuildCarsAndBoats(
        const std::vector<DayZNearEntityEntry>& near,
        const std::vector<DayZFarEntityEntry>&  far,
        const std::vector<DayZSlowEntityEntry>& slow);

    static std::vector<DayZOtherEntityEntry> BuildOtherEntities(
        const std::vector<DayZNearEntityEntry>& near,
        const std::vector<DayZFarEntityEntry>&  far,
        const std::vector<DayZSlowEntityEntry>& slow);

private:
    // ------------------------------------------------------------------
    // Unified flattened view of one entity from any list
    // ------------------------------------------------------------------
    struct EntityProjection {
        uint64_t               address        = 0;
        uint32_t               networkId      = 0;
        std::optional<Vector3> position;
        std::optional<float>   headingDegrees;
        std::string            entityName;
        std::string            typeName;
        std::string            configName;
        std::string            modelName;
    };

    // ------------------------------------------------------------------
    // Internal helpers
    // ------------------------------------------------------------------

    /// Merge all three lists, deduplicate by address, and sort by
    /// (entityName, address) for deterministic ordering.
    static std::vector<EntityProjection> EnumerateEntities(
        const std::vector<DayZNearEntityEntry>& near,
        const std::vector<DayZFarEntityEntry>&  far,
        const std::vector<DayZSlowEntityEntry>& slow);

    /// Returns true if typeName belongs to one of the explicitly-handled
    /// categories (player, animal, zombie, vehicle).
    static bool IsKnownCategoryType(const std::string& typeName);

    /// Returns the VehicleKind for known vehicle type names, or nullopt.
    static std::optional<VehicleKind> GetVehicleKind(const std::string& typeName);

    // Type-name constants
    static constexpr const char* kTypePlayer   = "dayzplayer";
    static constexpr const char* kTypeAnimal   = "dayzanimal";
    static constexpr const char* kTypeZombie   = "dayzinfected";
    static constexpr const char* kTypeBoat     = "boat";
    static constexpr const char* kTypeCar      = "car";
    static constexpr const char* kTypeHeli     = "helicopter";
    static constexpr const char* kTypePlane    = "plane";
};
