//! Role and permission model.

use std::collections::HashMap;

/// A fine-grained permission (e.g., "read", "write", "delete").
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Permission(pub String);

impl Permission {
    pub fn new(name: impl Into<String>) -> Self {
        Self(name.into())
    }

    pub fn as_str(&self) -> &str {
        &self.0
    }
}

impl std::fmt::Display for Permission {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.0.fmt(f)
    }
}

/// A role that grants a set of permissions.
#[derive(Debug, Clone)]
pub struct Role {
    pub name: String,
    pub permissions: Vec<Permission>,
}

impl Role {
    pub fn new(name: impl Into<String>) -> Self {
        Self {
            name: name.into(),
            permissions: Vec::new(),
        }
    }

    pub fn with_permission(mut self, perm: impl Into<String>) -> Self {
        self.permissions.push(Permission::new(perm));
        self
    }

    pub fn with_permissions(mut self, perms: Vec<impl Into<String>>) -> Self {
        self.permissions.extend(perms.into_iter().map(Permission::new));
        self
    }

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

/// The registry of all roles in the application.
#[derive(Debug, Default)]
pub struct RoleRegistry {
    roles: HashMap<String, Role>,
}

impl RoleRegistry {
    pub fn new() -> Self {
        Self::default()
    }

    /// Register a role.
    pub fn register(&mut self, role: Role) {
        self.roles.insert(role.name.clone(), role);
    }

    /// Get a role by name.
    pub fn get(&self, name: &str) -> Option<&Role> {
        self.roles.get(name)
    }

    /// Check if the given role names grant the given permission.
    pub fn has_permission(&self, role_names: &[String], permission: &str) -> bool {
        role_names.iter().any(|role_name| {
            self.roles
                .get(role_name)
                .map(|r| r.has_permission(permission))
                .unwrap_or(false)
        })
    }

    /// List all registered role names.
    pub fn role_names(&self) -> Vec<&str> {
        self.roles.keys().map(|s| s.as_str()).collect()
    }
}
