from typing import Any, Callable, Generic, Iterator, TypeVar

class Snapshot:
    """A snapshot of a document's state at a given point in time."""

    @staticmethod
    def from_doc(doc: "Doc") -> "Snapshot":
        """Create a snapshot from a document."""

    @staticmethod
    def decode(data: bytes) -> "Snapshot":
        """Decode a snapshot from its binary representation."""

    def encode(self) -> bytes:
        """Encode the snapshot to its binary representation."""

class Doc:
    """Shared document."""

    def __init__(self, client_id: int | None, skip_gc: bool | None, guid: str | None) -> None:
        """Create a new document with an optional global client ID.
        If no client ID is passed, a random one will be generated."""

    @staticmethod
    def from_snapshot(snapshot: "Snapshot", doc: Doc) -> "Doc":
        """Create a new Doc from a Snapshot and an original Doc."""

    def client_id(self) -> int:
        """Returns the document unique client identifier."""

    def guid(self) -> str:
        """Returns the document globally unique identifier."""

    def create_transaction(self) -> Transaction:
        """Create a document transaction."""

    def create_transaction_with_origin(self, origin: Any) -> Transaction:
        """Create a document transaction with an origin."""

    def get_or_insert_text(self, txn: Transaction, name: str) -> Text:
        """Create a text root type on this document, or get an existing one."""

    def get_or_insert_array(self, txn: Transaction, name: str) -> Array:
        """Create an array root type on this document, or get an existing one."""

    def get_or_insert_map(self, txn: Transaction, name: str) -> Map:
        """Create a map root type on this document, or get an existing one."""

    def get_or_insert_xml_fragment(self, txn: Transaction, name: str) -> XmlFragment:
        """Create an XML fragment root type on this document, or get an existing one."""

    def get_state(self, txn: Transaction) -> bytes:
        """Get the current document state."""

    def get_update(self, txn: Transaction, state: bytes) -> bytes:
        """Get the update from the given state to the current state."""

    def apply_update(self, txn: Transaction, update: bytes) -> None:
        """Apply the update to the document."""

    def roots(self, txn: Transaction) -> dict[str, Text | Array | Map]:
        """Get top-level (root) shared types available in current document."""

    def observe(self, callback: Callable[[TransactionEvent], None]) -> Subscription:
        """Subscribes a callback to be called with the shared document change event.
        Returns a subscription that can be used to unsubscribe."""

    def observe_subdocs(self, callback: Callable[[SubdocsEvent], None]) -> Subscription:
        """Subscribes a callback to be called with the shared document subdoc change event.
        Returns a subscription that can be used to unsubscribe."""

class Subscription:
    """Observer subscription."""

    def drop(self) -> None:
        """Drops the subscription, effectively unobserving."""

class Transaction:
    """Document transaction"""

    def drop(self) -> None:
        """Drop the transaction, effectively committing document changes."""

    def commit(self) -> None:
        """Commit the document changes."""

    @property
    def origin(self) -> Any:
        """The origin of the transaction."""

class TransactionEvent:
    """
    Event generated by the [observe][pycrdt.Doc.observe] method,
    emitted during the transaction commit phase.
    """

    @property
    def update(self) -> bytes:
        """The emitted binary update."""

class SubdocsEvent:
    """
    Event generated by the [observe_subdocs][pycrdt.Doc.observe_subdocs] method,
    emitted during the transaction commit phase."""

class TextEvent:
    """Event generated by `Text.observe` method. Emitted during transaction commit
    phase."""

class ArrayEvent:
    """Event generated by `Array.observe` method. Emitted during transaction commit
    phase."""

class MapEvent:
    """Event generated by `Map.observe` method. Emitted during transaction commit
    phase."""

class XmlEvent:
    """Event generated by `Xml*.observe` methods. Emitted during transaction commit
    phase."""

class Text:
    """Shared text."""

    def len(self, txn: Transaction) -> int:
        """Returns the number of characters visible in the current shared text."""

    def insert(
        self,
        txn: Transaction,
        index: int,
        chunk: str,
        attrs: Iterator[tuple[str, Any]] | None = None,
    ) -> None:
        """Inserts a `chunk` of text at a given `index`, with optional attributes."""

    def insert_embed(
        self,
        txn: Transaction,
        index: int,
        embed: Any,
        attrs: Iterator[tuple[str, Any]] | None = None,
    ) -> None:
        """Inserts an embed at a given `index`, with optional attributes."""

    def format(
        self, txn: Transaction, index: int, len: int, attrs: Iterator[tuple[str, Any]]
    ) -> None:
        """Formats a range of elements."""

    def remove_range(self, txn: Transaction, index: int, len: int) -> None:
        """Removes up to `len` characters from th current shared text, starting at
        given`index`."""

    def get_string(self, txn: Transaction) -> str:
        """Returns a text representation of the current shared text."""

    def diff(self, txn: Transaction) -> list[tuple[Any, dict[str, Any] | None]]:
        """Returns a sequence of formatted chunks."""

    def observe(self, callback: Callable[[TextEvent], None]) -> Subscription:
        """Subscribes a callback to be called with the shared text change event.
        Returns a subscription that can be used to unsubscribe."""

    def unobserve(self, subscription: Subscription) -> None:
        """Unsubscribes previously subscribed event callback identified by given
        `subscription`."""

class Array:
    """Shared array."""

    def len(self, txn: Transaction) -> int:
        """Returns the number of elements in the current array."""

    def insert(self, txn: Transaction, index: int, value: Any) -> None:
        """Inserts `value` at the given `index`."""

    def remove_range(self, txn: Transaction, index: int, len: int) -> None:
        """Removes 'len' elements starting at provided `index`."""

    def get(self, txn: Transaction, index: int) -> Any:
        """Retrieves a value stored at a given `index`."""

    def to_json(self, txn: Transaction) -> str:
        """Returns a JSON representation of the current array."""

    def observe(self, callback: Callable[[TextEvent], None]) -> Subscription:
        """Subscribes a callback to be called with the array change event.
        Returns a subscription that can be used to unsubscribe."""

    def observe_deep(self, callback: Callable[[TextEvent], None]) -> Subscription:
        """Subscribes a callback to be called with the array change event
        and its nested elements.
        Returns a subscription that can be used to unsubscribe."""

    def unobserve(self, subscription: Subscription) -> None:
        """Unsubscribes previously subscribed event callback identified by given
        `subscription`."""

class Map:
    """Shared map."""

    def len(self, txn: Transaction) -> int:
        """Returns a number of characters visible in a current text data structure."""

    def insert(self, txn: Transaction, key: str, value: Any) -> None:
        """Inserts `value` at the given `key`."""

    def remove(self, txn: Transaction, key: str) -> None:
        """Removes the `key` entry."""

    def get(self, txn: Transaction, key: str) -> Any:
        """Retrieves a value stored under a given `key`."""

    def to_json(self, txn: Transaction) -> str:
        """Returns a JSON representation of the current map."""

    def observe(self, callback: Callable[[TextEvent], None]) -> Subscription:
        """Subscribes a callback to be called with the map change event.
        Returns a subscription that can be used to unsubscribe."""

    def observe_deep(self, callback: Callable[[TextEvent], None]) -> Subscription:
        """Subscribes a callback to be called with the map change event
        and its nested elements.
        Returns a subscription that can be used to unsubscribe."""

    def unobserve(self, subscription: Subscription) -> None:
        """Unsubscribes previously subscribed event callback identified by given
        `subscription`."""

    def has(self, txn: Transaction, key: str) -> bool:
        """Returns true if the given key exists in the map."""

class XmlFragment:
    def parent(self) -> XmlFragment | XmlElement | XmlText | None:
        """Returns the parent of the XML fragment, if any."""

    def get_string(self, txn: Transaction) -> str:
        """Returns a text representation of the current shared XML."""

    def len(self, txn: Transaction) -> int:
        """Returns the numer of children of the current shared XML."""

    def get(self, txn: Transaction, index: int) -> XmlFragment | XmlElement | XmlText | None:
        """Gets a child item by index, or `None` if the index is out of bounds."""

    def remove_range(self, txn: Transaction, index: int, len: int) -> None:
        """Removes a range of children."""

    def insert_str(self, txn: Transaction, index: int, text: str) -> XmlText:
        """Inserts a text node."""

    def insert_element_prelim(self, txn: Transaction, index: int, tag: str) -> XmlElement:
        """Inserts an empty element node at given index, and returns it."""

    def observe(self, callback: Callable[[XmlEvent], None]) -> Subscription:
        """Subscribes a callback to be called with the XML change event.
        Returns a subscription that can be used to unsubscribe."""

    def observe_deep(self, callback: Callable[[XmlEvent], None]) -> Subscription:
        """Subscribes a callback to be called with the XML change event
        and its nested elements.
        Returns a subscription that can be used to unsubscribe."""

class XmlElement:
    def parent(self) -> XmlFragment | XmlElement | XmlText | None:
        """Returns the parent of the XML element, if any."""

    def get_string(self, txn: Transaction) -> str:
        """Returns a text representation of the current shared XML."""

    def len(self, txn: Transaction) -> int:
        """Returns the numer of children of the current shared XML."""

    def get(self, txn: Transaction, index: int) -> XmlFragment | XmlElement | XmlText | None:
        """Gets a child item by index, or `None` if the index is out of bounds."""

    def remove_range(self, txn: Transaction, index: int, len: int) -> None:
        """Removes a range of children."""

    def insert_str(self, txn: Transaction, index: int, text: str) -> XmlText:
        """Inserts a text node."""

    def insert_element_prelim(self, txn: Transaction, index: int, tag: str) -> XmlElement:
        """Inserts an empty element node at a given index, and returns it."""

    def attributes(self, txn: Transaction) -> list[tuple[str, str]]:
        """Gets all attributes, as a list of `(key, value)` tuples."""

    def attribute(self, txn: Transaction, name: str) -> str | None:
        """Gets an attribute, or `None` if the attribute does not exist."""

    def insert_attribute(self, txn: Transaction, name: str, value: str) -> None:
        """Inserts or overwrites an attribute."""

    def remove_attribute(self, txn: Transaction, name: str) -> None:
        """Removes an attribute."""

    def siblings(self, txn) -> list[XmlFragment | XmlElement | XmlText]:
        """Gets the siblings of this node."""

    def observe(self, callback: Callable[[XmlEvent], None]) -> Subscription:
        """Subscribes a callback to be called with the XML change event.
        Returns a subscription that can be used to unsubscribe."""

    def observe_deep(self, callback: Callable[[XmlEvent], None]) -> Subscription:
        """Subscribes a callback to be called with the XML change event
        and its nested elements.
        Returns a subscription that can be used to unsubscribe."""

class XmlText:
    def parent(self) -> XmlFragment | XmlElement | XmlText | None:
        """Returns the XML text parent, if any."""

    def get_string(self, txn: Transaction) -> str:
        """Returns a text representation of the current shared XML."""

    def attributes(self, txn: Transaction) -> list[tuple[str, str]]:
        """Gets all attributes, as a list of `(key, value)` tuples."""

    def attribute(self, txn: Transaction, name: str) -> str | None:
        """Gets an attribute, or `None` if the attribute does not exist."""

    def insert_attribute(self, txn: Transaction, name: str, value: str) -> None:
        """Inserts or overwrites an attribute."""

    def remove_attribute(self, txn: Transaction, name: str) -> None:
        """Removes an attribute."""

    def siblings(self, txn: Transaction) -> list[XmlFragment | XmlElement | XmlText]:
        """Gets the siblings of this node."""

    def insert(
        self,
        txn: Transaction,
        index: int,
        text: str,
        attrs: Iterator[tuple[str, Any]] | None = None,
    ):
        """Inserts text, with optional with attributes."""

    def remove_range(self, txn: Transaction, index: int, len: int):
        """Removes text."""

    def format(self, txn: Transaction, index: int, len: int, attrs: Iterator[tuple[str, Any]]):
        """Adds attributes to a section of text."""

    def diff(self, txn: Transaction) -> list[tuple[Any, dict[str, Any] | None]]:
        """Returns a sequence of formatted chunks."""

    def observe(self, callback: Callable[[XmlEvent], None]) -> Subscription:
        """Subscribes a callback to be called with the XML change event.
        Returns a subscription that can be used to unsubscribe."""

    def observe_deep(self, callback: Callable[[XmlEvent], None]) -> Subscription:
        """Subscribes a callback to be called with the XML change event
        and its nested elements.
        Returns a subscription that can be used to unsubscribe."""

class UndoManager:
    """Undo manager."""

    def __init__(
        self,
        capture_timeout_millis: int,
        timestamp: Callable[[], int],
        undo_stack: list[StackItem] | None = None,
        redo_stack: list[StackItem] | None = None,
    ) -> None:
        """Creates an undo manager."""

    def expand_scope(self, doc: Doc, scope: Text | Array | Map) -> None:
        """Extends a list of shared types tracked by current undo manager by a given scope."""

    def include_origin(self, origin: int) -> None:
        """Extends a list of origins tracked by current undo manager by a given origin."""

    def exclude_origin(self, origin: int) -> None:
        """Removes an origin from the list of origins tracked by current undo manager."""

    def can_undo(self) -> bool:
        """Whether there is any change to undo."""

    def undo(self) -> bool:
        """Undo last action tracked by current undo manager."""

    def can_redo(self) -> bool:
        """Whether there is any change to redo."""

    def redo(self) -> bool:
        """Redo last action previously undone by current undo manager."""

    def clear_all(self) -> None:
        """Clear all items stored within current undo manager."""

    def undo_stack(self) -> list[StackItem]:
        """Returns the undo manager's undo stack."""

    def redo_stack(self) -> list[StackItem]:
        """Returns the undo manager's redo stack."""

class IdSet:
    """A set of deletions in a CRDT document."""

    def __init__(self) -> None:
        """Create a new empty IdSet."""

    def encode(self) -> bytes:
        """Encode the IdSet to bytes."""

    @staticmethod
    def decode(data: bytes) -> IdSet:
        """Decode a IdSet from bytes."""

class ContentAttribute:
    """A named attribute attached to a range of block IDs in an [IdMap][pycrdt.IdMap]."""

    def __init__(self, name: str, value: Any) -> None:
        """Create a new attribute.

        Args:
            name: The attribute name.
            value: The attribute value. Must be JSON-serializable (None, bool, int,
                float, str, list, tuple, or dict). Like every other pycrdt value, numbers
                follow JS semantics: integers within the JS safe-integer range (|n| < 2**53)
                are stored and returned as floats; larger integers stay ints.
        """

    @property
    def name(self) -> str:
        """The attribute name."""

    @property
    def value(self) -> Any:
        """The attribute value, as a JSON-compatible object.

        Numbers follow JS semantics: an integer like `5` is returned as `5.0` (unless it
        exceeds the JS safe-integer range), matching how pycrdt represents Map/Array values.
        """

class AttrRange:
    """A contiguous clock range together with the attributes attached to it.

    Returned by [IdMap.attributions][pycrdt.IdMap.attributions] and
    [IdMap.entries][pycrdt.IdMap.entries].
    """

    @property
    def start(self) -> int:
        """The inclusive start clock of the range."""

    @property
    def end(self) -> int:
        """The exclusive end clock of the range."""

    @property
    def attributes(self) -> list[ContentAttribute]:
        """The attributes attached to this range (possibly empty)."""

class IdMap:
    """A set of block ID ranges, each associated with attribute metadata.

    Similar to [IdSet][pycrdt.IdSet], but it additionally attaches
    [ContentAttribute][pycrdt.ContentAttribute] metadata to individual block ranges.
    """

    def __init__(self) -> None:
        """Create a new, empty IdMap."""

    def insert(
        self, client: int, clock: int, length: int, attributes: list[ContentAttribute]
    ) -> None:
        """Attach `attributes` to the range `[clock, clock + length)` of `client`.

        Inserting an empty attribute list or a zero-length range is a no-op.
        """

    def remove(self, client: int, clock: int, length: int) -> None:
        """Remove the range `[clock, clock + length)` of `client` from the map."""

    def contains(self, client: int, clock: int) -> bool:
        """Return whether the `(client, clock)` ID is contained in the map."""

    def is_empty(self) -> bool:
        """Return whether the map is empty."""

    def attributions(self, client: int, clock: int, length: int) -> list[AttrRange]:
        """Return the attributions covering `[clock, clock + length)` of `client`.

        The result spans the whole queried range; gaps with no attributes are returned
        as [AttrRange][pycrdt.AttrRange] objects with an empty `attributes` list.
        """

    def entries(self) -> list[tuple[int, AttrRange]]:
        """Return every `(client, AttrRange)` entry stored in the map."""

    def merge_with(self, other: IdMap) -> None:
        """Merge `other` into this map in place (union of ranges and attributes)."""

    @staticmethod
    def merge_many(maps: list[IdMap]) -> IdMap:
        """Return the union of several maps."""

    def intersect_with(self, other: IdMap) -> None:
        """Intersect this map with `other` in place."""

    def diff_with(self, other: IdMap | IdSet) -> None:
        """Remove from this map every range that is also present in `other`."""

    def filter(self, predicate: Callable[[list[ContentAttribute]], bool]) -> IdMap:
        """Return a new map keeping only the ranges whose attributes satisfy `predicate`.

        `predicate` is called with the list of [ContentAttribute][pycrdt.ContentAttribute]
        of each range and must return a boolean.
        """

    def as_id_set(self) -> IdSet:
        """Return an [IdSet][pycrdt.IdSet] with the same ranges, stripped of attributes."""

    @staticmethod
    def from_set(id_set: IdSet, attributes: list[ContentAttribute]) -> IdMap:
        """Build an IdMap from an IdSet, attaching `attributes` to every range."""

    def encode(self) -> bytes:
        """Encode the map to bytes."""

    @staticmethod
    def decode(data: bytes) -> IdMap:
        """Decode a map from bytes."""

    def __bool__(self) -> bool:
        """Return whether the map is non-empty."""

    def __iter__(self) -> Iterator[tuple[int, AttrRange]]:
        """Iterate over the ``(client, AttrRange)`` entries."""

    def __or__(self, other: IdMap) -> IdMap:
        """Return the union (merge) of this map and `other`."""

    def __ior__(self, other: IdMap) -> IdMap:
        """Merge `other` into this map in place."""

    def __and__(self, other: IdMap) -> IdMap:
        """Return the intersection of this map and `other`."""

    def __iand__(self, other: IdMap) -> IdMap:
        """Intersect this map with `other` in place."""

    def __sub__(self, other: IdMap | IdSet) -> IdMap:
        """Return this map with every range present in `other` removed."""

    def __isub__(self, other: IdMap | IdSet) -> IdMap:
        """Remove from this map every range present in `other`, in place."""

MetaT = TypeVar("MetaT")

class StackItem(Generic[MetaT]):
    """A unit of work for the [UndoManager][pycrdt.UndoManager], consisting of
    compressed information about all updates and deletions tracked by it.
    """

    def __init__(
        self, doc: Doc, deletions: IdSet, insertions: IdSet, meta: MetaT | None = None
    ) -> None:
        """Create a new StackItem.

        Args:
            doc: The document this stack item belongs to.
            deletions: The IdSet of deletions.
            insertions: The IdSet of insertions.
            meta: Optional metadata (can be any Python object).
        """

    @property
    def deletions(self) -> IdSet:
        """Get the deletions IdSet."""

    @property
    def insertions(self) -> IdSet:
        """Get the insertions IdSet."""

    @property
    def meta(self) -> MetaT | None:
        """Custom metadata. Can be any Python object."""

    @staticmethod
    def merge(
        a: "StackItem[MetaT]",
        b: "StackItem[MetaT]",
        merge_meta: Callable[[MetaT | None, MetaT | None], Any] | None = None,
    ) -> "StackItem[Any]":
        """Merge two StackItems into one containing the union of their deletions and insertions.

        Args:
            a: First StackItem to merge.
            b: Second StackItem to merge.
            merge_meta: Optional function to handle metadata conflicts.
                Takes (meta_a, meta_b) and returns the merged metadata.
                If None, keeps the first item's metadata.
        """

class StickyIndex:
    def get_offset(self, txn: Transaction) -> int: ...
    def encode(self) -> bytes: ...
    def to_json_string(self) -> str: ...
    def get_assoc(self) -> int: ...

def merge_updates(updates: tuple[bytes, ...]) -> bytes: ...
def get_state(update: bytes) -> bytes: ...
def get_update(update: bytes, state: bytes) -> bytes: ...
def decode_sticky_index(data: bytes) -> StickyIndex: ...
def get_sticky_index_from_json_string(data: str) -> StickyIndex: ...
