use axum::{
    body::Body,
    extract::Request,
    http::header::AUTHORIZATION,
    middleware::Next,
    response::{IntoResponse, Response},
};
use jsonwebtoken::{Algorithm, DecodingKey, Validation, decode};
use serde::Deserialize;

use crate::rest::{AuthConfig, RestError};

#[derive(Debug, Deserialize)]
struct Claims {
    exp: usize,
}

/// JWT bearer authorization middleware with exact-path public allowlist.
pub async fn auth_middleware(auth: AuthConfig, request: Request<Body>, next: Next) -> Response {
    if auth.is_public(request.uri().path()) {
        return next.run(request).await;
    }

    let token = request
        .headers()
        .get(AUTHORIZATION)
        .and_then(|value| value.to_str().ok())
        .and_then(|value| value.strip_prefix("Bearer "))
        .map(str::trim);

    let Some(token) = token else {
        return RestError::Unauthorized.into_response();
    };

    let mut validation = Validation::new(Algorithm::HS256);
    validation.validate_exp = true;

    match decode::<Claims>(
        token,
        &DecodingKey::from_secret(auth.secret.as_bytes()),
        &validation,
    ) {
        Ok(data) => {
            let _ = data.claims.exp;
            next.run(request).await
        }
        Err(_) => RestError::Unauthorized.into_response(),
    }
}
