use crate::{Hash, InternalNode, KeyId, TreeIndex};
use thiserror::Error;

#[cfg_attr(feature = "py-bindings", derive(chia_datalayer_macro::PythonError))]
#[derive(Debug, Error)]
pub enum Error {
    #[error("IO error: {0}")]
    Io(#[from] std::io::Error),
    #[error("failed loading metadata: {0}")]
    FailedLoadingMetadata(chia_traits::chia_error::Error),
    #[error("failed loading node: {0}")]
    FailedLoadingNode(chia_traits::chia_error::Error),
    #[error("blob length must be a multiple of block count, found extra bytes: {0}")]
    InvalidBlobLength(usize),
    #[error("key already present")]
    KeyAlreadyPresent(),
    #[error("hash already present")]
    HashAlreadyPresent(),
    #[error("requested insertion at root but tree not empty")]
    UnableToInsertAsRootOfNonEmptyTree(),
    #[error("unable to find a leaf")]
    UnableToFindALeaf(),
    #[error("unknown key: {0:?}")]
    UnknownKey(KeyId),
    #[error("key not in key to index cache: {0:?}")]
    IntegrityKeyNotInCache(KeyId),
    #[error("key to index cache for {0:?} should be {1:?} got: {2:?}")]
    IntegrityKeyToIndexCacheIndex(KeyId, TreeIndex, TreeIndex),
    #[error("parent and child relationship mismatched: {0:?}")]
    IntegrityParentChildMismatch(TreeIndex),
    #[error("found {0:?} leaves but key to index cache length is: {1}")]
    IntegrityKeyToIndexCacheLength(usize, usize),
    #[error("found {0:?} leaves but leaf hash to index cache length is: {1}")]
    IntegrityLeafHashToIndexCacheLength(usize, usize),
    #[error("unmatched parent -> child references found: {0}")]
    IntegrityUnmatchedChildParentRelationships(usize),
    #[error("expected total node count {0:?} found: {1:?}")]
    IntegrityTotalNodeCount(TreeIndex, usize),
    #[error("zero-length seed bytes not allowed")]
    ZeroLengthSeedNotAllowed(),
    #[error("node not a leaf: {0:?}")]
    NodeNotALeaf(InternalNode),
    #[error("from streamable: {0:?}")]
    Streaming(chia_traits::chia_error::Error),
    #[error("index not a child: {0}")]
    IndexIsNotAChild(TreeIndex),
    #[error("cycle found")]
    CycleFound(),
    #[error("recursion depth exceeded while building merkle blob (max=64)")]
    RecursionDepthExceeded(),
    #[error("block index out of bounds: {0}")]
    BlockIndexOutOfBounds(TreeIndex),
    #[error("leaf hash not found: {0:?}")]
    LeafHashNotFound(Hash),
    #[error("root hash and node list disagreement")]
    RootHashAndNodeListDisagreement(),
    #[error("node hash not in nodes: {0:?}")]
    NodeHashNotInNodeMaps(Hash),
    #[error("move source index not in use: {0:?}")]
    MoveSourceIndexNotInUse(TreeIndex),
    #[error("move destination index not in use: {0:?}")]
    MoveDestinationIndexNotInUse(TreeIndex),
    #[error("must specify neither or both of reference_kid and side")]
    IncompleteInsertLocationParameters(),
    #[error("hash is dirty for index: {0:?}")]
    Dirty(TreeIndex),
    #[error("hash is dirty for leaf index: {0:?}")]
    DirtyLeaf(TreeIndex),
    #[error("key/value and hash collection lengths must match: {0:?} keys/values, {0:?} hashes")]
    UnmatchedKeysAndValues(usize, usize),
    #[error("hash not found: {0:?}")]
    HashNotFound(Hash),
    #[error("reference to unknown parent")]
    ReferenceToUnknownParent(),
    #[error("root has parent")]
    RootHasParent(),
    #[error("unexpected parentless node")]
    UnexpectedParentlessNode(),
    #[error("child's parent disclaims the child")]
    ParentDisagreesWithChild(),
    #[error("leaf cannot be parent")]
    LeafCannotBeParent(),
    #[error("invalid children")]
    InvalidChildren(),
    #[error("insert subtree requires reference leaf is not root")]
    LeafCannotBeRootWhenInsertingSubtree(),
}
