use thiserror::Error;

/// Distributed lock result type.
pub type LockResult<T> = Result<T, LockError>;

/// Errors returned by distributed lock backends.
#[derive(Debug, Error)]
pub enum LockError {
    /// Lock configuration is invalid.
    #[error("invalid distributed lock config: {0}")]
    InvalidConfig(String),

    /// Lock acquisition timed out or backend command timed out.
    #[error("distributed lock timed out: {0}")]
    Timeout(String),

    /// Lock backend connection failed.
    #[error("distributed lock connection error: {0}")]
    Connection(String),

    /// Lock backend rejected the operation through a local breaker.
    #[error("distributed lock circuit breaker rejected operation: {0}")]
    BreakerOpen(String),

    /// The lock is currently held by another owner.
    #[error("distributed lock is already held")]
    Busy,

    /// Unlock did not delete the key because ownership changed or expired.
    #[error("distributed lock owner token did not match")]
    OwnerMismatch,

    /// Backend returned an error.
    #[error("distributed lock backend error: {0}")]
    Backend(String),
}

impl From<crate::cache_redis::RedisCacheError> for LockError {
    fn from(error: crate::cache_redis::RedisCacheError) -> Self {
        match error {
            crate::cache_redis::RedisCacheError::InvalidConfig(message) => {
                Self::InvalidConfig(message)
            }
            crate::cache_redis::RedisCacheError::InvalidUrl { url } => {
                Self::InvalidConfig(format!("invalid redis url `{url}`"))
            }
            crate::cache_redis::RedisCacheError::Connection(message) => Self::Connection(message),
            crate::cache_redis::RedisCacheError::Timeout(operation) => Self::Timeout(operation),
            crate::cache_redis::RedisCacheError::BreakerOpen(message) => Self::BreakerOpen(message),
            other => Self::Backend(other.to_string()),
        }
    }
}
