use axum::response::IntoResponse;
use thiserror::Error;

use crate::rest::response::ApiResponse;

/// Result type used by REST helpers.
pub type RestResult<T> = Result<T, RestError>;

/// Errors returned by REST middleware and handlers.
#[derive(Debug, Error)]
pub enum RestError {
    #[error("unauthorized")]
    Unauthorized,

    #[error("request timed out")]
    Timeout,

    #[error("service unavailable: {0}")]
    ServiceUnavailable(String),

    #[error("concurrency limit reached")]
    ConcurrencyLimit,

    #[error("rate limit exceeded")]
    RateLimited,

    #[error("service overloaded")]
    Overloaded,

    #[error("bad request: {0}")]
    BadRequest(String),

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

impl RestError {
    /// Stable error code for uniform JSON responses.
    pub fn code(&self) -> &'static str {
        match self {
            Self::Unauthorized => "UNAUTHORIZED",
            Self::Timeout => "TIMEOUT",
            Self::ServiceUnavailable(_) => "SERVICE_UNAVAILABLE",
            Self::ConcurrencyLimit => "CONCURRENCY_LIMIT",
            Self::RateLimited => "RATE_LIMITED",
            Self::Overloaded => "OVERLOADED",
            Self::BadRequest(_) => "BAD_REQUEST",
            Self::Internal(_) => "INTERNAL",
        }
    }
}

impl IntoResponse for RestError {
    fn into_response(self) -> axum::response::Response {
        ApiResponse::<()>::fail(self.code(), self.to_string()).into_response()
    }
}
