//! Auth context — the current authenticated user and their roles/permissions.

/// The authenticated user.
#[derive(Debug, Clone)]
pub struct AuthUser {
    pub id: String,
    pub email: String,
    pub name: String,
}

impl AuthUser {
    pub fn new(
        id: impl Into<String>,
        email: impl Into<String>,
        name: impl Into<String>,
    ) -> Self {
        Self {
            id: id.into(),
            email: email.into(),
            name: name.into(),
        }
    }
}

/// The auth context — populated by `AuthMiddleware` and available to all
/// components and services downstream in the request.
///
/// Passed as `ctx.metadata["user_id"]`, `ctx.metadata["roles"]` in the AOP
/// layer (see `el-aop`).
#[derive(Debug, Clone)]
pub struct AuthContext {
    pub user: Option<AuthUser>,
    pub roles: Vec<String>,
    pub permissions: Vec<String>,
    /// The raw token/session ID that was verified.
    pub token: String,
}

impl AuthContext {
    pub fn anonymous() -> Self {
        Self {
            user: None,
            roles: Vec::new(),
            permissions: Vec::new(),
            token: String::new(),
        }
    }

    pub fn authenticated(user: AuthUser, roles: Vec<String>, token: impl Into<String>) -> Self {
        Self {
            user: Some(user),
            roles,
            permissions: Vec::new(),
            token: token.into(),
        }
    }

    pub fn with_permissions(mut self, perms: Vec<String>) -> Self {
        self.permissions = perms;
        self
    }

    pub fn is_authenticated(&self) -> bool {
        self.user.is_some()
    }

    pub fn has_role(&self, role: &str) -> bool {
        self.roles.iter().any(|r| r == role)
    }

    pub fn has_permission(&self, permission: &str) -> bool {
        self.permissions.iter().any(|p| p == permission)
    }

    pub fn user_id(&self) -> Option<&str> {
        self.user.as_ref().map(|u| u.id.as_str())
    }
}
