use std::sync::Arc;

use axum::{
    body::Body,
    extract::Request,
    middleware::Next,
    response::{IntoResponse, Response},
};
use tokio::sync::Semaphore;

use crate::rest::{
    RestError,
    middleware::{MiddlewareMetrics, record_resilience_event},
};

/// Applies a fail-fast concurrency limit to one request.
pub async fn concurrency_middleware(
    semaphore: Arc<Semaphore>,
    metrics: MiddlewareMetrics,
    request: Request<Body>,
    next: Next,
) -> Response {
    let permit = match semaphore.try_acquire_owned() {
        Ok(permit) => permit,
        Err(_) => {
            record_resilience_event(&metrics, "concurrency", "rejected");
            return RestError::ConcurrencyLimit.into_response();
        }
    };

    record_resilience_event(&metrics, "concurrency", "allowed");
    let response = next.run(request).await;
    drop(permit);
    response
}
