use thiserror::Error;

#[derive(Error, Debug)]
pub enum StorageError {
    #[error("session not found: {0}")]
    NotFound(String),

    #[error("session already exists: {0}")]
    AlreadyExists(String),

    #[error("database error: {0}")]
    Database(#[from] sqlx::Error),

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

    #[error("schema migration error: {0}")]
    Migration(String),

    #[error("storage conflict: {0}")]
    Conflict(String),
}

impl StorageError {
    /// Only retry failures that can plausibly clear without changing the data.
    pub fn is_transient(&self) -> bool {
        let Self::Database(error) = self else {
            return false;
        };
        match error {
            sqlx::Error::PoolTimedOut => true,
            sqlx::Error::Database(database) => {
                matches!(database.code().as_deref(), Some("5" | "6" | "261" | "262")) || {
                    let message = database.message().to_ascii_lowercase();
                    message.contains("database is locked") || message.contains("database is busy")
                }
            }
            _ => false,
        }
    }
}
