//! [`AuthLayer`] — Tower middleware that injects [`Auth`] into request extensions.
//!
//! Wrap your router with this layer once; all extractors ([`super::extractor`])
//! will then be able to resolve the shared [`Auth`] handle.
//!
//! ```rust,no_run
//! use axum::Router;
//! use rok_auth::{Auth, AuthConfig, axum::AuthLayer};
//!
//! let auth = Auth::new(AuthConfig {
//!     secret: "my-secret".to_string(),
//!     ..Default::default()
//! });
//!
//! let app: Router = Router::new()
//!     // ... routes ...
//!     .layer(AuthLayer::new(auth));
//! ```

use std::sync::Arc;
use std::task::{Context, Poll};

use axum::extract::Request;
use tower::{Layer, Service};

use super::auth_guard::{Guard, GuardLayer};
use crate::Auth;

/// Tower [`Layer`] that inserts an `Arc<Auth>` into every request's extension
/// map, making it available to [`super::extractor::Claims`] and other extractors.
#[derive(Clone)]
pub struct AuthLayer {
    auth: Arc<Auth>,
}

impl AuthLayer {
    pub fn new(auth: Auth) -> Self {
        Self {
            auth: Arc::new(auth),
        }
    }

    /// Create a [`GuardLayer`] from a custom [`Guard`] implementation.
    ///
    /// The guard's resolved [`Claims`] are injected into request extensions and
    /// automatically picked up by `Claims::from_request_parts` and `Ctx`.
    pub fn with_guard<G: Guard>(guard: G) -> GuardLayer<G> {
        GuardLayer::new(guard)
    }
}

impl<S> Layer<S> for AuthLayer {
    type Service = AuthMiddleware<S>;

    fn layer(&self, inner: S) -> Self::Service {
        AuthMiddleware {
            inner,
            auth: Arc::clone(&self.auth),
        }
    }
}

/// The concrete middleware service produced by [`AuthLayer`].
#[derive(Clone)]
pub struct AuthMiddleware<S> {
    inner: S,
    auth: Arc<Auth>,
}

impl<S, B> Service<Request<B>> for AuthMiddleware<S>
where
    S: Service<Request<B>>,
{
    type Response = S::Response;
    type Error = S::Error;
    type Future = S::Future;

    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        self.inner.poll_ready(cx)
    }

    fn call(&mut self, mut req: Request<B>) -> Self::Future {
        req.extensions_mut().insert(Arc::clone(&self.auth));
        self.inner.call(req)
    }
}
