#pragma once

#include <vector>
#include <unordered_set>
#include <nlohmann/json.hpp>

using json = nlohmann::json;

namespace warfactory {

// Forward declarations
class GMap;

/**
 * @brief Immutable feature set for tile feature collections
 *
 * Encapsulates a collection of features with caching and usage tracking.
 * Once created, the feature collection cannot be modified, ensuring consistency
 * and enabling aggressive caching optimizations.
 */
class FeatureSet {
private:
    // ========================================
    // IMMUTABLE FEATURE DATA
    // ========================================
    std::vector<uint32_t> feature_ids;          // Feature IDs in this set (immutable)
    uint32_t primary_feature_id;                // Most significant feature (0 = none)

    // ========================================
    // USAGE TRACKING (mutable for tracking)
    // ========================================
    mutable std::unordered_set<uint32_t> used_by_tiles;    // Tile indices using this set

    // ========================================
    // PERFORMANCE CACHE (mutable for lazy evaluation)
    // ========================================
    mutable float cached_budget_score;          // Cached total budget score
    mutable bool budget_cache_valid;            // Cache validity flag

public:
    /**
     * @brief Constructor - creates immutable feature set
     * @param features Vector of feature IDs
     * @param primary_feature Primary feature ID (0 = auto-determine)
     */
    explicit FeatureSet(const std::vector<uint32_t>& features, uint32_t primary_feature = 0);

    /**
     * @brief Constructor - single feature set
     * @param feature_id Single feature ID
     */
    explicit FeatureSet(uint32_t feature_id);

    /**
     * @brief Destructor
     */
    ~FeatureSet() = default;

    // ========================================
    // FEATURE ACCESS - Read-only interface
    // ========================================

    /**
     * @brief Get all feature IDs in set
     * @return Immutable reference to feature vector
     */
    const std::vector<uint32_t>& getFeatureIds() const { return feature_ids; }

    /**
     * @brief Get primary feature ID
     * @return Primary feature ID (0 if none)
     */
    uint32_t getPrimaryFeatureId() const { return primary_feature_id; }

    /**
     * @brief Check if set contains specific feature
     * @param feature_id Feature ID to check
     * @return true if feature is in set
     */
    bool hasFeature(uint32_t feature_id) const;

    /**
     * @brief Get number of features in set
     * @return Feature count
     */
    size_t getFeatureCount() const { return feature_ids.size(); }

    /**
     * @brief Check if set is empty
     * @return true if no features
     */
    bool isEmpty() const { return feature_ids.empty(); }

    // ========================================
    // BUDGET CALCULATIONS - Cached for performance
    // ========================================

    /**
     * @brief Calculate total budget score for this feature set
     * @param world_map Reference to world map for feature lookups
     * @return Total budget contribution (cached after first call)
     */
    float getBudgetScore(const GMap* world_map) const;

    /**
     * @brief Invalidate budget cache (call when features change globally)
     */
    void invalidateBudgetCache() const;

    // ========================================
    // USAGE TRACKING - For memory management
    // ========================================

    /**
     * @brief Mark tile as using this feature set
     * @param tile_index Tile index that uses this set
     */
    void addUsage(uint32_t tile_index) const;

    /**
     * @brief Remove tile usage of this feature set
     * @param tile_index Tile index that no longer uses this set
     */
    void removeUsage(uint32_t tile_index) const;

    /**
     * @brief Get number of tiles using this set
     * @return Usage count
     */
    size_t getUsageCount() const { return used_by_tiles.size(); }

    /**
     * @brief Check if any tiles are using this set
     * @return true if set is in use
     */
    bool isInUse() const { return !used_by_tiles.empty(); }

    /**
     * @brief Get tiles using this set
     * @return Set of tile indices
     */
    const std::unordered_set<uint32_t>& getUsedByTiles() const { return used_by_tiles; }

    // ========================================
    // COMPARISON AND HASHING - For deduplication
    // ========================================

    /**
     * @brief Check if two feature sets are identical
     * @param other Other feature set
     * @return true if feature collections are identical
     */
    bool operator==(const FeatureSet& other) const;

    /**
     * @brief Check if two feature sets are different
     * @param other Other feature set
     * @return true if feature collections differ
     */
    bool operator!=(const FeatureSet& other) const { return !(*this == other); }

    /**
     * @brief Generate hash for this feature set
     * @return Hash value for deduplication
     */
    size_t getHash() const;

    // ========================================
    // UTILITIES AND DEBUGGING
    // ========================================

    /**
     * @brief Get debug information about this set
     * @return JSON with set details and usage statistics
     */
    json getDebugInfo() const;

    /**
     * @brief Validate feature set consistency
     * @param world_map Reference to world map for validation
     * @return true if set is valid
     */
    bool validate(const GMap* world_map) const;

    /**
     * @brief Get memory footprint of this set
     * @return Memory usage in bytes
     */
    size_t getMemoryFootprint() const;

private:
    /**
     * @brief Determine primary feature from feature list
     * @param world_map Reference to world map for feature lookups
     * @return Feature ID with highest budget score
     */
    uint32_t determinePrimaryFeature(const GMap* world_map) const;
};

} // namespace warfactory

// Hash function for FeatureSet (enables use in unordered containers)
namespace std {
    template<>
    struct hash<warfactory::FeatureSet> {
        size_t operator()(const warfactory::FeatureSet& feature_set) const {
            return feature_set.getHash();
        }
    };
}