from typing import Any, List, Optional, Tuple, Iterator

class TaxonomyError(Exception):
    """Raised when an error occurs in the taxonomy library."""

    ...

class TaxonomyNode:
    """The data returned when looking up a taxonomy by id or by name"""

    id: str
    name: str
    parent: Optional[str]
    rank: str

    def __hash__(self) -> int: ...
    def __repr__(self) -> str: ...
    def __getitem__(self, key: str) -> Any: ...
    def __eq__(self, other: object) -> bool: ...
    def __ne__(self, other: object) -> bool: ...

class Taxonomy:
    """
    The Taxonomy object provides the primary interface for exploring a
    biological taxonomy.
    """
    @property
    def root(self) -> TaxonomyNode: ...
    @classmethod
    def from_gtdb(cls, value: str) -> "Taxonomy":
        """Load a Taxonomy from a GTDB-encoded string."""
        ...

    @classmethod
    def from_json(cls, value: str, json_pointer: Optional[str] = None) -> "Taxonomy":
        """
        Load a Taxonomy from a JSON-encoded string. The format can either be
        of the tree or node_link_data types and will be automatically detected.
        If `path` is specified, the JSON will be traversed to that sub-object
        before being parsed as a taxonomy. `path` has to be a valid JSON path string.
        """
        ...

    @classmethod
    def from_newick(cls, value: str) -> "Taxonomy":
        """Load a Taxonomy from a Newick-encoded string."""
        ...

    @classmethod
    def from_ncbi(cls, dump_dir: str) -> "Taxonomy":
        """
        Load a Taxonomy from a directory.
        The directory must contain the `nodes.dmp` and `names.dmp` files.
        """
        ...

    @classmethod
    def from_phyloxml(cls, value: str) -> "Taxonomy":
        """Load a Taxonomy from a PhyloXML-encoded string. Experimental."""
        ...

    def clone(self) -> "Taxonomy":
        """Clone the current taxonomy"""
        ...

    def to_json_tree(self) -> bytes:
        """Export a Taxonomy as a JSON-encoded byte string in a tree format"""
        ...

    def to_json_node_links(self) -> bytes:
        """Export a Taxonomy as a JSON-encoded byte string in a node link format"""
        ...

    def to_newick(self) -> bytes:
        """Export a Taxonomy as a Newick-encoded byte string."""
        ...

    def to_ncbi(self, output_dir: str) -> None:
        """
        Export a Taxonomy to NCBI format files (nodes.dmp and names.dmp).
        The output directory will be created if it doesn't exist.
        """
        ...

    def node(self, tax_id: str) -> Optional[TaxonomyNode]:
        """Find a node by its id. Returns `None` if not found"""
        ...

    def find_all_by_name(self, name: str) -> List[TaxonomyNode]:
        """Find a node by its name, Raises an exception if not found."""
        ...

    def parent_with_distance(
        self, tax_id: str, at_rank: Optional[str] = None
    ) -> Tuple[Optional[TaxonomyNode], Optional[float]]:
        """
        Return the immediate parent taxonomy node of the node id provided and the distance to it.
        If `at_rank` is provided, scan all the nodes in the node's lineage and return
        the parent id at that rank.
        """
        ...

    def parent(self, tax_id: str, at_rank: Optional[str] = None) -> Optional[TaxonomyNode]:
        """
        Return the immediate parent taxonomy node of the node id provided.
        If `at_rank` is provided, scan all the nodes in the node's lineage and return
        the parent id at that rank.
        """
        ...

    def children(self, tax_id: str) -> List[TaxonomyNode]:
        """Return a list of direct child taxonomy nodes from the node id provided."""
        ...

    def descendants(self, tax_id: str) -> List[TaxonomyNode]:
        """Return a list of all child taxonomy nodes from the node id provided."""
        ...

    def lineage(self, tax_id: str) -> List[TaxonomyNode]:
        """
        Return a list of all the parent taxonomy nodes of the node id provided
        (including that node itself).
        """
        ...

    def internal_index(self, tax_id: str) -> int:
        """Return the internal integer ID generated by the taxonomy library"""
        ...

    def parents(self, tax_id: str) -> List[TaxonomyNode]:
        """
        Return a list of all the parent taxonomy nodes of the node id provided.
        It is equivalent to `lineage` except it doesn't include itself
        """
        ...

    def lca(self, id1: str, id2: str) -> Optional[TaxonomyNode]:
        """Return the lowest common ancestor of two taxonomy nodes."""
        ...

    def prune(
        self, keep: Optional[List[str]] = None, remove: Optional[List[str]] = None
    ) -> "Taxonomy":
        """
        Return a copy of the taxonomy containing:
         - only the nodes in `keep` and their parents if provided
         - all of the nodes except those in remove and their children if provided
        """
        ...

    def remove_node(self, tax_id: str) -> None:
        """Remove the node from the tree."""
        ...

    def add_node(self, parent_id: str, tax_id: str, name: str, rank: str) -> None:
        """Add a new node to the tree at the parent provided."""
        ...

    def edit_node(
        self,
        tax_id: str,
        name: Optional[str] = None,
        rank: Optional[str] = None,
        parent_id: Optional[str] = None,
        parent_distance: Optional[float] = None,
    ) -> None:
        """Edit properties on a taxonomy node."""
        ...

    def __repr__(self) -> str: ...
    def __len__(self) -> int: ...
    def __getitem__(self, tax_id: str) -> TaxonomyNode: ...
    def __delitem__(self, tax_id: str) -> None: ...
    def __contains__(self, tax_id: str) -> bool: ...
    def __iter__(self) -> Iterator[str]: ...

class TaxonomyIterator:
    def __next__(self) -> Optional[str]: ...
    def __iter__(self) -> "TaxonomyIterator": ...
