//! Role-based access control and permissions

use rustelo_core::{Result, RusteloError};
use std::collections::{HashMap, HashSet};

/// Permission definition
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Permission {
    /// Resource (e.g., "users", "posts")
    pub resource: String,
    /// Action (e.g., "read", "write", "delete")
    pub action: String,
}

/// Role definition
#[derive(Debug, Clone)]
pub struct Role {
    /// Role name
    pub name: String,
    /// Role permissions
    pub permissions: HashSet<Permission>,
}

/// RBAC manager
pub struct RbacManager {
    /// User role assignments
    user_roles: HashMap<String, HashSet<String>>,
    /// Role definitions
    roles: HashMap<String, Role>,
}

impl RbacManager {
    /// Create a new RBAC manager
    pub fn new() -> Self {
        Self {
            user_roles: HashMap::new(),
            roles: HashMap::new(),
        }
    }

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

    /// Assign role to user
    pub fn assign_role(&mut self, user_id: &str, role_name: &str) -> Result<()> {
        if !self.roles.contains_key(role_name) {
            return Err(RusteloError::generic("Role not found"));
        }

        self.user_roles
            .entry(user_id.to_string())
            .or_default()
            .insert(role_name.to_string());

        Ok(())
    }

    /// Check if user has permission
    pub fn has_permission(&self, user_id: &str, permission: &Permission) -> bool {
        if let Some(user_roles) = self.user_roles.get(user_id) {
            for role_name in user_roles {
                if let Some(role) = self.roles.get(role_name) {
                    if role.permissions.contains(permission) {
                        return true;
                    }
                }
            }
        }
        false
    }
}

impl Default for RbacManager {
    fn default() -> Self {
        Self::new()
    }
}

impl Permission {
    /// Create a new permission
    pub fn new(resource: impl Into<String>, action: impl Into<String>) -> Self {
        Self {
            resource: resource.into(),
            action: action.into(),
        }
    }
}
