use crate::{AuthError, Claims};

/// Fine-grained, object-scoped authorization policy.
///
/// Roles (`has_role`) are coarse-grained checks on `Claims`.  Policies go
/// further: they receive the authenticated user *and* the specific resource
/// being acted on, so ownership checks and complex rules are straightforward.
///
/// # Example
///
/// ```rust,ignore
/// pub struct PostPolicy;
///
/// impl Policy<Post> for PostPolicy {
///     async fn can(user: &Claims, action: &str, resource: &Post) -> bool {
///         match action {
///             "update" => user.has_role("admin") || resource.user_id == user.sub.parse().unwrap_or(0),
///             "delete" => user.has_role("admin"),
///             _        => false,
///         }
///     }
/// }
///
/// // In a controller:
/// ctx.authorize::<PostPolicy, Post>("update", &post).await?;
/// ```
pub trait Policy<T> {
    fn can(
        user: &Claims,
        action: &str,
        resource: &T,
    ) -> impl std::future::Future<Output = bool> + Send;
}

/// Error type for authorization failures from [`crate::axum::Ctx::authorize`].
#[derive(Debug)]
pub struct PolicyDenied(pub String);

impl From<PolicyDenied> for AuthError {
    fn from(e: PolicyDenied) -> Self {
        AuthError::Forbidden(e.0)
    }
}
