//! Error types for the RPC layer.
//!
//! Provides a unified error type for client/server operations,
//! with automatic conversions from underlying transport errors.

use thiserror::Error;

/// Errors that can occur during RPC operations.
#[derive(Debug, Error)]
pub enum RpcError {
    /// Connection failed or was refused.
    #[error("Connection error: {0}")]
    Connection(String),

    /// Request timed out.
    #[error("Request timeout: {0}")]
    Timeout(String),

    /// Server returned an error status.
    #[error("Server error: {0}")]
    Server(String),

    /// Failed to serialize/deserialize data.
    #[error("Serialization error: {0}")]
    Serialization(String),

    /// Invalid request or response data.
    #[error("Invalid data: {0}")]
    InvalidData(String),

    /// Maximum retry attempts exceeded.
    #[error("Retry limit exceeded after {attempts} attempts: {last_error}")]
    RetryExhausted {
        /// Number of attempts made.
        attempts: u32,
        /// The last error encountered.
        last_error: String,
    },

    /// Internal transport error.
    #[error("Transport error: {0}")]
    Transport(String),
}

impl From<tonic::Status> for RpcError {
    fn from(status: tonic::Status) -> Self {
        match status.code() {
            tonic::Code::Unavailable | tonic::Code::Unknown => {
                RpcError::Connection(status.message().to_string())
            }
            tonic::Code::DeadlineExceeded => RpcError::Timeout(status.message().to_string()),
            tonic::Code::InvalidArgument => RpcError::InvalidData(status.message().to_string()),
            _ => RpcError::Server(format!("{}: {}", status.code(), status.message())),
        }
    }
}

impl From<tonic::transport::Error> for RpcError {
    fn from(err: tonic::transport::Error) -> Self {
        RpcError::Connection(err.to_string())
    }
}

/// Result type for RPC operations.
pub type Result<T> = std::result::Result<T, RpcError>;
