//! Error types for Chainfire

use thiserror::Error;

/// Result type alias using Chainfire's Error
pub type Result<T> = std::result::Result<T, Error>;

/// Main error type for Chainfire operations
#[derive(Error, Debug)]
pub enum Error {
    /// Storage layer errors
    #[error("Storage error: {0}")]
    Storage(#[from] StorageError),

    /// Raft consensus errors
    #[error("Raft error: {0}")]
    Raft(#[from] RaftError),

    /// Network/RPC errors
    #[error("Network error: {0}")]
    Network(#[from] NetworkError),

    /// Watch errors
    #[error("Watch error: {0}")]
    Watch(#[from] WatchError),

    /// Gossip protocol errors
    #[error("Gossip error: {0}")]
    Gossip(#[from] GossipError),

    /// Configuration errors
    #[error("Configuration error: {0}")]
    Config(String),

    /// Serialization errors
    #[error("Serialization error: {0}")]
    Serialization(String),

    /// Generic internal error
    #[error("Internal error: {0}")]
    Internal(String),
}

/// Storage layer errors
#[derive(Error, Debug)]
pub enum StorageError {
    #[error("Key not found: {0:?}")]
    KeyNotFound(Vec<u8>),

    #[error("IO error: {0}")]
    Io(#[from] std::io::Error),

    #[error("RocksDB error: {0}")]
    RocksDb(String),

    #[error("Serialization error: {0}")]
    Serialization(String),

    #[error("Snapshot error: {0}")]
    Snapshot(String),

    #[error("Log compacted: requested {requested}, compacted to {compacted}")]
    LogCompacted { requested: u64, compacted: u64 },

    #[error("Lease error: {0}")]
    LeaseError(String),
}

/// Raft consensus errors
#[derive(Error, Debug)]
pub enum RaftError {
    #[error("Not leader, leader is node {leader_id:?}")]
    NotLeader { leader_id: Option<u64> },

    #[error("Node {0} not found")]
    NodeNotFound(u64),

    #[error("Proposal failed: {0}")]
    ProposalFailed(String),

    #[error("Timeout waiting for consensus")]
    Timeout,

    #[error("Cluster not initialized")]
    NotInitialized,

    #[error("Already initialized")]
    AlreadyInitialized,

    #[error("Internal Raft error: {0}")]
    Internal(String),
}

/// Network/RPC errors
#[derive(Error, Debug)]
pub enum NetworkError {
    #[error("Connection failed to {addr}: {reason}")]
    ConnectionFailed { addr: String, reason: String },

    #[error("RPC failed: {0}")]
    RpcFailed(String),

    #[error("Node {0} unreachable")]
    Unreachable(u64),

    #[error("Timeout")]
    Timeout,

    #[error("Invalid address: {0}")]
    InvalidAddress(String),
}

/// Watch errors
#[derive(Error, Debug)]
pub enum WatchError {
    #[error("Watch {0} not found")]
    NotFound(i64),

    #[error("Watch {0} already exists")]
    AlreadyExists(i64),

    #[error("Compacted: requested revision {requested}, compacted to {compacted}")]
    Compacted { requested: u64, compacted: u64 },

    #[error("Stream closed")]
    StreamClosed,
}

/// Gossip protocol errors
#[derive(Error, Debug)]
pub enum GossipError {
    #[error("Failed to join cluster: {0}")]
    JoinFailed(String),

    #[error("Broadcast failed: {0}")]
    BroadcastFailed(String),

    #[error("Invalid identity: {0}")]
    InvalidIdentity(String),

    #[error("UDP error: {0}")]
    Udp(String),
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_error_display() {
        let err = Error::Storage(StorageError::KeyNotFound(b"test".to_vec()));
        assert!(err.to_string().contains("Key not found"));

        let err = Error::Raft(RaftError::NotLeader { leader_id: Some(1) });
        assert!(err.to_string().contains("Not leader"));
    }

    #[test]
    fn test_error_conversion() {
        let storage_err = StorageError::KeyNotFound(b"key".to_vec());
        let err: Error = storage_err.into();
        assert!(matches!(err, Error::Storage(_)));
    }
}
