use actix_web::{
    Error, HttpMessage, HttpRequest, HttpResponse,
    body::MessageBody,
    dev::{ServiceRequest, ServiceResponse},
    http::header::AUTHORIZATION,
    middleware::Next,
    web,
};

use crate::{AppState, auth::AuthImpl};

pub async fn protected(
    app_state: web::Data<AppState>,
    req: ServiceRequest,
    next: Next<impl MessageBody + 'static>,
) -> Result<ServiceResponse<impl MessageBody>, Error> {
    let Some(token) = token_from_request(&req) else {
        return Ok(req
            .into_response(HttpResponse::Unauthorized().finish())
            .map_into_right_body());
    };

    let user = app_state.auth.for_protected(&token).await?;
    req.extensions_mut().insert(user);

    next.call(req)
        .await
        .map(ServiceResponse::map_into_left_body)
}

fn token_from_request(req: &ServiceRequest) -> Option<String> {
    let token = req
        .headers()
        .get(AUTHORIZATION)
        .and_then(|s| s.to_str().ok())?;

    let token = if token.to_lowercase().starts_with("bearer ") {
        token[7..].to_string()
    } else {
        token.to_string()
    };

    Some(token)
}
