use axum::{
    extract::{Request, State},
    http::StatusCode,
    middleware::Next,
    response::Response,
};
use crate::config::AppState;
use crate::auth::Claims;

/// Middleware to check if user has permission for a resource
/// 
/// This middleware checks JWT claims (attached by auth middleware)
/// and verifies the user has the required permission level.
/// 
/// # Permission Levels
/// - "read": Can view resource
/// - "write": Can modify resource
/// - "admin": Full control including deletion
pub async fn has_permission(
    State(state): State<AppState>,
    required_permission: String,
    request: Request,
    next: Next,
) -> Result<Response, StatusCode> {
    // Extract user_id from JWT claims (attached by auth middleware)
    let user_id = match request.extensions().get::<Claims>() {
        Some(claims) => claims.sub.clone(),
        None => return Err(StatusCode::UNAUTHORIZED),
    };
    
    // Extract resource_id from URL path
    let resource_id = match extract_resource_id(request.uri().path()) {
        Some(id) => id,
        None => return Err(StatusCode::BAD_REQUEST),
    };
    
    // Check if user has the required permission (either directly or through shares)
    let has_perm = match state.db
        .check_permission(&user_id, &resource_id, &required_permission)
        .await
    {
        Ok(allowed) => allowed,
        Err(_) => return Err(StatusCode::INTERNAL_SERVER_ERROR),
    };
    
    if !has_perm {
        return Err(StatusCode::FORBIDDEN);
    }
    
    Ok(next.run(request).await)
}

/// Extract resource ID from URL path
/// 
/// # Examples
/// - /api/shares/123 -> Some("123")
/// - /api/users/me/profile -> None
fn extract_resource_id(path: &str) -> Option<String> {
    let segments: Vec<&str> = path.split('/').collect();
    
    // Look for ID segment after a resource type
    // e.g., /api/shares/:id
    for (i, segment) in segments.iter().enumerate() {
        if segment == &"shares" || segment == &"permissions" {
            if i + 1 < segments.len() {
                let id = segments[i + 1];
                if !id.is_empty() {
                    return Some(id.to_string());
                }
            }
        }
    }
    
    None
}

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_extract_resource_id() {
        assert_eq!(
            extract_resource_id("/api/shares/123"),
            Some("123".to_string())
        );
        assert_eq!(
            extract_resource_id("/api/shares/abc-123"),
            Some("abc-123".to_string())
        );
        assert_eq!(
            extract_resource_id("/api/users/me"),
            None
        );
    }
}
