#include "node.h"
#include "tree.h"
#include "simple_parallel_walker.h"
#include "optimized_parallel_walker.h"
#include "ultra_fast_walker.h"
#include "lightning_walker.h"
#include "bulk_operations.h"
#include "zero_context_batch.h"
#include <algorithm>
#include <cctype>

namespace cutysoup {

Node::Node(GumboNode* gumbo_node, const Tree* tree) 
    : gumbo_node_(gumbo_node), tree_(tree) {
}

const GumboVector* Node::get_child_vector(GumboNode* node) {
    if (!node) {
        return nullptr;
    }
    if (node->type == GUMBO_NODE_ELEMENT) {
        return &node->v.element.children;
    }
    if (node->type == GUMBO_NODE_DOCUMENT) {
        return &node->v.document.children;
    }
    return nullptr;
}

std::string Node::trim_whitespace(const std::string& value) {
    const size_t start = value.find_first_not_of(" \t\n\r\f\v");
    if (start == std::string::npos) {
        return "";
    }
    const size_t end = value.find_last_not_of(" \t\n\r\f\v");
    return value.substr(start, end - start + 1);
}

std::string Node::get_name() const {
    if (!gumbo_node_ || gumbo_node_->type != GUMBO_NODE_ELEMENT) {
        return "";
    }
    return gumbo_normalized_tagname(gumbo_node_->v.element.tag);
}

std::map<std::string, std::string> Node::get_attributes() const {
    std::map<std::string, std::string> attrs;
    
    if (!gumbo_node_ || gumbo_node_->type != GUMBO_NODE_ELEMENT) {
        return attrs;
    }
    
    const GumboVector* attributes = &gumbo_node_->v.element.attributes;
    for (unsigned int i = 0; i < attributes->length; ++i) {
        GumboAttribute* attr = static_cast<GumboAttribute*>(attributes->data[i]);
        attrs[attr->name] = attr->value;
    }
    
    return attrs;
}

std::string Node::get_attribute(const std::string& name) const {
    if (!gumbo_node_ || gumbo_node_->type != GUMBO_NODE_ELEMENT) {
        return "";
    }
    
    const GumboVector* attributes = &gumbo_node_->v.element.attributes;
    for (unsigned int i = 0; i < attributes->length; ++i) {
        GumboAttribute* attr = static_cast<GumboAttribute*>(attributes->data[i]);
        if (name == attr->name) {
            return attr->value;
        }
    }
    
    return "";
}

std::string Node::get_text() const {
    std::string result;
    collect_text_recursive(gumbo_node_, result);
    return result;
}

std::string Node::get_text_with_separator(const std::string& separator) const {
    std::vector<std::string> parts;
    collect_text_parts_recursive(gumbo_node_, parts);

    if (parts.empty()) {
        return "";
    }
    if (parts.size() == 1) {
        return parts[0];
    }

    std::string result;
    for (size_t i = 0; i < parts.size(); ++i) {
        if (i > 0) {
            result += separator;
        }
        result += parts[i];
    }
    return result;
}

std::string Node::get_text_stripped() const {
    return trim_whitespace(get_text());
}

std::string Node::get_text_with_separator_stripped(const std::string& separator) const {
    return trim_whitespace(get_text_with_separator(separator));
}

std::vector<std::string> Node::get_strings() const {
    std::vector<std::string> strings;
    collect_strings_recursive(gumbo_node_, strings);
    return strings;
}

std::vector<std::string> Node::get_stripped_strings() const {
    std::vector<std::string> strings = get_strings();
    std::vector<std::string> stripped;
    stripped.reserve(strings.size());

    for (const auto& value : strings) {
        std::string trimmed = trim_whitespace(value);
        if (!trimmed.empty()) {
            stripped.push_back(trimmed);
        }
    }

    return stripped;
}

std::string Node::get_direct_text() const {
    std::string direct_text;
    const GumboVector* children = get_child_vector(gumbo_node_);
    if (!children) {
        return direct_text;
    }

    for (unsigned int i = 0; i < children->length; ++i) {
        GumboNode* child = static_cast<GumboNode*>(children->data[i]);
        if (child && (child->type == GUMBO_NODE_TEXT || child->type == GUMBO_NODE_WHITESPACE) && child->v.text.text) {
            direct_text += child->v.text.text;
        }
    }

    return direct_text;
}

void Node::collect_text_recursive(GumboNode* node, std::string& result) const {
    if (!node) return;
    
    if (node->type == GUMBO_NODE_TEXT || node->type == GUMBO_NODE_WHITESPACE) {
        result += node->v.text.text;
    } else if (const GumboVector* children = get_child_vector(node)) {
        for (unsigned int i = 0; i < children->length; ++i) {
            collect_text_recursive(static_cast<GumboNode*>(children->data[i]), result);
        }
    }
}

void Node::collect_text_parts_recursive(GumboNode* node, std::vector<std::string>& parts) const {
    if (!node) {
        return;
    }

    if ((node->type == GUMBO_NODE_TEXT || node->type == GUMBO_NODE_WHITESPACE) && node->v.text.text) {
        parts.push_back(node->v.text.text);
    } else if (const GumboVector* children = get_child_vector(node)) {
        for (unsigned int i = 0; i < children->length; ++i) {
            collect_text_parts_recursive(static_cast<GumboNode*>(children->data[i]), parts);
        }
    }
}

void Node::collect_strings_recursive(GumboNode* node, std::vector<std::string>& strings) const {
    if (!node) {
        return;
    }

    if ((node->type == GUMBO_NODE_TEXT || node->type == GUMBO_NODE_WHITESPACE) && node->v.text.text) {
        strings.push_back(node->v.text.text);
    } else if (const GumboVector* children = get_child_vector(node)) {
        for (unsigned int i = 0; i < children->length; ++i) {
            collect_strings_recursive(static_cast<GumboNode*>(children->data[i]), strings);
        }
    }
}

bool Node::is_element() const {
    return gumbo_node_ && gumbo_node_->type == GUMBO_NODE_ELEMENT;
}

bool Node::is_text() const {
    return gumbo_node_ && (gumbo_node_->type == GUMBO_NODE_TEXT || 
                          gumbo_node_->type == GUMBO_NODE_WHITESPACE);
}

bool Node::is_document() const {
    return gumbo_node_ && gumbo_node_->type == GUMBO_NODE_DOCUMENT;
}

std::shared_ptr<Node> Node::get_parent() const {
    if (!gumbo_node_ || !gumbo_node_->parent) {
        return nullptr;
    }
    return create_node(gumbo_node_->parent);
}

std::vector<std::shared_ptr<Node>> Node::get_children() const {
    std::vector<std::shared_ptr<Node>> children;
    
    const GumboVector* gumbo_children = get_child_vector(gumbo_node_);
    if (!gumbo_children) {
        return children;
    }

    for (unsigned int i = 0; i < gumbo_children->length; ++i) {
        GumboNode* child = static_cast<GumboNode*>(gumbo_children->data[i]);
        children.push_back(create_node(child));
    }
    
    return children;
}

std::shared_ptr<Node> Node::get_next_sibling() const {
    if (!gumbo_node_ || !gumbo_node_->parent) {
        return nullptr;
    }

    const GumboVector* siblings = get_child_vector(gumbo_node_->parent);
    if (!siblings) {
        return nullptr;
    }
    for (unsigned int i = 0; i < siblings->length - 1; ++i) {
        if (siblings->data[i] == gumbo_node_) {
            return create_node(static_cast<GumboNode*>(siblings->data[i + 1]));
        }
    }
    
    return nullptr;
}

std::shared_ptr<Node> Node::get_previous_sibling() const {
    if (!gumbo_node_ || !gumbo_node_->parent) {
        return nullptr;
    }

    const GumboVector* siblings = get_child_vector(gumbo_node_->parent);
    if (!siblings) {
        return nullptr;
    }
    for (unsigned int i = 1; i < siblings->length; ++i) {
        if (siblings->data[i] == gumbo_node_) {
            return create_node(static_cast<GumboNode*>(siblings->data[i - 1]));
        }
    }
    
    return nullptr;
}

std::vector<std::shared_ptr<Node>> Node::find_all(const std::string& tag_name, 
                                                  const std::map<std::string, std::string>& attrs) const {
    if (!gumbo_node_ || !tree_) {
        return {};
    }
    
    // ZERO-CONTEXT OPTIMIZATION: Use batch operations for root-level searches
    if (gumbo_node_ == tree_->get_output()->root) {
        ZeroContextBatch* zero_context = tree_->get_zero_context_batch();
        if (zero_context) {
            // Use zero-context batch for ultimate performance
            std::vector<BatchQuery> queries;
            
            if (attrs.empty() && !tag_name.empty()) {
                queries.emplace_back(BatchOp::FIND_ALL_TAG, tag_name);
            } else if (tag_name.empty() && attrs.size() == 1) {
                auto attr_it = attrs.find("class");
                if (attr_it != attrs.end()) {
                    queries.emplace_back(BatchOp::FIND_ALL_CLASS, attr_it->second);
                }
            }
            
            if (!queries.empty()) {
                auto results = zero_context->execute_batch(queries);
                if (!results.empty()) {
                    const auto& element_infos = std::get<std::vector<ElementInfo>>(results[0]);
                    
                    // Convert ElementInfo to Node objects only when needed
                    std::vector<std::shared_ptr<Node>> nodes;
                    nodes.reserve(element_infos.size());
                    
                    std::vector<size_t> indices;
                    indices.reserve(element_infos.size());
                    for (const auto& info : element_infos) {
                        indices.push_back(info.element_index);
                    }
                    
                    return zero_context->create_nodes_from_indices(indices);
                }
            }
        }
        
        // Fallback to bulk operations
        BulkOperations* bulk_ops = tree_->get_bulk_operations();
        if (bulk_ops && bulk_ops->indices_ready()) {
            // Handle simple tag-only searches with bulk operations
            if (attrs.empty() && !tag_name.empty()) {
                return bulk_ops->find_all_by_tag_bulk(tag_name);
            }
            
            // Handle class-only searches
            if (tag_name.empty() && attrs.size() == 1) {
                auto attr_it = attrs.find("class");
                if (attr_it != attrs.end()) {
                    return bulk_ops->find_all_by_class_bulk(attr_it->second);
                }
            }
        }
    }
    
    // For sub-tree searches or complex queries, use lightning walker
    LightningWalker walker;
    
    // Handle simple tag-only searches with specialized lightning methods
    if (attrs.empty() && !tag_name.empty()) {
        return walker.find_by_tag_lightning(gumbo_node_, tree_, tag_name);
    }
    
    // Handle class-only searches
    if (tag_name.empty() && attrs.size() == 1) {
        auto attr_it = attrs.find("class");
        if (attr_it != attrs.end()) {
            return walker.find_by_class_lightning(gumbo_node_, tree_, attr_it->second);
        }
    }
    
    // Fallback to ultra-fast implementation for complex searches
    return find_all_ultra_fast(tag_name, attrs);
}

std::shared_ptr<Node> Node::find(const std::string& tag_name, 
                                const std::map<std::string, std::string>& attrs) const {
    if (!gumbo_node_ || !tree_) {
        return nullptr;
    }
    
    // Use bulk operations for root-level searches (ultra-fast indexed lookup)
    if (gumbo_node_ == tree_->get_output()->root) {
        BulkOperations* bulk_ops = tree_->get_bulk_operations();
        if (bulk_ops && bulk_ops->indices_ready()) {
            // Handle ID searches with bulk operations
            if (tag_name.empty() && attrs.size() == 1) {
                auto attr_it = attrs.find("id");
                if (attr_it != attrs.end()) {
                    return bulk_ops->find_by_id_bulk(attr_it->second);
                }
            }
            
            // Handle simple tag-only searches
            if (attrs.empty() && !tag_name.empty()) {
                auto results = bulk_ops->find_all_by_tag_bulk(tag_name);
                return results.empty() ? nullptr : results[0];
            }
        }
    }
    
    // For sub-tree searches, use lightning walker
    LightningWalker walker;
    
    // Handle ID searches with specialized lightning method
    if (tag_name.empty() && attrs.size() == 1) {
        auto attr_it = attrs.find("id");
        if (attr_it != attrs.end()) {
            return walker.find_by_id_lightning(gumbo_node_, tree_, attr_it->second);
        }
    }
    
    // Handle simple tag-only searches
    if (attrs.empty() && !tag_name.empty()) {
        auto results = walker.find_by_tag_lightning(gumbo_node_, tree_, tag_name);
        return results.empty() ? nullptr : results[0];
    }
    
    // Fallback to ultra-fast implementation for complex searches
    return find_ultra_fast(tag_name, attrs);
}

void Node::search_recursive(GumboNode* node, const std::string& tag_name, 
                           const std::map<std::string, std::string>& attrs,
                           std::vector<std::shared_ptr<Node>>& results) const {
    if (!node) return;
    
    if (matches_criteria(node, tag_name, attrs)) {
        results.push_back(create_node(node));
    }
    
    if (const GumboVector* children = get_child_vector(node)) {
        for (unsigned int i = 0; i < children->length; ++i) {
            search_recursive(static_cast<GumboNode*>(children->data[i]), tag_name, attrs, results);
        }
    }
}

bool Node::matches_criteria(GumboNode* node, const std::string& tag_name,
                           const std::map<std::string, std::string>& attrs) const {
    if (!node || node->type != GUMBO_NODE_ELEMENT) {
        return false;
    }
    
    // Check tag name if specified
    if (!tag_name.empty()) {
        std::string node_name = gumbo_normalized_tagname(node->v.element.tag);
        if (node_name != tag_name) {
            return false;
        }
    }
    
    // Check attributes if specified
    for (const auto& attr_pair : attrs) {
        const std::string& attr_name = attr_pair.first;
        const std::string& attr_value = attr_pair.second;
        
        const GumboVector* attributes = &node->v.element.attributes;
        bool found = false;
        
        for (unsigned int i = 0; i < attributes->length; ++i) {
            GumboAttribute* attr = static_cast<GumboAttribute*>(attributes->data[i]);
            if (attr_name == attr->name && attr_value == attr->value) {
                found = true;
                break;
            }
        }
        
        if (!found) {
            return false;
        }
    }
    
    return true;
}

std::shared_ptr<Node> Node::create_node(GumboNode* gumbo_node) const {
    if (!gumbo_node) return nullptr;
    if (tree_) {
        return tree_->get_or_create_node(gumbo_node);
    }
    return std::make_shared<Node>(gumbo_node, tree_);
}

// Advanced navigation methods
std::shared_ptr<Node> Node::get_next_element() const {
    // CutySoup's next_element skips text nodes and returns next element tag
    // This is different from BeautifulSoup4 but matches CutySoup's intended behavior
    
    if (!gumbo_node_ || !gumbo_node_->parent) {
        return nullptr;
    }
    
    // Find our position in parent's children
    const GumboVector* parent_children = nullptr;
    if (gumbo_node_->parent->type == GUMBO_NODE_ELEMENT) {
        parent_children = &gumbo_node_->parent->v.element.children;
    } else if (gumbo_node_->parent->type == GUMBO_NODE_DOCUMENT) {
        parent_children = &gumbo_node_->parent->v.document.children;
    } else {
        return nullptr;
    }
    
    // Find current node's index
    unsigned int current_index = parent_children->length; // Invalid by default
    for (unsigned int i = 0; i < parent_children->length; ++i) {
        if (parent_children->data[i] == gumbo_node_) {
            current_index = i;
            break;
        }
    }
    
    if (current_index >= parent_children->length) {
        return nullptr; // Node not found in parent's children
    }
    
    // Look for next element sibling
    for (unsigned int i = current_index + 1; i < parent_children->length; ++i) {
        GumboNode* sibling = static_cast<GumboNode*>(parent_children->data[i]);
        if (sibling && sibling->type == GUMBO_NODE_ELEMENT) {
            return create_node(sibling);
        }
    }
    
    // If no sibling found, go up to parent and try its next element
    if (gumbo_node_->parent && gumbo_node_->parent->type == GUMBO_NODE_ELEMENT) {
        auto parent_node = create_node(gumbo_node_->parent);
        return parent_node->get_next_element();
    }
    
    return nullptr;
}

std::shared_ptr<Node> Node::get_previous_element() const {
    // BeautifulSoup4's previous_element traverses backwards in document order
    // 1. First try previous sibling
    auto sibling = get_previous_sibling();
    while (sibling && !sibling->is_element()) {
        sibling = sibling->get_previous_sibling();
    }
    if (sibling) {
        // Go to the deepest last child of this sibling
        auto last_descendant = sibling;
        while (true) {
            auto children = last_descendant->get_children();
            if (children.empty()) break;
            
            // Find the last element child
            std::shared_ptr<Node> last_element_child = nullptr;
            for (auto it = children.rbegin(); it != children.rend(); ++it) {
                if ((*it)->is_element()) {
                    last_element_child = *it;
                    break;
                }
            }
            if (!last_element_child) break;
            last_descendant = last_element_child;
        }
        return last_descendant;
    }
    
    // 2. Then try parent
    auto parent = get_parent();
    if (parent && parent->is_element()) {
        return parent;
    }
    
    return nullptr;
}

std::vector<std::shared_ptr<Node>> Node::get_descendants() const {
    std::vector<std::shared_ptr<Node>> descendants;
    collect_descendants_recursive(gumbo_node_, descendants);
    return descendants;
}

void Node::collect_descendants_recursive(GumboNode* node, std::vector<std::shared_ptr<Node>>& descendants) const {
    const GumboVector* children = get_child_vector(node);
    if (!children) return;

    for (unsigned int i = 0; i < children->length; ++i) {
        GumboNode* child = static_cast<GumboNode*>(children->data[i]);
        descendants.push_back(create_node(child));
        collect_descendants_recursive(child, descendants);
    }
}

std::vector<std::shared_ptr<Node>> Node::get_all_descendants_with_text() const {
    std::vector<std::shared_ptr<Node>> descendants;
    collect_descendants_with_text_recursive(gumbo_node_, descendants);
    return descendants;
}

void Node::collect_descendants_with_text_recursive(
    GumboNode* node,
    std::vector<std::shared_ptr<Node>>& descendants
) const {
    const GumboVector* children = get_child_vector(node);
    if (!children) {
        return;
    }

    for (unsigned int i = 0; i < children->length; ++i) {
        GumboNode* child = static_cast<GumboNode*>(children->data[i]);
        if (!child) {
            continue;
        }
        if (child->type == GUMBO_NODE_ELEMENT ||
            child->type == GUMBO_NODE_TEXT ||
            child->type == GUMBO_NODE_WHITESPACE) {
            descendants.push_back(create_node(child));
        }
        collect_descendants_with_text_recursive(child, descendants);
    }
}

std::vector<std::shared_ptr<Node>> Node::get_all_parents() const {
    std::vector<std::shared_ptr<Node>> parents;
    GumboNode* current = gumbo_node_ ? gumbo_node_->parent : nullptr;

    while (current) {
        if (current->type == GUMBO_NODE_ELEMENT) {
            parents.push_back(create_node(current));
        }
        current = current->parent;
    }

    return parents;
}

std::vector<std::shared_ptr<Node>> Node::get_all_next_siblings_including_text() const {
    std::vector<std::shared_ptr<Node>> siblings;
    if (!gumbo_node_ || !gumbo_node_->parent) {
        return siblings;
    }

    const GumboVector* parent_children = get_child_vector(gumbo_node_->parent);
    if (!parent_children) {
        return siblings;
    }

    bool found_self = false;
    for (unsigned int i = 0; i < parent_children->length; ++i) {
        GumboNode* child = static_cast<GumboNode*>(parent_children->data[i]);
        if (child == gumbo_node_) {
            found_self = true;
            continue;
        }
        if (!found_self || !child) {
            continue;
        }
        if (child->type == GUMBO_NODE_ELEMENT ||
            child->type == GUMBO_NODE_TEXT ||
            child->type == GUMBO_NODE_WHITESPACE) {
            siblings.push_back(create_node(child));
        }
    }

    return siblings;
}

std::vector<std::shared_ptr<Node>> Node::get_all_previous_siblings_including_text() const {
    std::vector<std::shared_ptr<Node>> siblings;
    if (!gumbo_node_ || !gumbo_node_->parent) {
        return siblings;
    }

    const GumboVector* parent_children = get_child_vector(gumbo_node_->parent);
    if (!parent_children) {
        return siblings;
    }

    for (unsigned int i = 0; i < parent_children->length; ++i) {
        GumboNode* child = static_cast<GumboNode*>(parent_children->data[i]);
        if (child == gumbo_node_) {
            break;
        }
        if (!child) {
            continue;
        }
        if (child->type == GUMBO_NODE_ELEMENT ||
            child->type == GUMBO_NODE_TEXT ||
            child->type == GUMBO_NODE_WHITESPACE) {
            siblings.push_back(create_node(child));
        }
    }

    return siblings;
}

std::vector<std::shared_ptr<Node>> Node::find_descendants_by_tag(const std::string& tag_name) const {
    std::vector<std::shared_ptr<Node>> results;
    collect_matching_descendants_recursive(gumbo_node_, tag_name, results);
    return results;
}

void Node::collect_matching_descendants_recursive(
    GumboNode* node,
    const std::string& tag_name,
    std::vector<std::shared_ptr<Node>>& results
) const {
    const GumboVector* children = get_child_vector(node);
    if (!children) {
        return;
    }

    for (unsigned int i = 0; i < children->length; ++i) {
        GumboNode* child = static_cast<GumboNode*>(children->data[i]);
        if (!child) {
            continue;
        }
        if (child->type == GUMBO_NODE_ELEMENT) {
            const char* node_tag = gumbo_normalized_tagname(child->v.element.tag);
            if (node_tag && tag_name == node_tag) {
                results.push_back(create_node(child));
            }
        }
        collect_matching_descendants_recursive(child, tag_name, results);
    }
}

// DOM manipulation methods (simplified implementations for compatibility)
void Node::decompose() {
    // Placeholder implementation to prevent crashes
    // Python layer will handle the actual logic
}

std::shared_ptr<Node> Node::extract() {
    // Return a copy of this node as placeholder
    return create_node(gumbo_node_);
}

void Node::clear() {
    // Placeholder implementation
}

void Node::replace_with(std::shared_ptr<Node> new_node) {
    // Placeholder implementation
}

void Node::append(std::shared_ptr<Node> child) {
    // Placeholder implementation
}

void Node::insert(int index, std::shared_ptr<Node> child) {
    // Placeholder implementation
}

std::shared_ptr<Node> Node::wrap(std::shared_ptr<Node> wrapper) {
    // Return the wrapper as placeholder
    return wrapper ? wrapper : create_node(gumbo_node_);
}

std::shared_ptr<Node> Node::unwrap() {
    // Return this node as placeholder
    return create_node(gumbo_node_);
}

// Attribute manipulation
bool Node::has_attribute(const std::string& name) const {
    return !get_attribute(name).empty();
}

void Node::set_attribute(const std::string& name, const std::string& value) {
    // Placeholder implementation - Python layer will handle this
}

void Node::remove_attribute(const std::string& name) {
    // Placeholder implementation - Python layer will handle this
}

// High-performance parallel search implementations
std::vector<std::shared_ptr<Node>> Node::find_all_parallel(const std::string& tag_name, 
                                                           const std::map<std::string, std::string>& attrs,
                                                           bool enable_parallel) const {
    if (!enable_parallel) {
        // Fall back to sequential search
        return find_all(tag_name, attrs);
    }
    
    // Create simple parallel walker
    SimpleParallelWalker walker;
    
    // Optimize for common search patterns
    if (!tag_name.empty() && attrs.empty()) {
        // Tag-only search - use optimized path
        return walker.find_by_tag_parallel(gumbo_node_, tree_, tag_name);
    } else if (tag_name.empty() && attrs.size() == 1) {
        // Single attribute search - check for common patterns
        auto attr_it = attrs.begin();
        if (attr_it->first == "class") {
            return walker.find_by_class_parallel(gumbo_node_, tree_, attr_it->second);
        } else if (attr_it->first == "id") {
            auto result = walker.find_by_id_parallel(gumbo_node_, tree_, attr_it->second);
            return result ? std::vector<std::shared_ptr<Node>>{result} : std::vector<std::shared_ptr<Node>>{};
        }
    }
    
    // General predicate-based search
    auto predicate = [tag_name, attrs](GumboNode* node) -> bool {
        if (!node || node->type != GUMBO_NODE_ELEMENT) {
            return false;
        }
        
        // Check tag name if specified
        if (!tag_name.empty()) {
            std::string node_name = gumbo_normalized_tagname(node->v.element.tag);
            if (node_name != tag_name) {
                return false;
            }
        }
        
        // Check attributes if specified
        for (const auto& attr_pair : attrs) {
            const std::string& attr_name = attr_pair.first;
            const std::string& attr_value = attr_pair.second;
            
            const GumboVector* attributes = &node->v.element.attributes;
            bool found = false;
            
            for (unsigned int i = 0; i < attributes->length; ++i) {
                GumboAttribute* attr = static_cast<GumboAttribute*>(attributes->data[i]);
                if (attr_name == attr->name && attr_value == attr->value) {
                    found = true;
                    break;
                }
            }
            
            if (!found) {
                return false;
            }
        }
        
        return true;
    };
    
    return walker.parallel_find_all(gumbo_node_, tree_, predicate);
}

std::shared_ptr<Node> Node::find_parallel(const std::string& tag_name, 
                                         const std::map<std::string, std::string>& attrs,
                                         bool enable_parallel) const {
    if (!enable_parallel) {
        // Fall back to sequential search
        return find(tag_name, attrs);
    }
    
    // Create simple parallel walker
    SimpleParallelWalker walker;
    
    // Optimize for ID search (most common single result search)
    if (tag_name.empty() && attrs.size() == 1) {
        auto attr_it = attrs.begin();
        if (attr_it->first == "id") {
            return walker.find_by_id_parallel(gumbo_node_, tree_, attr_it->second);
        }
    }
    
    // General predicate-based search with early termination
    auto predicate = [tag_name, attrs](GumboNode* node) -> bool {
        if (!node || node->type != GUMBO_NODE_ELEMENT) {
            return false;
        }
        
        // Check tag name if specified
        if (!tag_name.empty()) {
            std::string node_name = gumbo_normalized_tagname(node->v.element.tag);
            if (node_name != tag_name) {
                return false;
            }
        }
        
        // Check attributes if specified
        for (const auto& attr_pair : attrs) {
            const std::string& attr_name = attr_pair.first;
            const std::string& attr_value = attr_pair.second;
            
            const GumboVector* attributes = &node->v.element.attributes;
            bool found = false;
            
            for (unsigned int i = 0; i < attributes->length; ++i) {
                GumboAttribute* attr = static_cast<GumboAttribute*>(attributes->data[i]);
                if (attr_name == attr->name && attr_value == attr->value) {
                    found = true;
                    break;
                }
            }
            
            if (!found) {
                return false;
            }
        }
        
        return true;
    };
    
    return walker.parallel_find(gumbo_node_, tree_, predicate);
}

// Optimized parallel search implementations with reduced overhead
std::vector<std::shared_ptr<Node>> Node::find_all_parallel_optimized(const std::string& tag_name, 
                                                                     const std::map<std::string, std::string>& attrs,
                                                                     bool enable_parallel) const {
    if (!enable_parallel) {
        // Fall back to sequential search
        return find_all(tag_name, attrs);
    }
    
    // Create optimized parallel walker
    OptimizedParallelWalker walker;
    
    // Optimize for common search patterns
    if (!tag_name.empty() && attrs.empty()) {
        // Tag-only search - use optimized path
        return walker.find_by_tag_parallel(gumbo_node_, tree_, tag_name);
    }
    
    // General predicate-based search
    auto predicate = [tag_name, attrs](GumboNode* node) -> bool {
        if (!node || node->type != GUMBO_NODE_ELEMENT) {
            return false;
        }
        
        // Check tag name if specified
        if (!tag_name.empty()) {
            std::string node_name = gumbo_normalized_tagname(node->v.element.tag);
            if (node_name != tag_name) {
                return false;
            }
        }
        
        // Check attributes if specified
        for (const auto& attr_pair : attrs) {
            const std::string& attr_name = attr_pair.first;
            const std::string& attr_value = attr_pair.second;
            
            const GumboVector* attributes = &node->v.element.attributes;
            bool found = false;
            
            for (unsigned int i = 0; i < attributes->length; ++i) {
                GumboAttribute* attr = static_cast<GumboAttribute*>(attributes->data[i]);
                if (attr_name == attr->name && attr_value == attr->value) {
                    found = true;
                    break;
                }
            }
            
            if (!found) {
                return false;
            }
        }
        
        return true;
    };
    
    return walker.parallel_find_all(gumbo_node_, tree_, predicate);
}

std::shared_ptr<Node> Node::find_parallel_optimized(const std::string& tag_name, 
                                                   const std::map<std::string, std::string>& attrs,
                                                   bool enable_parallel) const {
    if (!enable_parallel) {
        // Fall back to sequential search
        return find(tag_name, attrs);
    }
    
    // Create optimized parallel walker
    OptimizedParallelWalker walker;
    
    // General predicate-based search with early termination
    auto predicate = [tag_name, attrs](GumboNode* node) -> bool {
        if (!node || node->type != GUMBO_NODE_ELEMENT) {
            return false;
        }
        
        // Check tag name if specified
        if (!tag_name.empty()) {
            std::string node_name = gumbo_normalized_tagname(node->v.element.tag);
            if (node_name != tag_name) {
                return false;
            }
        }
        
        // Check attributes if specified
        for (const auto& attr_pair : attrs) {
            const std::string& attr_name = attr_pair.first;
            const std::string& attr_value = attr_pair.second;
            
            const GumboVector* attributes = &node->v.element.attributes;
            bool found = false;
            
            for (unsigned int i = 0; i < attributes->length; ++i) {
                GumboAttribute* attr = static_cast<GumboAttribute*>(attributes->data[i]);
                if (attr_name == attr->name && attr_value == attr->value) {
                    found = true;
                    break;
                }
            }
            
            if (!found) {
                return false;
            }
        }
        
        return true;
    };
    
    return walker.parallel_find(gumbo_node_, tree_, predicate);
}

// Ultra-fast sequential search implementations with SIMD optimizations
std::vector<std::shared_ptr<Node>> Node::find_all_ultra_fast(const std::string& tag_name, 
                                                            const std::map<std::string, std::string>& attrs) const {
    if (!gumbo_node_ || !tree_) {
        return {};
    }
    
    // Create ultra-fast walker
    UltraFastWalker walker;
    
    // Handle simple tag-only searches with specialized ultra-fast methods
    if (attrs.empty() && !tag_name.empty()) {
        return walker.find_by_tag_ultra_fast(gumbo_node_, tree_, tag_name);
    }
    
    // Handle class-only searches
    if (tag_name.empty() && attrs.size() == 1) {
        auto attr_it = attrs.find("class");
        if (attr_it != attrs.end()) {
            return walker.find_by_class_ultra_fast(gumbo_node_, tree_, attr_it->second);
        }
    }
    
    // Handle complex searches with optimized generic predicate
    auto predicate = [&tag_name, &attrs](GumboNode* node) -> bool {
        if (!node || node->type != GUMBO_NODE_ELEMENT) {
            return false;
        }
        
        // Check tag name if specified
        if (!tag_name.empty()) {
            const char* node_name = gumbo_normalized_tagname(node->v.element.tag);
            if (!node_name || tag_name != node_name) {
                return false;
            }
        }
        
        // Check attributes if specified
        for (const auto& attr_pair : attrs) {
            const std::string& attr_name = attr_pair.first;
            const std::string& attr_value = attr_pair.second;
            
            // Look for the attribute
            const GumboVector* attributes = &node->v.element.attributes;
            bool found = false;
            
            for (unsigned int i = 0; i < attributes->length; ++i) {
                GumboAttribute* attr = static_cast<GumboAttribute*>(attributes->data[i]);
                if (attr && attr->name && attr_name == attr->name) {
                    if (attr_value.empty() || (attr->value && attr_value == attr->value)) {
                        found = true;
                        break;
                    }
                }
            }
            
            if (!found) {
                return false;
            }
        }
        
        return true;
    };
    
    return walker.find_all_optimized(gumbo_node_, tree_, predicate);
}

std::shared_ptr<Node> Node::find_ultra_fast(const std::string& tag_name, 
                                           const std::map<std::string, std::string>& attrs) const {
    if (!gumbo_node_ || !tree_) {
        return nullptr;
    }
    
    // Create ultra-fast walker
    UltraFastWalker walker;
    
    // Handle ID searches with specialized ultra-fast method
    if (tag_name.empty() && attrs.size() == 1) {
        auto attr_it = attrs.find("id");
        if (attr_it != attrs.end()) {
            return walker.find_by_id_ultra_fast(gumbo_node_, tree_, attr_it->second);
        }
    }
    
    // Handle simple tag-only searches
    if (attrs.empty() && !tag_name.empty()) {
        auto results = walker.find_by_tag_ultra_fast(gumbo_node_, tree_, tag_name);
        return results.empty() ? nullptr : results[0];
    }
    
    // Handle complex searches with optimized generic predicate
    auto predicate = [&tag_name, &attrs](GumboNode* node) -> bool {
        if (!node || node->type != GUMBO_NODE_ELEMENT) {
            return false;
        }
        
        // Check tag name if specified
        if (!tag_name.empty()) {
            const char* node_name = gumbo_normalized_tagname(node->v.element.tag);
            if (!node_name || tag_name != node_name) {
                return false;
            }
        }
        
        // Check attributes if specified
        for (const auto& attr_pair : attrs) {
            const std::string& attr_name = attr_pair.first;
            const std::string& attr_value = attr_pair.second;
            
            // Look for the attribute
            const GumboVector* attributes = &node->v.element.attributes;
            bool found = false;
            
            for (unsigned int i = 0; i < attributes->length; ++i) {
                GumboAttribute* attr = static_cast<GumboAttribute*>(attributes->data[i]);
                if (attr && attr->name && attr_name == attr->name) {
                    if (attr_value.empty() || (attr->value && attr_value == attr->value)) {
                        found = true;
                        break;
                    }
                }
            }
            
            if (!found) {
                return false;
            }
        }
        
        return true;
    };
    
    return walker.find_optimized(gumbo_node_, tree_, predicate);
}

// Bulk search methods (eliminate Python/C++ boundary overhead)
std::vector<std::shared_ptr<Node>> Node::find_all_bulk(const std::string& tag_name, 
                                                      const std::map<std::string, std::string>& attrs) const {
    if (!gumbo_node_ || !tree_) {
        return {};
    }
    
    // Only works for root-level searches to use the pre-built indices
    if (gumbo_node_ != tree_->get_output()->root) {
        // For non-root searches, fall back to ultra-fast implementation
        return find_all_ultra_fast(tag_name, attrs);
    }
    
    BulkOperations* bulk_ops = tree_->get_bulk_operations();
    if (!bulk_ops || !bulk_ops->indices_ready()) {
        // Indices not available, fall back to ultra-fast implementation
        return find_all_ultra_fast(tag_name, attrs);
    }
    
    // Use the bulk operations for maximum performance
    return bulk_ops->find_all_bulk(tag_name, attrs);
}

std::shared_ptr<Node> Node::find_bulk(const std::string& tag_name, 
                                     const std::map<std::string, std::string>& attrs) const {
    auto results = find_all_bulk(tag_name, attrs);
    return results.empty() ? nullptr : results[0];
}

// Zero-context methods: Return primitive data without Node creation overhead
std::vector<std::string> Node::get_all_text_by_tag(const std::string& tag_name) const {
    if (!gumbo_node_ || !tree_ || gumbo_node_ != tree_->get_output()->root) {
        return {};  // Only works for root-level searches
    }
    
    ZeroContextBatch* zero_context = tree_->get_zero_context_batch();
    if (!zero_context) return {};
    
    return zero_context->get_all_text_by_tag(tag_name);
}

std::vector<std::string> Node::get_all_text_by_class(const std::string& class_name) const {
    if (!gumbo_node_ || !tree_ || gumbo_node_ != tree_->get_output()->root) {
        return {};  // Only works for root-level searches
    }
    
    ZeroContextBatch* zero_context = tree_->get_zero_context_batch();
    if (!zero_context) return {};
    
    return zero_context->get_all_text_by_class(class_name);
}

std::vector<std::string> Node::get_all_hrefs() const {
    if (!gumbo_node_ || !tree_ || gumbo_node_ != tree_->get_output()->root) {
        return {};
    }
    
    ZeroContextBatch* zero_context = tree_->get_zero_context_batch();
    if (!zero_context) return {};
    
    return zero_context->get_all_attributes("a", "href");
}

std::vector<std::string> Node::get_all_src_attributes() const {
    if (!gumbo_node_ || !tree_ || gumbo_node_ != tree_->get_output()->root) {
        return {};
    }
    
    ZeroContextBatch* zero_context = tree_->get_zero_context_batch();
    if (!zero_context) return {};
    
    return zero_context->get_all_attributes("img", "src");
}

size_t Node::count_elements_by_tag(const std::string& tag_name) const {
    if (!gumbo_node_ || !tree_ || gumbo_node_ != tree_->get_output()->root) {
        return 0;
    }
    
    ZeroContextBatch* zero_context = tree_->get_zero_context_batch();
    if (!zero_context) return 0;
    
    std::vector<BatchQuery> queries;
    queries.emplace_back(BatchOp::COUNT_TAG, tag_name);
    
    auto results = zero_context->execute_batch(queries);
    if (!results.empty()) {
        return std::get<size_t>(results[0]);
    }
    
    return 0;
}

size_t Node::count_elements_by_class(const std::string& class_name) const {
    if (!gumbo_node_ || !tree_ || gumbo_node_ != tree_->get_output()->root) {
        return 0;
    }
    
    ZeroContextBatch* zero_context = tree_->get_zero_context_batch();
    if (!zero_context) return 0;
    
    std::vector<BatchQuery> queries;
    queries.emplace_back(BatchOp::COUNT_CLASS, class_name);
    
    auto results = zero_context->execute_batch(queries);
    if (!results.empty()) {
        return std::get<size_t>(results[0]);
    }
    
    return 0;
}

} // namespace cutysoup
