#include "fast_tree.h"
#include <algorithm>
#include <sstream>
#include <cctype>
#include <chrono>
#include <cstring>
#include <regex>
#include <memory>
#include <unordered_set>

namespace cutysoup {

//==============================================================================
// FastTree Implementation
//==============================================================================

FastTree::FastTree(const std::string& html) : output_(nullptr), node_count_(0) {
    // Parse HTML using Gumbo
    output_ = gumbo_parse(html.c_str());
    if (output_ && output_->root) {
        // Count nodes for statistics - don't create root wrapper yet
        count_nodes_recursive(output_->root);
    }
}

FastTree::~FastTree() {
    if (output_) {
        gumbo_destroy_output(&kGumboDefaultOptions, output_);
    }
}

std::shared_ptr<FastNode> FastTree::get_root() const {
    if (!root_node_ && output_ && output_->root) {
        // Lazy creation of root node wrapper
        root_node_ = create_node_wrapper(output_->root);
    }
    return root_node_;
}

std::shared_ptr<FastNode> FastTree::create_element(const std::string& tag_name, 
                                                   const std::map<std::string, std::string>& attributes) {
    // For now, create a simple placeholder node
    // In a full implementation, this would create a proper GumboNode
    // but for performance testing, we'll keep it simple
    return nullptr; // TODO: Implement proper node creation
}

std::vector<std::shared_ptr<FastNode>> FastTree::find_all_by_tag(const std::string& tag_name) const {
    if (!indexes_built_) {
        build_indexes();
    }
    
    std::vector<std::shared_ptr<FastNode>> results;
    auto it = tag_index_.find(tag_name);
    if (it != tag_index_.end()) {
        results.reserve(it->second.size());
        for (GumboNode* node : it->second) {
            results.push_back(create_node_wrapper(node));
        }
    }
    return results;
}

std::vector<std::shared_ptr<FastNode>> FastTree::find_all_by_class(const std::string& class_name) const {
    if (!indexes_built_) {
        build_indexes();
    }
    
    std::vector<std::shared_ptr<FastNode>> results;
    auto it = class_index_.find(class_name);
    if (it != class_index_.end()) {
        results.reserve(it->second.size());
        for (GumboNode* node : it->second) {
            results.push_back(create_node_wrapper(node));
        }
    }
    return results;
}

std::vector<std::shared_ptr<FastNode>> FastTree::find_all_by_id(const std::string& id) const {
    if (!indexes_built_) {
        build_indexes();
    }
    
    std::vector<std::shared_ptr<FastNode>> results;
    auto it = id_index_.find(id);
    if (it != id_index_.end()) {
        results.push_back(create_node_wrapper(it->second));
    }
    return results;
}

std::vector<std::shared_ptr<FastNode>> FastTree::select(const std::string& css_selector) const {
    // Simple CSS selector implementation - focus on common patterns first
    std::vector<std::shared_ptr<FastNode>> results;
    
    if (css_selector.empty()) {
        return results;
    }
    
    // Handle simple cases first (these cover 80% of real usage)
    if (css_selector[0] == '#') {
        // ID selector
        return find_all_by_id(css_selector.substr(1));
    } else if (css_selector[0] == '.') {
        // Class selector
        return find_all_by_class(css_selector.substr(1));
    } else if (css_selector.find_first_of(".#[ ") == std::string::npos) {
        // Simple tag selector
        return find_all_by_tag(css_selector);
    }
    
    // For complex selectors, fall back to slower but comprehensive method
    return select_complex(css_selector);
}

std::shared_ptr<FastNode> FastTree::select_one(const std::string& css_selector) const {
    auto results = select(css_selector);
    return results.empty() ? nullptr : results[0];
}

std::vector<std::string> FastTree::extract_all_text_by_tag(const std::string& tag_name) const {
    auto nodes = find_all_by_tag(tag_name);
    std::vector<std::string> texts;
    texts.reserve(nodes.size());
    
    for (const auto& node : nodes) {
        std::string text = node->get_text();
        if (!text.empty()) {
            texts.push_back(std::move(text));
        }
    }
    
    return texts;
}

std::vector<std::string> FastTree::extract_all_hrefs() const {
    auto links = find_all_by_tag("a");
    std::vector<std::string> hrefs;
    hrefs.reserve(links.size());
    
    for (const auto& link : links) {
        std::string href = link->get_attribute("href");
        if (!href.empty()) {
            hrefs.push_back(std::move(href));
        }
    }
    
    return hrefs;
}

std::vector<std::string> FastTree::extract_all_src() const {
    auto images = find_all_by_tag("img");
    std::vector<std::string> srcs;
    srcs.reserve(images.size());
    
    for (const auto& img : images) {
        std::string src = img->get_attribute("src");
        if (!src.empty()) {
            srcs.push_back(std::move(src));
        }
    }
    
    return srcs;
}

void FastTree::build_indexes() const {
    if (indexes_built_ || !output_ || !output_->root) {
        return;
    }
    
    auto start_time = std::chrono::high_resolution_clock::now();
    
    tag_index_.clear();
    class_index_.clear();
    id_index_.clear();
    attribute_index_.clear();
    text_index_.clear();
    all_elements_.clear();
    indexed_node_count_ = 0;
    
    build_indexes_recursive(output_->root);
    indexes_built_ = true;
    
    auto end_time = std::chrono::high_resolution_clock::now();
    index_build_time_ = std::chrono::duration<double>(end_time - start_time).count();
}

void FastTree::build_indexes_recursive(GumboNode* node) const {
    if (!node) return;
    
    if (node->type == GUMBO_NODE_ELEMENT) {
        indexed_node_count_++;
        
        // Index by tag name
        const char* tag_name = gumbo_normalized_tagname(node->v.element.tag);
        tag_index_[tag_name].push_back(node);
        
        // Add to all elements list for nth-child operations
        all_elements_.push_back(node);
        
        // Index all attributes as attr=value pairs
        GumboVector* attributes = &node->v.element.attributes;
        for (unsigned int i = 0; i < attributes->length; ++i) {
            GumboAttribute* attr = static_cast<GumboAttribute*>(attributes->data[i]);
            std::string attr_key = std::string(attr->name) + "=" + std::string(attr->value);
            attribute_index_[attr_key].push_back(node);
            
            // Special handling for class and id
            if (strcmp(attr->name, "class") == 0) {
                std::string class_value = attr->value;
                std::istringstream iss(class_value);
                std::string class_name;
                while (iss >> class_name) {
                    class_index_[class_name].push_back(node);
                }
            } else if (strcmp(attr->name, "id") == 0) {
                id_index_[attr->value] = node;
            }
        }
        
        // Index text content (only immediate text children)
        GumboVector* children = &node->v.element.children;
        for (unsigned int i = 0; i < children->length; ++i) {
            GumboNode* child = static_cast<GumboNode*>(children->data[i]);
            if (child->type == GUMBO_NODE_TEXT && child->v.text.text) {
                std::string text_content = child->v.text.text;
                if (!text_content.empty()) {
                    // Trim whitespace for better indexing
                    text_content.erase(0, text_content.find_first_not_of(" \t\n\r"));
                    text_content.erase(text_content.find_last_not_of(" \t\n\r") + 1);
                    if (!text_content.empty()) {
                        text_index_[text_content].push_back(node);
                    }
                }
            }
        }
        
        // Recurse to children for further indexing
        for (unsigned int i = 0; i < children->length; ++i) {
            build_indexes_recursive(static_cast<GumboNode*>(children->data[i]));
        }
    }
}

std::shared_ptr<FastNode> FastTree::create_node_wrapper(GumboNode* node) const {
    if (!node) return nullptr;
    return std::make_shared<FastNode>(node, std::weak_ptr<FastTree>(
        std::const_pointer_cast<FastTree>(shared_from_this())));
}

void FastTree::count_nodes_recursive(GumboNode* node) {
    if (!node) return;
    
    node_count_++;
    
    if (node->type == GUMBO_NODE_ELEMENT) {
        GumboVector* children = &node->v.element.children;
        for (unsigned int i = 0; i < children->length; ++i) {
            count_nodes_recursive(static_cast<GumboNode*>(children->data[i]));
        }
    }
}

std::vector<std::shared_ptr<FastNode>> FastTree::select_complex(const std::string& css_selector) const {
    return select_by_complex_selector(css_selector);
}

//==============================================================================
// MaxCpp Phase 35: C++ Bulk Find Operations
//==============================================================================

std::vector<std::shared_ptr<FastNode>> FastTree::bulk_find_all(const std::string& name,
                                                              const std::map<std::string, std::string>& attrs,
                                                              bool recursive) const {
    std::vector<std::shared_ptr<FastNode>> results;
    
    if (!indexes_built_) {
        build_indexes();
    }
    
    // Start with all elements if doing recursive search, or just root children if not
    std::vector<GumboNode*> candidates;
    if (recursive) {
        candidates = all_elements_;
    } else {
        // Non-recursive: just direct children of root
        GumboNode* root = output_->root;
        if (root && root->type == GUMBO_NODE_ELEMENT) {
            GumboVector* children = &root->v.element.children;
            for (unsigned int i = 0; i < children->length; ++i) {
                GumboNode* child = static_cast<GumboNode*>(children->data[i]);
                if (child->type == GUMBO_NODE_ELEMENT) {
                    candidates.push_back(child);
                }
            }
        }
    }
    
    // Filter candidates based on criteria
    for (GumboNode* node : candidates) {
        if (!node || node->type != GUMBO_NODE_ELEMENT) continue;
        
        bool matches = true;
        
        // Check tag name if specified
        if (!name.empty() && name != "*") {
            const char* tag_name = gumbo_normalized_tagname(node->v.element.tag);
            if (!tag_name || name != tag_name) {
                matches = false;
            }
        }
        
        // Check attributes if specified
        if (matches && !attrs.empty()) {
            for (const auto& attr_pair : attrs) {
                const std::string& attr_name = attr_pair.first;
                const std::string& attr_value = attr_pair.second;
                
                // Find attribute in node
                bool found_attr = false;
                GumboVector* attributes = &node->v.element.attributes;
                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) {
                        // Check value if specified
                        if (attr_value.empty() || (attr->value && attr_value == attr->value)) {
                            found_attr = true;
                            break;
                        }
                    }
                }
                
                if (!found_attr) {
                    matches = false;
                    break;
                }
            }
        }
        
        if (matches) {
            results.push_back(create_node_wrapper(node));
        }
    }
    
    return results;
}

std::shared_ptr<FastNode> FastTree::bulk_find(const std::string& name,
                                             const std::map<std::string, std::string>& attrs,
                                             bool recursive) const {
    auto results = bulk_find_all(name, attrs, recursive);
    return results.empty() ? nullptr : results[0];
}

std::vector<std::shared_ptr<FastNode>> FastTree::bulk_find_all_strings() const {
    std::vector<std::shared_ptr<FastNode>> results;
    
    if (!indexes_built_) {
        build_indexes();
    }
    
    // Traverse all nodes to find text nodes
    std::function<void(GumboNode*)> collect_text_nodes = [&](GumboNode* node) {
        if (!node) return;
        
        if (node->type == GUMBO_NODE_TEXT || node->type == GUMBO_NODE_WHITESPACE) {
            // Create a wrapper for text nodes - need to handle this carefully
            // For now, skip text nodes as they need special handling in our wrapper
            // This method is mainly for element nodes with text content
        } else if (node->type == GUMBO_NODE_ELEMENT) {
            // Check if this element has direct text content
            GumboVector* children = &node->v.element.children;
            for (unsigned int i = 0; i < children->length; ++i) {
                collect_text_nodes(static_cast<GumboNode*>(children->data[i]));
            }
        }
    };
    
    collect_text_nodes(output_->root);
    return results;
}

std::vector<std::shared_ptr<FastNode>> FastTree::bulk_find_parents(const std::shared_ptr<FastNode>& node,
                                                                  const std::string& name,
                                                                  const std::map<std::string, std::string>& attrs) const {
    std::vector<std::shared_ptr<FastNode>> results;
    if (!node) return results;
    
    // Traverse up the parent chain
    GumboNode* current = node->get_gumbo_node()->parent;
    while (current && current->type == GUMBO_NODE_ELEMENT) {
        bool matches = true;
        
        // Check tag name if specified
        if (!name.empty() && name != "*") {
            const char* tag_name = gumbo_normalized_tagname(current->v.element.tag);
            if (!tag_name || name != tag_name) {
                matches = false;
            }
        }
        
        // Check attributes if specified
        if (matches && !attrs.empty()) {
            for (const auto& attr_pair : attrs) {
                const std::string& attr_name = attr_pair.first;
                const std::string& attr_value = attr_pair.second;
                
                // Find attribute in node
                bool found_attr = false;
                GumboVector* attributes = &current->v.element.attributes;
                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) {
                        // Check value if specified
                        if (attr_value.empty() || (attr->value && attr_value == attr->value)) {
                            found_attr = true;
                            break;
                        }
                    }
                }
                
                if (!found_attr) {
                    matches = false;
                    break;
                }
            }
        }
        
        if (matches) {
            results.push_back(create_node_wrapper(current));
        }
        
        current = current->parent;
    }
    
    return results;
}

//==============================================================================
// Advanced Indexing Methods
//==============================================================================

std::vector<std::shared_ptr<FastNode>> FastTree::find_by_attribute(const std::string& attr_name, const std::string& value) const {
    if (!indexes_built_) {
        build_indexes();
    }
    
    std::vector<std::shared_ptr<FastNode>> results;
    std::string attr_key = attr_name + "=" + value;
    
    auto it = attribute_index_.find(attr_key);
    if (it != attribute_index_.end()) {
        results.reserve(it->second.size());
        for (GumboNode* node : it->second) {
            results.push_back(create_node_wrapper(node));
        }
    }
    
    return results;
}

std::vector<std::shared_ptr<FastNode>> FastTree::find_by_text_content(const std::string& text) const {
    if (!indexes_built_) {
        build_indexes();
    }
    
    std::vector<std::shared_ptr<FastNode>> results;
    auto it = text_index_.find(text);
    if (it != text_index_.end()) {
        results.reserve(it->second.size());
        for (GumboNode* node : it->second) {
            results.push_back(create_node_wrapper(node));
        }
    }
    
    return results;
}

std::shared_ptr<FastNode> FastTree::get_nth_element(size_t index) const {
    if (!indexes_built_) {
        build_indexes();
    }
    
    if (index < all_elements_.size()) {
        return create_node_wrapper(all_elements_[index]);
    }
    
    return nullptr;
}

//==============================================================================
// FastNode Implementation  
//==============================================================================

FastNode::FastNode(GumboNode* node, std::weak_ptr<FastTree> tree) 
    : node_(node), tree_(tree) {
}

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

std::string FastNode::get_text() const {
    if (!node_) return "";
    
    if (node_->type == GUMBO_NODE_TEXT) {
        return node_->v.text.text;
    } else if (node_->type == GUMBO_NODE_ELEMENT) {
        // Extract all text from descendants
        std::string result;
        extract_text_recursive(node_, result);
        return result;
    }
    
    return "";
}

void FastNode::extract_text_recursive(GumboNode* node, std::string& result) const {
    if (!node) return;
    
    if (node->type == GUMBO_NODE_TEXT || node->type == GUMBO_NODE_WHITESPACE) {
        // Include all text content, including whitespace - matches BeautifulSoup4 behavior
        result += node->v.text.text;
    } else if (node->type == GUMBO_NODE_ELEMENT) {
        GumboVector* children = &node->v.element.children;
        for (unsigned int i = 0; i < children->length; ++i) {
            extract_text_recursive(static_cast<GumboNode*>(children->data[i]), result);
        }
    }
}

//==============================================================================
// Advanced C++ Text Processing Methods (MaxCpp Phase 32)
//==============================================================================

std::string FastNode::get_text_with_separator(const std::string& separator) const {
    if (!node_) return "";
    
    std::vector<std::string> text_parts;
    collect_text_parts(node_, text_parts);
    
    // Join with separator
    if (text_parts.empty()) return "";
    if (text_parts.size() == 1) return text_parts[0];
    
    std::string result;
    for (size_t i = 0; i < text_parts.size(); ++i) {
        if (i > 0) result += separator;
        result += text_parts[i];
    }
    
    return result;
}

std::string FastNode::get_text_stripped() const {
    std::string text = get_text();
    
    // Trim leading whitespace
    size_t start = text.find_first_not_of(" \t\n\r\f\v");
    if (start == std::string::npos) return "";
    
    // Trim trailing whitespace
    size_t end = text.find_last_not_of(" \t\n\r\f\v");
    
    return text.substr(start, (end - start + 1));
}

std::string FastNode::get_text_with_separator_stripped(const std::string& separator) const {
    std::string text = get_text_with_separator(separator);
    
    // Trim leading whitespace
    size_t start = text.find_first_not_of(" \t\n\r\f\v");
    if (start == std::string::npos) return "";
    
    // Trim trailing whitespace
    size_t end = text.find_last_not_of(" \t\n\r\f\v");
    
    return text.substr(start, (end - start + 1));
}

std::vector<std::string> FastNode::get_strings() const {
    if (!node_) return {};
    
    std::vector<std::string> strings;
    collect_strings_recursive(node_, strings);
    return strings;
}

std::vector<std::string> FastNode::get_stripped_strings() const {
    std::vector<std::string> strings = get_strings();
    std::vector<std::string> stripped_strings;
    
    for (const auto& str : strings) {
        // Trim whitespace
        size_t start = str.find_first_not_of(" \t\n\r\f\v");
        if (start != std::string::npos) {
            size_t end = str.find_last_not_of(" \t\n\r\f\v");
            std::string stripped = str.substr(start, (end - start + 1));
            if (!stripped.empty()) {
                stripped_strings.push_back(stripped);
            }
        }
    }
    
    return stripped_strings;
}

std::string FastNode::get_direct_text() const {
    if (!node_ || node_->type != GUMBO_NODE_ELEMENT) {
        return "";
    }
    
    std::string direct_text;
    GumboVector* children = &node_->v.element.children;
    
    // Only collect direct text children, not from nested elements
    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->v.text.text) {
            direct_text += child->v.text.text;
        }
    }
    
    return direct_text;
}

void FastNode::collect_text_parts(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) {
        std::string text = node->v.text.text;
        // Include all text parts including whitespace - matches BeautifulSoup4 behavior
        parts.push_back(text);
    } else if (node->type == GUMBO_NODE_ELEMENT) {
        GumboVector* children = &node->v.element.children;
        for (unsigned int i = 0; i < children->length; ++i) {
            collect_text_parts(static_cast<GumboNode*>(children->data[i]), parts);
        }
    }
}

void FastNode::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 (node->type == GUMBO_NODE_ELEMENT) {
        GumboVector* children = &node->v.element.children;
        for (unsigned int i = 0; i < children->length; ++i) {
            collect_strings_recursive(static_cast<GumboNode*>(children->data[i]), strings);
        }
    }
}

//==============================================================================
// MaxCpp Phase 33: C++ Tree Navigation Bulk Operations
//==============================================================================

std::vector<std::shared_ptr<FastNode>> FastNode::get_all_descendants() const {
    if (!node_) return {};
    
    auto tree = tree_.lock();
    if (!tree) return {};
    
    std::vector<std::shared_ptr<FastNode>> descendants;
    collect_all_descendants(node_, descendants);
    return descendants;
}

std::vector<std::shared_ptr<FastNode>> FastNode::get_all_descendants_with_text() const {
    if (!node_) return {};
    
    auto tree = tree_.lock();
    if (!tree) return {};
    
    std::vector<std::shared_ptr<FastNode>> descendants;
    collect_all_descendants_with_text(node_, descendants);
    return descendants;
}

std::vector<std::shared_ptr<FastNode>> FastNode::get_all_parents() const {
    if (!node_ || !node_->parent) return {};
    
    auto tree = tree_.lock();
    if (!tree) return {};
    
    std::vector<std::shared_ptr<FastNode>> parents;
    GumboNode* current = node_->parent;
    
    while (current && current->type == GUMBO_NODE_ELEMENT) {
        parents.push_back(tree->create_node_wrapper(current));
        current = current->parent;
    }
    
    return parents;
}

std::vector<std::shared_ptr<FastNode>> FastNode::get_all_next_siblings() const {
    if (!node_ || !node_->parent) return {};
    
    auto tree = tree_.lock();
    if (!tree) return {};
    
    std::vector<std::shared_ptr<FastNode>> siblings;
    
    if (node_->parent->type == GUMBO_NODE_ELEMENT) {
        GumboVector* parent_children = &node_->parent->v.element.children;
        bool found_self = false;
        
        for (unsigned int i = 0; i < parent_children->length; ++i) {
            GumboNode* child = static_cast<GumboNode*>(parent_children->data[i]);
            
            if (found_self && child && child->type == GUMBO_NODE_ELEMENT) {
                siblings.push_back(tree->create_node_wrapper(child));
            } else if (child == node_) {
                found_self = true;
            }
        }
    }
    
    return siblings;
}

std::vector<std::shared_ptr<FastNode>> FastNode::get_all_previous_siblings() const {
    if (!node_ || !node_->parent) return {};
    
    auto tree = tree_.lock();
    if (!tree) return {};
    
    std::vector<std::shared_ptr<FastNode>> siblings;
    
    if (node_->parent->type == GUMBO_NODE_ELEMENT) {
        GumboVector* parent_children = &node_->parent->v.element.children;
        
        for (unsigned int i = 0; i < parent_children->length; ++i) {
            GumboNode* child = static_cast<GumboNode*>(parent_children->data[i]);
            
            if (child == node_) {
                break; // Found self, stop collecting previous siblings
            } else if (child && child->type == GUMBO_NODE_ELEMENT) {
                siblings.push_back(tree->create_node_wrapper(child));
            }
        }
    }
    
    return siblings;
}

std::vector<std::shared_ptr<FastNode>> FastNode::get_all_next_siblings_including_text() const {
    if (!node_ || !node_->parent) return {};
    
    auto tree = tree_.lock();
    if (!tree) return {};
    
    std::vector<std::shared_ptr<FastNode>> siblings;
    
    if (node_->parent->type == GUMBO_NODE_ELEMENT) {
        GumboVector* parent_children = &node_->parent->v.element.children;
        bool found_self = false;
        
        for (unsigned int i = 0; i < parent_children->length; ++i) {
            GumboNode* child = static_cast<GumboNode*>(parent_children->data[i]);
            
            if (found_self && child) {
                if (child->type == GUMBO_NODE_ELEMENT || 
                    (child->type == GUMBO_NODE_TEXT && child->v.text.text)) {
                    siblings.push_back(tree->create_node_wrapper(child));
                }
            } else if (child == node_) {
                found_self = true;
            }
        }
    }
    
    return siblings;
}

std::vector<std::shared_ptr<FastNode>> FastNode::get_all_previous_siblings_including_text() const {
    if (!node_ || !node_->parent) return {};
    
    auto tree = tree_.lock();
    if (!tree) return {};
    
    std::vector<std::shared_ptr<FastNode>> siblings;
    
    if (node_->parent->type == GUMBO_NODE_ELEMENT) {
        GumboVector* parent_children = &node_->parent->v.element.children;
        
        for (unsigned int i = 0; i < parent_children->length; ++i) {
            GumboNode* child = static_cast<GumboNode*>(parent_children->data[i]);
            
            if (child == node_) {
                break; // Found self, stop collecting previous siblings
            } else if (child) {
                if (child->type == GUMBO_NODE_ELEMENT || 
                    (child->type == GUMBO_NODE_TEXT && child->v.text.text)) {
                    siblings.push_back(tree->create_node_wrapper(child));
                }
            }
        }
    }
    
    return siblings;
}

void FastNode::collect_all_descendants(GumboNode* node, std::vector<std::shared_ptr<FastNode>>& descendants) const {
    if (!node || node->type != GUMBO_NODE_ELEMENT) return;
    
    auto tree = tree_.lock();
    if (!tree) return;
    
    GumboVector* children = &node->v.element.children;
    for (unsigned int i = 0; i < children->length; ++i) {
        GumboNode* child = static_cast<GumboNode*>(children->data[i]);
        
        if (child && child->type == GUMBO_NODE_ELEMENT) {
            descendants.push_back(tree->create_node_wrapper(child));
            // Recursively collect descendants of this child
            collect_all_descendants(child, descendants);
        }
    }
}

void FastNode::collect_all_descendants_with_text(GumboNode* node, std::vector<std::shared_ptr<FastNode>>& descendants) const {
    if (!node) return;
    
    auto tree = tree_.lock();
    if (!tree) return;
    
    // For element nodes, collect all children including text nodes
    if (node->type == GUMBO_NODE_ELEMENT) {
        GumboVector* children = &node->v.element.children;
        for (unsigned int i = 0; i < children->length; ++i) {
            GumboNode* child = static_cast<GumboNode*>(children->data[i]);
            
            if (child) {
                if (child->type == GUMBO_NODE_TEXT || child->type == GUMBO_NODE_WHITESPACE) {
                    // Add text nodes
                    descendants.push_back(tree->create_node_wrapper(child));
                } else if (child->type == GUMBO_NODE_ELEMENT) {
                    // Add element node and recursively collect its descendants
                    descendants.push_back(tree->create_node_wrapper(child));
                    collect_all_descendants_with_text(child, descendants);
                }
            }
        }
    }
}

std::map<std::string, std::string> FastNode::get_attributes() const {
    std::map<std::string, std::string> attrs;
    
    if (!node_ || node_->type != GUMBO_NODE_ELEMENT) {
        return attrs;
    }
    
    GumboVector* attributes = &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 FastNode::get_attribute(const std::string& name) const {
    if (!node_ || node_->type != GUMBO_NODE_ELEMENT) {
        return "";
    }
    
    GumboAttribute* attr = gumbo_get_attribute(&node_->v.element.attributes, name.c_str());
    return attr ? attr->value : "";
}

bool FastNode::has_attribute(const std::string& name) const {
    if (!node_ || node_->type != GUMBO_NODE_ELEMENT) {
        return false;
    }
    
    return gumbo_get_attribute(&node_->v.element.attributes, name.c_str()) != nullptr;
}

std::shared_ptr<FastNode> FastNode::get_parent() const {
    if (!node_ || !node_->parent) {
        return nullptr;
    }
    
    auto tree = tree_.lock();
    if (!tree) return nullptr;
    
    return tree->create_node_wrapper(node_->parent);
}

std::vector<std::shared_ptr<FastNode>> FastNode::get_children() const {
    std::vector<std::shared_ptr<FastNode>> children;
    
    if (!node_ || node_->type != GUMBO_NODE_ELEMENT) {
        return children;
    }
    
    auto tree = tree_.lock();
    if (!tree) return children;
    
    GumboVector* child_nodes = &node_->v.element.children;
    children.reserve(child_nodes->length);
    
    for (unsigned int i = 0; i < child_nodes->length; ++i) {
        GumboNode* child = static_cast<GumboNode*>(child_nodes->data[i]);
        children.push_back(tree->create_node_wrapper(child));
    }
    
    return children;
}

std::shared_ptr<FastNode> FastNode::get_next_sibling() const {
    if (!node_ || !node_->parent) {
        return nullptr;
    }
    
    auto tree = tree_.lock();
    if (!tree) return nullptr;
    
    // Find this node in parent's children and return the next one
    if (node_->parent->type == GUMBO_NODE_ELEMENT) {
        GumboVector* siblings = &node_->parent->v.element.children;
        for (unsigned int i = 0; i < siblings->length - 1; ++i) {
            if (siblings->data[i] == node_) {
                return tree->create_node_wrapper(static_cast<GumboNode*>(siblings->data[i + 1]));
            }
        }
    }
    
    return nullptr;
}

std::shared_ptr<FastNode> FastNode::get_previous_sibling() const {
    if (!node_ || !node_->parent) {
        return nullptr;
    }
    
    auto tree = tree_.lock();
    if (!tree) return nullptr;
    
    // Find this node in parent's children and return the previous one
    if (node_->parent->type == GUMBO_NODE_ELEMENT) {
        GumboVector* siblings = &node_->parent->v.element.children;
        for (unsigned int i = 1; i < siblings->length; ++i) {
            if (siblings->data[i] == node_) {
                return tree->create_node_wrapper(static_cast<GumboNode*>(siblings->data[i - 1]));
            }
        }
    }
    
    return nullptr;
}

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

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

std::vector<std::shared_ptr<FastNode>> FastNode::find_descendants_by_tag(const std::string& tag_name) const {
    std::vector<std::shared_ptr<FastNode>> results;
    
    if (!node_) return results;
    
    auto tree = tree_.lock();
    if (!tree) return results;
    
    collect_descendants_by_tag(node_, tag_name, results);
    return results;
}

std::vector<std::shared_ptr<FastNode>> FastNode::select(const std::string& css_selector) const {
    // For node-level selects, search within this node's descendants
    auto tree = tree_.lock();
    if (!tree) return {};
    
    std::vector<std::shared_ptr<FastNode>> results;
    // TODO: Implement node-scoped CSS selection
    // For now, delegate to tree-level selection and filter results
    return results;
}

std::shared_ptr<FastNode> FastNode::select_one(const std::string& css_selector) const {
    auto results = select(css_selector);
    return results.empty() ? nullptr : results[0];
}

void FastNode::collect_descendants_by_tag(GumboNode* node, const std::string& tag_name, 
                                         std::vector<std::shared_ptr<FastNode>>& results) const {
    if (!node) return;
    
    auto tree = tree_.lock();
    if (!tree) return;
    
    if (node->type == GUMBO_NODE_ELEMENT) {
        // Check if this node matches
        const char* node_tag = gumbo_normalized_tagname(node->v.element.tag);
        if (tag_name == node_tag) {
            results.push_back(tree->create_node_wrapper(node));
        }
        
        // Recurse to children
        GumboVector* children = &node->v.element.children;
        for (unsigned int i = 0; i < children->length; ++i) {
            collect_descendants_by_tag(static_cast<GumboNode*>(children->data[i]), tag_name, results);
        }
    }
}

//==============================================================================
// MaxCpp Phase 35: FastNode Bulk Find Operations 
//==============================================================================

std::vector<std::shared_ptr<FastNode>> FastNode::node_find_all(const std::string& name,
                                                              const std::map<std::string, std::string>& attrs,
                                                              bool recursive) const {
    std::vector<std::shared_ptr<FastNode>> results;
    if (!node_) return results;
    
    auto tree = tree_.lock();
    if (!tree) return results;
    
    // Use a lambda to traverse the tree from this node
    std::function<void(GumboNode*)> traverse = [&](GumboNode* node) {
        if (!node || node->type != GUMBO_NODE_ELEMENT) return;
        
        bool matches = true;
        
        // Check tag name if specified
        if (!name.empty() && name != "*") {
            const char* tag_name = gumbo_normalized_tagname(node->v.element.tag);
            if (!tag_name || name != tag_name) {
                matches = false;
            }
        }
        
        // Check attributes if specified
        if (matches && !attrs.empty()) {
            for (const auto& attr_pair : attrs) {
                const std::string& attr_name = attr_pair.first;
                const std::string& attr_value = attr_pair.second;
                
                // Find attribute in node
                bool found_attr = false;
                GumboVector* attributes = &node->v.element.attributes;
                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) {
                        // Check value if specified
                        if (attr_value.empty() || (attr->value && attr_value == attr->value)) {
                            found_attr = true;
                            break;
                        }
                    }
                }
                
                if (!found_attr) {
                    matches = false;
                    break;
                }
            }
        }
        
        if (matches) {
            results.push_back(tree->create_node_wrapper(node));
        }
        
        // Recurse to children if recursive
        if (recursive) {
            GumboVector* children = &node->v.element.children;
            for (unsigned int i = 0; i < children->length; ++i) {
                traverse(static_cast<GumboNode*>(children->data[i]));
            }
        }
    };
    
    // Start traversal from this node's children (not this node itself)
    if (node_->type == GUMBO_NODE_ELEMENT) {
        GumboVector* children = &node_->v.element.children;
        for (unsigned int i = 0; i < children->length; ++i) {
            traverse(static_cast<GumboNode*>(children->data[i]));
        }
    }
    
    return results;
}

std::shared_ptr<FastNode> FastNode::node_find(const std::string& name,
                                             const std::map<std::string, std::string>& attrs,
                                             bool recursive) const {
    auto results = node_find_all(name, attrs, recursive);
    return results.empty() ? nullptr : results[0];
}

//==============================================================================
// MaxCpp Phase 34: Advanced CSS Selector Implementation
//==============================================================================

std::vector<std::shared_ptr<FastNode>> FastTree::select_by_complex_selector(const std::string& css_selector) const {
    std::vector<std::shared_ptr<FastNode>> results;
    
    // Handle multiple selectors separated by commas (OR logic)
    std::vector<std::string> selectors;
    std::stringstream ss(css_selector);
    std::string item;
    
    while (std::getline(ss, item, ',')) {
        // Trim whitespace
        item.erase(item.begin(), std::find_if(item.begin(), item.end(), [](int ch) {
            return !std::isspace(ch);
        }));
        item.erase(std::find_if(item.rbegin(), item.rend(), [](int ch) {
            return !std::isspace(ch);
        }).base(), item.end());
        
        if (!item.empty()) {
            selectors.push_back(item);
        }
    }
    
    // Process each selector and combine results
    std::unordered_set<FastNode*> unique_results;
    for (const std::string& selector : selectors) {
        auto parts = parse_css_selector(selector);
        
        // Find all descendant elements
        if (!indexes_built_) {
            build_indexes();
        }
        
        for (GumboNode* gumbo_node : all_elements_) {
            if (gumbo_node && gumbo_node->type == GUMBO_NODE_ELEMENT) {
                auto node = create_node_wrapper(gumbo_node);
                if (matches_complex_selector(node, parts)) {
                    if (unique_results.find(node.get()) == unique_results.end()) {
                        unique_results.insert(node.get());
                        results.push_back(node);
                    }
                }
            }
        }
    }
    
    return results;
}

bool FastTree::matches_css_selector(const std::shared_ptr<FastNode>& node, const std::string& selector) const {
    auto parts = parse_css_selector(selector);
    return matches_complex_selector(node, parts);
}

std::vector<FastTree::SelectorPart> FastTree::parse_css_selector(const std::string& selector) const {
    std::vector<SelectorPart> parts;
    
    // Protect attribute selectors by replacing them temporarily
    std::string temp_selector = selector;
    std::vector<std::string> protected_attrs;
    std::regex attr_regex(R"(\[[^\]]*\])");
    std::sregex_iterator attr_iter(selector.begin(), selector.end(), attr_regex);
    std::sregex_iterator attr_end;
    
    int attr_count = 0;
    for (; attr_iter != attr_end; ++attr_iter) {
        std::string attr = attr_iter->str();
        std::string placeholder = "__ATTR_" + std::to_string(attr_count) + "__";
        protected_attrs.push_back(attr);
        temp_selector = std::regex_replace(temp_selector, std::regex(std::regex_replace(attr, std::regex(R"([\[\]])"), R"(\\$&)")), placeholder);
        attr_count++;
    }
    
    // Split on combinators while preserving them
    std::regex combinator_regex(R"((\s*[>+~]\s*|\s+))");
    std::sregex_token_iterator token_iter(temp_selector.begin(), temp_selector.end(), combinator_regex, -1);
    std::sregex_token_iterator token_end;
    
    std::vector<std::string> tokens;
    for (; token_iter != token_end; ++token_iter) {
        std::string token = token_iter->str();
        if (!token.empty()) {
            tokens.push_back(token);
        }
    }
    
    // Restore attribute selectors
    for (auto& token : tokens) {
        for (size_t i = 0; i < protected_attrs.size(); ++i) {
            std::string placeholder = "__ATTR_" + std::to_string(i) + "__";
            if (token.find(placeholder) != std::string::npos) {
                token = std::regex_replace(token, std::regex(placeholder), protected_attrs[i]);
            }
        }
    }
    
    std::string current_combinator = "descendant";
    
    for (const std::string& token : tokens) {
        std::string trimmed_token = token;
        // Trim whitespace
        trimmed_token.erase(trimmed_token.begin(), std::find_if(trimmed_token.begin(), trimmed_token.end(), [](int ch) {
            return !std::isspace(ch);
        }));
        trimmed_token.erase(std::find_if(trimmed_token.rbegin(), trimmed_token.rend(), [](int ch) {
            return !std::isspace(ch);
        }).base(), trimmed_token.end());
        
        if (trimmed_token == ">") {
            current_combinator = "child";
        } else if (trimmed_token == "+") {
            current_combinator = "adjacent_sibling";
        } else if (trimmed_token == "~") {
            current_combinator = "general_sibling";
        } else if (!trimmed_token.empty()) {
            SelectorPart part = parse_simple_selector(trimmed_token);
            part.combinator = current_combinator;
            parts.push_back(part);
            current_combinator = "descendant"; // Reset to default
        }
    }
    
    return parts;
}

FastTree::SelectorPart FastTree::parse_simple_selector(const std::string& selector) const {
    SelectorPart result;
    std::string remaining = selector;
    
    // Extract attribute selectors first
    std::regex attr_regex(R"(\[([^\]]+)\])");
    std::sregex_iterator attr_iter(remaining.begin(), remaining.end(), attr_regex);
    std::sregex_iterator attr_end;
    
    for (; attr_iter != attr_end; ++attr_iter) {
        std::string attr_content = attr_iter->str(1);
        
        // Parse attribute content (name=value, name~=value, etc.)
        std::vector<std::string> operators = {"~=", "|=", "^=", "$=", "*=", "="};
        bool found_op = false;
        
        for (const std::string& op : operators) {
            size_t op_pos = attr_content.find(op);
            if (op_pos != std::string::npos) {
                std::string attr_name = attr_content.substr(0, op_pos);
                std::string attr_value = attr_content.substr(op_pos + op.length());
                
                // Trim whitespace from attribute name
                attr_name.erase(attr_name.begin(), std::find_if(attr_name.begin(), attr_name.end(), [](int ch) {
                    return !std::isspace(ch);
                }));
                attr_name.erase(std::find_if(attr_name.rbegin(), attr_name.rend(), [](int ch) {
                    return !std::isspace(ch);
                }).base(), attr_name.end());
                
                // Trim whitespace from attribute value
                attr_value.erase(attr_value.begin(), std::find_if(attr_value.begin(), attr_value.end(), [](int ch) {
                    return !std::isspace(ch);
                }));
                attr_value.erase(std::find_if(attr_value.rbegin(), attr_value.rend(), [](int ch) {
                    return !std::isspace(ch);
                }).base(), attr_value.end());
                
                // Remove quotes from value
                if (!attr_value.empty() && 
                    ((attr_value.front() == '"' && attr_value.back() == '"') ||
                     (attr_value.front() == '\'' && attr_value.back() == '\''))) {
                    attr_value = attr_value.substr(1, attr_value.length() - 2);
                }
                
                result.attrs[attr_name] = std::make_pair(op, attr_value);
                found_op = true;
                break;
            }
        }
        
        if (!found_op) {
            // Just attribute existence check - trim whitespace
            std::string trimmed_attr = attr_content;
            trimmed_attr.erase(trimmed_attr.begin(), std::find_if(trimmed_attr.begin(), trimmed_attr.end(), [](int ch) {
                return !std::isspace(ch);
            }));
            trimmed_attr.erase(std::find_if(trimmed_attr.rbegin(), trimmed_attr.rend(), [](int ch) {
                return !std::isspace(ch);
            }).base(), trimmed_attr.end());
            
            result.attrs[trimmed_attr] = std::make_pair("exists", "");
        }
        
        // Remove the attribute selector from remaining string
        remaining = std::regex_replace(remaining, std::regex(std::regex_replace(attr_iter->str(), std::regex(R"([\[\]])"), R"(\\$&)")), "");
    }
    
    // Extract pseudo-selectors
    std::regex pseudo_regex(R"(:([a-zA-Z0-9\-_]+(?:\([^)]*\))?))");
    std::smatch pseudo_match;
    if (std::regex_search(remaining, pseudo_match, pseudo_regex)) {
        result.pseudo = pseudo_match.str(1);
        remaining = std::regex_replace(remaining, pseudo_regex, "");
    }
    
    // Extract ID
    std::regex id_regex(R"(#([a-zA-Z0-9\-_]+))");
    std::smatch id_match;
    if (std::regex_search(remaining, id_match, id_regex)) {
        result.id = id_match.str(1);
        remaining = std::regex_replace(remaining, id_regex, "");
    }
    
    // Extract classes
    std::regex class_regex(R"(\.([a-zA-Z0-9\-_]+))");
    std::sregex_iterator class_iter(remaining.begin(), remaining.end(), class_regex);
    std::sregex_iterator class_end;
    
    for (; class_iter != class_end; ++class_iter) {
        result.classes.push_back(class_iter->str(1));
    }
    remaining = std::regex_replace(remaining, class_regex, "");
    
    // What's left should be the tag name
    remaining.erase(remaining.begin(), std::find_if(remaining.begin(), remaining.end(), [](int ch) {
        return !std::isspace(ch);
    }));
    remaining.erase(std::find_if(remaining.rbegin(), remaining.rend(), [](int ch) {
        return !std::isspace(ch);
    }).base(), remaining.end());
    
    if (!remaining.empty() && remaining != "*") {
        result.tag = remaining;
    }
    
    return result;
}

bool FastTree::matches_selector_part(const std::shared_ptr<FastNode>& node, const SelectorPart& part) const {
    // Check tag name first (most selective)
    if (!part.tag.empty() && node->get_name() != part.tag) {
        return false;
    }
    
    // Check ID (highly selective)
    if (!part.id.empty() && node->get_attribute("id") != part.id) {
        return false;
    }
    
    // Check classes
    if (!part.classes.empty()) {
        std::string class_attr = node->get_attribute("class");
        std::unordered_set<std::string> node_classes;
        
        // Split class attribute by spaces
        std::stringstream ss(class_attr);
        std::string class_name;
        while (ss >> class_name) {
            node_classes.insert(class_name);
        }
        
        for (const std::string& required_class : part.classes) {
            if (node_classes.find(required_class) == node_classes.end()) {
                return false;
            }
        }
    }
    
    // Check attributes
    for (const auto& attr_pair : part.attrs) {
        if (!matches_attribute(node, attr_pair.first, attr_pair.second.first, attr_pair.second.second)) {
            return false;
        }
    }
    
    // Check pseudo-selectors
    if (!part.pseudo.empty()) {
        if (!matches_pseudo(node, part.pseudo)) {
            return false;
        }
    }
    
    return true;
}

bool FastTree::matches_attribute(const std::shared_ptr<FastNode>& node, const std::string& attr_name, 
                                const std::string& op, const std::string& value) const {
    if (op == "exists") {
        return node->has_attribute(attr_name);
    }
    
    std::string attr_value = node->get_attribute(attr_name);
    if (attr_value.empty()) {
        return false;
    }
    
    if (op == "=") {
        return attr_value == value;
    } else if (op == "~=") {
        // Word match
        std::stringstream ss(attr_value);
        std::string word;
        while (ss >> word) {
            if (word == value) {
                return true;
            }
        }
        return false;
    } else if (op == "|=") {
        // Exact match or starts with value followed by hyphen
        return attr_value == value || (attr_value.length() > value.length() && 
                                      attr_value.substr(0, value.length()) == value &&
                                      attr_value[value.length()] == '-');
    } else if (op == "^=") {
        // Starts with
        return attr_value.length() >= value.length() && 
               attr_value.substr(0, value.length()) == value;
    } else if (op == "$=") {
        // Ends with
        return attr_value.length() >= value.length() && 
               attr_value.substr(attr_value.length() - value.length()) == value;
    } else if (op == "*=") {
        // Contains
        return attr_value.find(value) != std::string::npos;
    }
    
    return false;
}

bool FastTree::matches_pseudo(const std::shared_ptr<FastNode>& node, const std::string& pseudo) const {
    // For now, implement basic pseudo-selectors
    // This can be extended with more complex pseudo-selector logic
    
    if (pseudo == "first-child") {
        auto parent = node->get_parent();
        if (!parent) return false;
        auto children = parent->get_children();
        return !children.empty() && children[0].get() == node.get();
    } else if (pseudo == "last-child") {
        auto parent = node->get_parent();
        if (!parent) return false;
        auto children = parent->get_children();
        return !children.empty() && children.back().get() == node.get();
    } else if (pseudo == "empty") {
        auto children = node->get_children();
        std::string text = node->get_text();
        return children.empty() && text.empty();
    } else if (pseudo.find("contains(") == 0 && pseudo.back() == ')') {
        // Extract text from :contains(text)
        std::string contains_text = pseudo.substr(9, pseudo.length() - 10);
        // Remove quotes if present
        if ((contains_text.front() == '"' && contains_text.back() == '"') ||
            (contains_text.front() == '\'' && contains_text.back() == '\'')) {
            contains_text = contains_text.substr(1, contains_text.length() - 2);
        }
        return node->get_text().find(contains_text) != std::string::npos;
    }
    
    // Default: unknown pseudo-selectors return false
    return false;
}

bool FastTree::matches_complex_selector(const std::shared_ptr<FastNode>& node, const std::vector<SelectorPart>& parts) const {
    if (parts.empty()) {
        return true;
    }
    
    const SelectorPart& current_part = parts.back(); // Start from the rightmost part
    std::vector<SelectorPart> remaining_parts(parts.begin(), parts.end() - 1);
    
    // Check if current node matches the rightmost selector part
    if (!matches_selector_part(node, current_part)) {
        return false;
    }
    
    // If this is the only part, we're done
    if (remaining_parts.empty()) {
        return true;
    }
    
    // Handle combinator for remaining parts
    const std::string& combinator = current_part.combinator;
    
    if (combinator == "descendant") {
        // Look for any ancestor that matches remaining parts
        auto current = node->get_parent();
        while (current) {
            if (matches_complex_selector(current, remaining_parts)) {
                return true;
            }
            current = current->get_parent();
        }
    } else if (combinator == "child") {
        // Direct parent must match remaining parts
        auto parent = node->get_parent();
        if (parent) {
            return matches_complex_selector(parent, remaining_parts);
        }
    } else if (combinator == "adjacent_sibling") {
        // Find the immediately previous element sibling
        auto parent = node->get_parent();
        if (!parent) return false;
        
        auto siblings = parent->get_children();
        for (size_t i = 1; i < siblings.size(); ++i) {
            if (siblings[i].get() == node.get()) {
                return matches_complex_selector(siblings[i-1], remaining_parts);
            }
        }
    } else if (combinator == "general_sibling") {
        // Any previous sibling must match remaining parts
        auto parent = node->get_parent();
        if (!parent) return false;
        
        auto siblings = parent->get_children();
        bool found_current = false;
        for (auto it = siblings.rbegin(); it != siblings.rend(); ++it) {
            if (it->get() == node.get()) {
                found_current = true;
            } else if (found_current && matches_complex_selector(*it, remaining_parts)) {
                return true;
            }
        }
    }
    
    return false;
}

} // namespace cutysoup