use axum::{
    extract::Request,
    http::StatusCode,
    middleware::Next,
    response::Response,
};

// API key auth middleware. Checks X-API-Key header against configured key.
// Fixed P5-001 (2026-04-23): previously #[allow(dead_code)] — the function
// existed but was never layered onto the router, so [auth] enabled=true
// silently enforced nothing. Now wired via from_fn_with_state in main.rs.
pub async fn api_key_auth(
    axum::extract::State(expected): axum::extract::State<ApiKey>,
    request: Request,
    next: Next,
) -> Result<Response, StatusCode> {
    // /health stays public (LB/systemd probes). Every other route is gated.
    if request.uri().path() == "/health" {
        return Ok(next.run(request).await);
    }

    let provided = request
        .headers()
        .get("x-api-key")
        .and_then(|v| v.to_str().ok());

    // Constant-time-ish eq on the raw bytes; good enough for a shared-secret
    // X-API-Key. Timing-attack resistance here matters less than the
    // equivalent HMAC check would; adopt subtle crate if key-space grows.
    match provided {
        Some(key) if eq_ct(key.as_bytes(), expected.0.as_bytes()) => {
            Ok(next.run(request).await)
        }
        _ => {
            tracing::warn!(
                path = %request.uri().path(),
                "unauthorized request: missing or invalid API key",
            );
            Err(StatusCode::UNAUTHORIZED)
        }
    }
}

fn eq_ct(a: &[u8], b: &[u8]) -> bool {
    if a.len() != b.len() {
        return false;
    }
    let mut diff: u8 = 0;
    for (x, y) in a.iter().zip(b.iter()) {
        diff |= x ^ y;
    }
    diff == 0
}

/// Wrapper type for the API key, stored in request extensions.
#[derive(Clone)]
pub struct ApiKey(pub String);
