#pragma once

#include <vector>
#include <string>
#include <memory>
#include <map>
#include <unordered_map>
#include <cstring>
#include "gumbo.h"
#include "node.h"

namespace cutysoup {

// Forward declarations
class Tree;
class Node;

/**
 * Bulk Operations Engine - Eliminates Python binding overhead
 * 
 * Strategy: 
 * - Pre-index all elements by tag/class/id during parsing
 * - Bulk operations return results without individual Python calls
 * - Lazy evaluation with caching
 * - Zero Python-to-C++ call overhead for subsequent searches
 */
class BulkOperations {
public:
    explicit BulkOperations(const Tree* tree);
    
    // Pre-index the entire document (called once after parsing)
    void build_indices(GumboNode* root);
    
    // Ultra-fast bulk searches using pre-built indices
    std::vector<std::shared_ptr<Node>> find_all_by_tag_bulk(const std::string& tag_name);
    std::vector<std::shared_ptr<Node>> find_all_by_class_bulk(const std::string& class_name);
    std::shared_ptr<Node> find_by_id_bulk(const std::string& id);
    
    // General bulk find with attribute matching (eliminates Python boundary crossings)
    std::vector<std::shared_ptr<Node>> find_all_bulk(
        const std::string& tag_name,
        const std::map<std::string, std::string>& attributes
    );
    std::shared_ptr<Node> find_bulk(
        const std::string& tag_name,
        const std::map<std::string, std::string>& attributes
    );
    
    // Bulk operations for multiple searches (amortize Python call overhead)
    std::vector<std::vector<std::shared_ptr<Node>>> multi_find_all(
        const std::vector<std::string>& tag_names
    );
    
    // Fast element counting without creating Python objects
    size_t count_by_tag(const std::string& tag_name);
    size_t count_by_class(const std::string& class_name);
    
    // Advanced indexing methods for large documents
    std::vector<std::shared_ptr<Node>> find_by_attribute(const std::string& attr_name, const std::string& value);
    std::vector<std::shared_ptr<Node>> find_by_text_content(const std::string& text);
    std::shared_ptr<Node> get_nth_element(size_t index);
    
    // Performance metrics
    double get_index_build_time() const { return index_build_time_; }
    size_t get_indexed_element_count() const { return all_elements_.size(); }
    
    // Check if indices are built
    bool indices_ready() const { return indices_built_; }
    
    // Statistics
    struct IndexStats {
        size_t total_elements = 0;
        size_t unique_tags = 0;
        size_t unique_classes = 0;
        size_t unique_ids = 0;
        size_t index_memory_bytes = 0;
    };
    
    IndexStats get_stats() const;
    
private:
    const Tree* tree_;
    bool indices_built_ = false;
    
    // Pre-built indices for instant lookup
    std::unordered_map<std::string, std::vector<GumboNode*>> tag_index_;
    std::unordered_map<std::string, std::vector<GumboNode*>> class_index_;
    std::unordered_map<std::string, GumboNode*> id_index_;
    
    // Advanced indexing features
    std::unordered_map<std::string, std::vector<GumboNode*>> attribute_index_;  // attr=value pairs
    std::unordered_map<std::string, std::vector<GumboNode*>> text_index_;       // text content
    std::vector<GumboNode*> all_elements_;                                      // All elements for nth operations
    
    // Performance metrics
    double index_build_time_ = 0.0;
    
    // Statistics
    IndexStats stats_;
    
    // Index building helpers
    void index_element(GumboNode* node);
    void extract_classes(const char* class_attr, GumboNode* node);
    
    // Bulk Node creation
    std::vector<std::shared_ptr<Node>> create_nodes_from_raw(
        const std::vector<GumboNode*>& raw_nodes
    ) const;
    
    // Memory estimation
    size_t estimate_memory_usage() const;
};

} // namespace cutysoup
