import os
from enum import Enum
from pathlib import Path
from typing import Collection, Mapping, Optional, Sequence, Union, Any, ClassVar, final
from .sized_bytes import bytes32, bytes100
from .sized_ints import uint8, uint16, uint32, uint64, uint128, int8, int16, int32, int64
from typing_extensions import Self

from chia_rs import ReadableBuffer


DATA_SIZE: int
BLOCK_SIZE: int
METADATA_SIZE: int

class IoError(Exception): ...
class FailedLoadingMetadataError(Exception): ...
class FailedLoadingNodeError(Exception): ...
class InvalidBlobLengthError(Exception): ...
class KeyAlreadyPresentError(Exception): ...
class UnableToInsertAsRootOfNonEmptyTreeError(Exception): ...
class UnableToFindALeafError(Exception): ...
class UnknownKeyError(Exception): ...
class IntegrityKeyNotInCacheError(Exception): ...
class IntegrityKeyToIndexCacheIndexError(Exception): ...
class IntegrityParentChildMismatchError(Exception): ...
class IntegrityKeyToIndexCacheLengthError(Exception): ...
class IntegrityLeafHashToIndexCacheLengthError(Exception): ...
class IntegrityUnmatchedChildParentRelationshipsError(Exception): ...
class IntegrityTotalNodeCountError(Exception): ...
class ZeroLengthSeedNotAllowedError(Exception): ...
class NodeNotALeafError(Exception): ...
class StreamingError(Exception): ...
class IndexIsNotAChildError(Exception): ...
class CycleFoundError(Exception): ...
class BlockIndexOutOfBoundsError(Exception): ...
class LeafHashNotFoundError(Exception): ...
class NodeHashNotInNodeMapsError(Exception): ...
class RootHashAndNodeListDisagreementError(Exception): ...
class MoveDestinationIndexNotInUseError(Exception): ...
class MoveSourceIndexNotInUseError(Exception): ...
class DirtyError(Exception): ...
class IncompleteInsertLocationParametersError(Exception): ...
class UnmatchedKeysAndValuesError(Exception): ...
class HashNotFoundError(Exception): ...
class DirtyLeafError(Exception): ...
class HashAlreadyPresentError(Exception): ...
class InvalidChildrenError(Exception): ...
class ReferenceToUnknownParentError(Exception): ...
class UnexpectedParentlessNodeError(Exception): ...
class RootHasParentError(Exception): ...
class ParentDisagreesWithChildError(Exception): ...
class LeafCannotBeParentError(Exception): ...
class LeafCannotBeRootWhenInsertingSubtreeError(Exception): ...

@final
class KeyId:
    raw: int64

    def __new__(cls, raw: int64) -> KeyId: ...

    # TODO: generate
    def __hash__(self) -> int: ...
    def __repr__(self) -> str: ...
    def __deepcopy__(self, memo: object) -> Self: ...
    def __copy__(self) -> Self: ...
    @classmethod
    def from_bytes(cls, blob: bytes) -> Self: ...
    @classmethod
    def from_bytes_unchecked(cls, blob: bytes) -> Self: ...
    @classmethod
    def parse_rust(cls, blob: ReadableBuffer, trusted: bool = False) -> tuple[Self, int]: ...
    def to_bytes(self) -> bytes: ...
    def __bytes__(self) -> bytes: ...
    def stream_to_bytes(self) -> bytes: ...
    def get_hash(self) -> bytes32: ...
    def to_json_dict(self) -> int64: ...
    @classmethod
    def from_json_dict(cls, json_dict: int64) -> Self: ...

@final
class ValueId:
    raw: int64

    def __new__(cls, raw: int64) -> ValueId: ...

    # TODO: generate
    def __hash__(self) -> int: ...
    def __repr__(self) -> str: ...
    def __deepcopy__(self, memo: object) -> Self: ...
    def __copy__(self) -> Self: ...
    @classmethod
    def from_bytes(cls, blob: bytes) -> Self: ...
    @classmethod
    def from_bytes_unchecked(cls, blob: bytes) -> Self: ...
    @classmethod
    def parse_rust(cls, blob: ReadableBuffer, trusted: bool = False) -> tuple[Self, int]: ...
    def to_bytes(self) -> bytes: ...
    def __bytes__(self) -> bytes: ...
    def stream_to_bytes(self) -> bytes: ...
    def get_hash(self) -> bytes32: ...
    def to_json_dict(self) -> int64: ...
    @classmethod
    def from_json_dict(cls, json_dict: int64) -> Self: ...

@final
class TreeIndex:
    raw: uint32

    def __new__(cls, raw: int) -> TreeIndex: ...

    # TODO: generate
    def __hash__(self) -> int: ...
    def __repr__(self) -> str: ...
    def __deepcopy__(self, memo: object) -> Self: ...
    def __copy__(self) -> Self: ...
    @classmethod
    def from_bytes(cls, blob: bytes) -> Self: ...
    @classmethod
    def from_bytes_unchecked(cls, blob: bytes) -> Self: ...
    @classmethod
    def parse_rust(cls, blob: ReadableBuffer, trusted: bool = False) -> tuple[Self, int]: ...
    def to_bytes(self) -> bytes: ...
    def __bytes__(self) -> bytes: ...
    def stream_to_bytes(self) -> bytes: ...
    def get_hash(self) -> bytes32: ...
    def to_json_dict(self) -> uint32: ...
    @classmethod
    def from_json_dict(cls, json_dict: uint32) -> Self: ...

@final
class InternalNode:
    def __new__(cls, hash: bytes32, parent: Optional[TreeIndex], left: TreeIndex, right: TreeIndex) -> InternalNode: ...

    @property
    def parent(self) -> Optional[TreeIndex]: ...
    @property
    def hash(self) -> bytes32: ...

    @property
    def left(self) -> TreeIndex: ...
    @property
    def right(self) -> TreeIndex: ...

    # TODO: generate
    def __hash__(self) -> int: ...
    def __repr__(self) -> str: ...
    def __deepcopy__(self, memo: object) -> Self: ...
    def __copy__(self) -> Self: ...
    @classmethod
    def from_bytes(cls, blob: bytes) -> Self: ...
    @classmethod
    def from_bytes_unchecked(cls, blob: bytes) -> Self: ...
    @classmethod
    def parse_rust(cls, blob: ReadableBuffer, trusted: bool = False) -> tuple[Self, int]: ...
    def to_bytes(self) -> bytes: ...
    def __bytes__(self) -> bytes: ...
    def stream_to_bytes(self) -> bytes: ...
    def get_hash(self) -> bytes32: ...
    def to_json_dict(self) -> uint32: ...
    @classmethod
    def from_json_dict(cls, json_dict: uint32) -> Self: ...
    def replace(self, *, left: bytes32 = ..., right: bytes32 = ...) -> Self: ...


@final
class LeafNode:
    def __new__(cls, hash: bytes32, parent: Optional[TreeIndex], key: KeyId, value: ValueId) -> LeafNode: ...

    @property
    def parent(self) -> Optional[TreeIndex]: ...
    @property
    def hash(self) -> bytes32: ...

    @property
    def key(self) -> KeyId: ...
    @property
    def value(self) -> ValueId: ...

    # TODO: generate
    def __hash__(self) -> int: ...
    def __repr__(self) -> str: ...
    def __deepcopy__(self, memo: object) -> Self: ...
    def __copy__(self) -> Self: ...
    @classmethod
    def from_bytes(cls, blob: bytes) -> Self: ...
    @classmethod
    def from_bytes_unchecked(cls, blob: bytes) -> Self: ...
    @classmethod
    def parse_rust(cls, blob: ReadableBuffer, trusted: bool = False) -> tuple[Self, int]: ...
    def to_bytes(self) -> bytes: ...
    def __bytes__(self) -> bytes: ...
    def stream_to_bytes(self) -> bytes: ...
    def get_hash(self) -> bytes32: ...
    def to_json_dict(self) -> uint32: ...
    @classmethod
    def from_json_dict(cls, json_dict: uint32) -> Self: ...
    def replace(self, *, key: KeyId = ..., value: ValueId = ...) -> Self: ...


@final
class ProofOfInclusionLayer:
    other_hash_side: uint8
    other_hash: bytes32
    combined_hash: bytes32

    def __new__(cls, other_hash_side: uint8, other_hash: bytes32, combined_hash: bytes32) -> ProofOfInclusionLayer: ...

    # TODO: generate
    def __hash__(self) -> int: ...
    def __repr__(self) -> str: ...
    def __deepcopy__(self, memo: object) -> Self: ...
    def __copy__(self) -> Self: ...
    @classmethod
    def from_bytes(cls, blob: bytes) -> Self: ...
    @classmethod
    def from_bytes_unchecked(cls, blob: bytes) -> Self: ...
    @classmethod
    def parse_rust(cls, blob: ReadableBuffer, trusted: bool = False) -> tuple[Self, int]: ...
    def to_bytes(self) -> bytes: ...
    def __bytes__(self) -> bytes: ...
    def stream_to_bytes(self) -> bytes: ...
    def get_hash(self) -> bytes32: ...
    def to_json_dict(self) -> dict[str, Any]: ...
    @classmethod
    def from_json_dict(cls, json_dict: dict[str, Any]) -> Self: ...
    def replace(self, *, other_hash_side: uint8 = ..., other_hash: bytes32 = ..., combined_hash: bytes32 = ...) -> Self: ...

@final
class ProofOfInclusion:
    node_hash: bytes32
    # children before parents
    layers: list[ProofOfInclusionLayer]

    def root_hash(self) -> bytes32: ...
    def valid(self) -> bool: ...

    def __new__(cls, node_hash: bytes32, layers: list[ProofOfInclusionLayer]) -> ProofOfInclusion: ...

    # TODO: generate
    def __hash__(self) -> int: ...
    def __repr__(self) -> str: ...
    def __deepcopy__(self, memo: object) -> Self: ...
    def __copy__(self) -> Self: ...
    @classmethod
    def from_bytes(cls, blob: bytes) -> Self: ...
    @classmethod
    def from_bytes_unchecked(cls, blob: bytes) -> Self: ...
    @classmethod
    def parse_rust(cls, blob: ReadableBuffer, trusted: bool = False) -> tuple[Self, int]: ...
    def to_bytes(self) -> bytes: ...
    def __bytes__(self) -> bytes: ...
    def stream_to_bytes(self) -> bytes: ...
    def get_hash(self) -> bytes32: ...
    def to_json_dict(self) -> dict[str, Any]: ...
    @classmethod
    def from_json_dict(cls, json_dict: dict[str, Any]) -> Self: ...
    def replace(self, *, node_hash: bytes32 = ..., layers: list[ProofOfInclusionLayer] = ...) -> Self: ...


@final
class BlockStatusCache:
    pass


@final
class DeltaReader:
    def __new__(
        cls,
        internal_nodes: Mapping[bytes32, tuple[bytes32, bytes32]],
        leaf_nodes: Mapping[bytes32, tuple[KeyId, ValueId]],
    ) -> DeltaReader: ...

    def add_internal_nodes(self, internal_nodes: Mapping[bytes32, tuple[bytes32, bytes32]]) -> None: ...
    def add_leaf_nodes(self, leaf_nodes: Mapping[bytes32, tuple[KeyId, ValueId]]) -> None: ...
    def get_missing_hashes(self, root_hash: bytes32) -> set[bytes32]: ...
    def collect_from_merkle_blob(self, path: os.PathLike, indexes: list[TreeIndex]) -> None: ...
    def collect_from_merkle_blobs(self, jobs: Collection[tuple[os.PathLike, Collection[TreeIndex]]]) -> None: ...
    def collect_and_return_from_merkle_blobs(self, jobs: Collection[tuple[bytes32, os.PathLike]], hashes: set[bytes32]) -> list[tuple[bytes32, dict[bytes32, TreeIndex]]]: ...
    def create_merkle_blob_and_filter_unused_nodes(self, root_hash: bytes32, interested_hashes: set[bytes32]) -> MerkleBlob: ...


@final
class DeltaFileCache:
    def __new__(cls, path: os.PathLike) -> DeltaFileCache: ...
    def load_previous_hashes(self, path: os.PathLike) -> None: ...
    def get_index(self, hash: bytes32) -> TreeIndex: ...
    def seen_previous_hash(self, hash: bytes32) -> bool: ...
    def get_raw_node(self, index: TreeIndex) -> Union[InternalNode, LeafNode]: ...
    def get_hash_at_index(self, index: TreeIndex) -> bytes32: ...



@final
class MerkleBlob:
    @property
    def blob(self) -> bytearray: ...
    @property
    def block_status_cache(self) -> BlockStatusCache: ...
    @property
    def check_integrity_on_drop(self) -> bool: ...

    def __new__(
        cls,
        blob: bytes,
    ) -> MerkleBlob: ...

    @classmethod
    def from_path(cls, path: os.PathLike) -> MerkleBlob: ...

    def __deepcopy__(self, memo: object) -> Self: ...

    def to_path(self, path: os.PathLike) -> None: ...
    def insert(self, key: KeyId, value: ValueId, hash: bytes32, reference_kid: Optional[KeyId] = None, side: Optional[uint8] = None) -> None: ...
    def upsert(self, key: KeyId, value: ValueId, new_hash: bytes32) -> None: ...
    def delete(self, key: KeyId) -> None: ...
    def get_raw_node(self, index: TreeIndex) -> Union[InternalNode, LeafNode]: ...
    def calculate_lazy_hashes(self) -> None: ...
    def get_lineage_with_indexes(self, index: TreeIndex) -> list[tuple[TreeIndex, Union[InternalNode, LeafNode]]]:...
    def get_nodes_with_indexes(self, index: Optional[TreeIndex] = ...) -> list[tuple[TreeIndex, Union[InternalNode, LeafNode]]]: ...
    def empty(self) -> bool: ...
    def get_root_hash(self) -> bytes32: ...
    def batch_insert(self, keys_values: list[tuple[KeyId, ValueId]], hashes: list[bytes32]): ...
    def get_hash_at_index(self, index: TreeIndex): ...
    def get_keys_values(self) -> dict[KeyId, ValueId]: ...
    def get_key_index(self, key: KeyId) -> TreeIndex: ...
    def get_proof_of_inclusion(self, key: KeyId) -> ProofOfInclusion: ...
    def get_node_by_hash(self, node_hash: bytes32) -> tuple[KeyId, ValueId]: ...
    def get_hashes_indexes(self, leafs_only: bool = ...) -> dict[bytes32, TreeIndex]: ...
    def get_random_leaf_node(self, seed: bytes) -> LeafNode: ...
    def check_integrity(self) -> None: ...

    def __len__(self) -> int: ...

# just disallow * importing so we don't have to maintain this repetitive list
__all__: Sequence[str] = []
