//! Authentication middleware for JWT validation
//!
//! Validates JWT tokens in Authorization headers and makes user claims available
//! to downstream handlers through response extensions.

use crate::state::AppState;
use axum::{extract::State, http::HeaderMap, middleware::Next, response::Response};
use shared_api_lib::auth::jwt::Claims;
use shared_api_lib::error::ApiError;

/// Extract Bearer token from Authorization header
fn extract_bearer_token(headers: &HeaderMap) -> Result<String, ApiError> {
    headers
        .get("Authorization")
        .and_then(|h| h.to_str().ok())
        .ok_or_else(|| ApiError::Unauthorized("Missing Authorization header".to_string()))?
        .strip_prefix("Bearer ")
        .map(|s| s.to_string())
        .ok_or_else(|| {
            ApiError::Unauthorized(
                "Invalid Authorization header format. Expected: Bearer <token>".to_string(),
            )
        })
}

/// Authentication middleware for JWT validation
///
/// Validates Bearer tokens from Authorization header using JwtProvider.
/// Inserts validated claims into request extensions for downstream handlers.
///
/// Usage:
/// ```rust,ignore
/// let app = Router::new()
///     .route("/protected", get(protected_handler))
///     .with_state(app_state)
///     .layer(axum::middleware::from_fn_with_state(
///         app_state,
///         auth_middleware
///     ));
/// ```
pub async fn auth_middleware(
    State(state): State<AppState>,
    headers: HeaderMap,
    mut req: axum::extract::Request,
    next: Next,
) -> Result<Response, ApiError> {
    let token = extract_bearer_token(&headers)?;

    // Get JWT provider from app state
    let jwt_provider = state
        .jwt_provider
        .as_ref()
        .ok_or_else(|| ApiError::Unauthorized("JWT authentication not configured".to_string()))?;

    // Validate JWT token signature and claims
    let claims = jwt_provider
        .verify_token(&token)
        .map_err(|e| ApiError::Unauthorized(format!("JWT validation failed: {}", e)))?;

    // Check token against revoked_tokens table
    let token_jti = format!("{}_{}", claims.sub, claims.exp);
    let revoked_token = sqlx::query("SELECT id FROM revoked_tokens WHERE token_jti = ?")
        .bind(&token_jti)
        .fetch_optional(state.db.pool())
        .await
        .map_err(|e| ApiError::Internal(format!("Failed to check revoked tokens: {}", e)))?;

    if revoked_token.is_some() {
        return Err(ApiError::Unauthorized("Token has been revoked".to_string()));
    }

    // Insert claims into request extensions for downstream handlers
    req.extensions_mut().insert(claims);

    Ok(next.run(req).await)
}

/// Extract claims from request extensions (after middleware)
///
/// Usage in handlers:
/// ```rust,ignore
/// async fn protected_handler(
///     axum::Extension(claims): axum::Extension<Claims>,
/// ) -> impl IntoResponse {
///     format!("Hello {}", claims.sub)
/// }
/// ```
#[allow(dead_code)]
pub fn extract_claims(extensions: &axum::http::Extensions) -> Result<Claims, ApiError> {
    extensions.get::<Claims>().cloned().ok_or_else(|| {
        ApiError::Unauthorized("User claims not found in request context".to_string())
    })
}

#[cfg(test)]
mod tests {
    use super::*;
    use axum::http::HeaderValue;

    #[test]
    fn test_extract_bearer_token() {
        let mut headers = HeaderMap::new();
        headers.insert(
            "Authorization",
            HeaderValue::from_static("Bearer my_test_token"),
        );

        let token = extract_bearer_token(&headers).unwrap();
        assert_eq!(token, "my_test_token");
    }

    #[test]
    fn test_extract_bearer_token_missing_header() {
        let headers = HeaderMap::new();
        let result = extract_bearer_token(&headers);
        assert!(result.is_err());
    }

    #[test]
    fn test_extract_bearer_token_invalid_format() {
        let mut headers = HeaderMap::new();
        headers.insert("Authorization", HeaderValue::from_static("InvalidFormat"));
        let result = extract_bearer_token(&headers);
        assert!(result.is_err());
    }
}
