#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/operators.h>
#include <cstdint>
#include "tree.h"
#include "node.h"

namespace py = pybind11;
using namespace cutysoup;

PYBIND11_MODULE(_cutysoup, m) {
    m.doc() = "CutySoup: High-performance HTML parsing library";
    
    // Tree class
    py::class_<Tree>(m, "Tree")
        .def(py::init<const std::string&>())
        .def("get_root", &Tree::get_root, py::return_value_policy::reference_internal,
             "Get the root node of the parsed HTML tree")
        .def("create_element", &Tree::create_element,
             py::arg("tag_name"), py::arg("attributes") = std::map<std::string, std::string>(),
             "Create a new element with the given tag name and attributes")
        
        // Advanced indexing methods for large documents
        .def("find_by_attribute", &Tree::find_by_attribute,
             py::arg("attr_name"), py::arg("value"),
             "Find elements by attribute name and value using advanced indexing")
        .def("find_by_text_content", &Tree::find_by_text_content,
             py::arg("text"),
             "Find elements by their immediate text content using advanced indexing")
        .def("get_nth_element", &Tree::get_nth_element,
             py::arg("index"),
             "Get the nth element from the indexed elements list")
        
        // Performance and indexing statistics
        .def("get_index_build_time", &Tree::get_index_build_time,
             "Get time taken to build indexes (in seconds)")
        .def("get_indexed_element_count", &Tree::get_indexed_element_count,
             "Get number of indexed elements");
    
    // Node class
    py::class_<Node, std::shared_ptr<Node>>(m, "Node")
        .def("get_name", &Node::get_name,
             "Get the tag name of this node")
        .def("get_attributes", &Node::get_attributes,
             "Get all attributes as a dictionary")
        .def("get_attribute", &Node::get_attribute,
             "Get a specific attribute value")
        .def("get_text", &Node::get_text,
             "Get all text content within this node")
        .def("get_text_with_separator", &Node::get_text_with_separator,
             py::arg("separator"),
             "Get all text content joined with a separator")
        .def("get_text_stripped", &Node::get_text_stripped,
             "Get all text content with leading and trailing whitespace trimmed")
        .def("get_text_with_separator_stripped", &Node::get_text_with_separator_stripped,
             py::arg("separator"),
             "Get all text content with a separator and trimmed whitespace")
        .def("get_strings", &Node::get_strings,
             "Get all descendant string nodes as Python strings")
        .def("get_stripped_strings", &Node::get_stripped_strings,
             "Get all descendant string nodes with whitespace trimmed")
        .def("get_direct_text", &Node::get_direct_text,
             "Get only direct text children of this node")
        .def("is_element", &Node::is_element,
             "Check if this node is an element")
        .def("is_text", &Node::is_text,
             "Check if this node is a text node")
        .def("is_document", &Node::is_document,
             "Check if this node is the document root")
        .def("get_parent", &Node::get_parent,
             "Get the parent node")
        .def("get_children", &Node::get_children,
             "Get all child nodes")
        .def("get_next_sibling", &Node::get_next_sibling,
             "Get the next sibling node")
        .def("get_previous_sibling", &Node::get_previous_sibling,
             "Get the previous sibling node")
        .def("get_next_element", &Node::get_next_element,
             "Get the next element sibling")
        .def("get_previous_element", &Node::get_previous_element,
             "Get the previous element sibling")
        .def("get_descendants", &Node::get_descendants,
             "Get all descendant nodes")
        .def("get_all_descendants_with_text", &Node::get_all_descendants_with_text,
             "Get all descendant nodes including text nodes")
        .def("get_all_parents", &Node::get_all_parents,
             "Get all parent element nodes up to the document root")
        .def("get_all_next_siblings_including_text", &Node::get_all_next_siblings_including_text,
             "Get all following siblings, including text nodes")
        .def("get_all_previous_siblings_including_text", &Node::get_all_previous_siblings_including_text,
             "Get all previous siblings, including text nodes")
        .def("find_all", &Node::find_all,
             py::arg("tag_name") = "", py::arg("attrs") = std::map<std::string, std::string>(),
             "Find all matching nodes")
        .def("find", &Node::find,
             py::arg("tag_name") = "", py::arg("attrs") = std::map<std::string, std::string>(),
             "Find the first matching node")
        .def("find_descendants_by_tag", &Node::find_descendants_by_tag,
             py::arg("tag_name"),
             "Find descendant elements by tag name without including the current node")
        .def("find_all_bulk", &Node::find_all_bulk,
             py::arg("tag_name") = "", py::arg("attrs") = std::map<std::string, std::string>(),
             "Find all matching nodes using bulk operations (eliminates Python/C++ boundary overhead)")
        .def("find_bulk", &Node::find_bulk,
             py::arg("tag_name") = "", py::arg("attrs") = std::map<std::string, std::string>(),
             "Find the first matching node using bulk operations (eliminates Python/C++ boundary overhead)")
        .def("decompose", &Node::decompose,
             "Remove and destroy this node")
        .def("extract", &Node::extract,
             "Remove and return this node")
        .def("clear", &Node::clear,
             "Remove all children from this node")
        .def("replace_with", &Node::replace_with,
             "Replace this node with another node")
        .def("append", &Node::append,
             "Append a child node")
        .def("insert", &Node::insert,
             "Insert a child node at a specific position")
        .def("wrap", &Node::wrap,
             "Wrap this node with another element")
        .def("unwrap", &Node::unwrap,
             "Remove the wrapper while keeping content")
        .def("has_attribute", &Node::has_attribute,
             "Check if node has the specified attribute")
        .def("set_attribute", &Node::set_attribute,
             "Set an attribute value")
        .def("remove_attribute", &Node::remove_attribute,
             "Remove an attribute")
        .def("get_gumbo_node", [](const std::shared_ptr<Node>& node) {
                return reinterpret_cast<std::uintptr_t>(node->get_gumbo_node());
             },
             "Get the raw Gumbo node pointer as an integer identity token")
        .def("find_all_parallel", &Node::find_all_parallel,
             py::arg("tag_name") = "", py::arg("attrs") = std::map<std::string, std::string>(),
             py::arg("enable_parallel") = true,
             "Find all matching nodes using parallel processing")
        .def("find_parallel", &Node::find_parallel,
             py::arg("tag_name") = "", py::arg("attrs") = std::map<std::string, std::string>(),
             py::arg("enable_parallel") = true,
             "Find the first matching node using parallel processing")
        .def("find_all_parallel_optimized", &Node::find_all_parallel_optimized,
             py::arg("tag_name") = "", py::arg("attrs") = std::map<std::string, std::string>(),
             py::arg("enable_parallel") = true,
             "Find all matching nodes using optimized parallel processing")
        .def("find_parallel_optimized", &Node::find_parallel_optimized,
             py::arg("tag_name") = "", py::arg("attrs") = std::map<std::string, std::string>(),
             py::arg("enable_parallel") = true,
             "Find the first matching node using optimized parallel processing")
        .def("find_all_ultra_fast", &Node::find_all_ultra_fast,
             py::arg("tag_name") = "",
             py::arg("attrs") = std::map<std::string, std::string>(),
             "Find all matching nodes using ultra-fast SIMD-optimized processing")
        .def("find_ultra_fast", &Node::find_ultra_fast,
             py::arg("tag_name") = "",
             py::arg("attrs") = std::map<std::string, std::string>(),
             "Find the first matching node using ultra-fast SIMD-optimized processing")
        .def("get_all_text_by_tag", &Node::get_all_text_by_tag,
             py::arg("tag_name"),
             "Get all text content for elements with specified tag (zero-context)")
        .def("get_all_text_by_class", &Node::get_all_text_by_class,
             py::arg("class_name"),
             "Get all text content for elements with specified class (zero-context)")
        .def("get_all_hrefs", &Node::get_all_hrefs,
             "Get all href attributes from anchor tags (zero-context)")
        .def("get_all_src_attributes", &Node::get_all_src_attributes,
             "Get all src attributes from img tags (zero-context)")
        .def("count_elements_by_tag", &Node::count_elements_by_tag,
             py::arg("tag_name"),
             "Count elements with specified tag (zero-context)")
        .def("count_elements_by_class", &Node::count_elements_by_class,
             py::arg("class_name"),
             "Count elements with specified class (zero-context)")
        .def("test_method", &Node::test_method,
             "Test method to verify compilation");
}
