//! Role-Based Access Control (RBAC) Module
//!
//! Provides mode-based RBAC with different execution modes:
//! - Solo: Single user, admin by default
//! - MultiUser: Multiple users with role-based access
//! - CICD: Service accounts for automation
//! - Enterprise: Full RBAC with audit logging

pub mod roles;
pub mod permissions;
pub mod middleware;
pub mod policy;

pub use roles::*;
pub use permissions::*;
pub use middleware::*;
pub use policy::*;

use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
use uuid::Uuid;

/// Execution mode for the system
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ExecutionMode {
    /// Single user mode - admin by default
    Solo,
    /// Multi-user mode - role-based access
    MultiUser,
    /// CI/CD mode - service account automation
    CICD,
    /// Enterprise mode - full RBAC and audit
    Enterprise,
}

impl Default for ExecutionMode {
    fn default() -> Self {
        ExecutionMode::Solo
    }
}

impl ExecutionMode {
    /// Get default roles for this mode
    pub fn default_roles(&self) -> Vec<(String, Role)> {
        match self {
            ExecutionMode::Solo => vec![
                ("default".to_string(), Role::Admin),
            ],
            ExecutionMode::MultiUser => vec![
                ("admin".to_string(), Role::Admin),
                ("operator".to_string(), Role::Operator),
                ("developer".to_string(), Role::Developer),
                ("viewer".to_string(), Role::Viewer),
            ],
            ExecutionMode::CICD => vec![
                ("admin".to_string(), Role::Admin),
                ("service".to_string(), Role::ServiceAccount),
            ],
            ExecutionMode::Enterprise => vec![
                ("admin".to_string(), Role::Admin),
                ("operator".to_string(), Role::Operator),
                ("developer".to_string(), Role::Developer),
                ("viewer".to_string(), Role::Viewer),
                ("auditor".to_string(), Role::Auditor),
            ],
        }
    }

    /// Check if audit logging is required for this mode
    pub fn requires_audit(&self) -> bool {
        matches!(self, ExecutionMode::Enterprise | ExecutionMode::CICD)
    }

    /// Check if multi-user features are enabled
    pub fn is_multi_user(&self) -> bool {
        !matches!(self, ExecutionMode::Solo)
    }
}

/// RBAC Configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RbacConfig {
    pub mode: ExecutionMode,
    pub enabled: bool,
    pub default_role: Role,
    pub admin_users: Vec<String>,
    pub allow_mode_switch: bool,
    pub session_timeout_minutes: u32,
}

impl Default for RbacConfig {
    fn default() -> Self {
        Self {
            mode: ExecutionMode::Solo,
            enabled: true,
            default_role: Role::Viewer,
            admin_users: vec!["admin".to_string()],
            allow_mode_switch: true,
            session_timeout_minutes: 60,
        }
    }
}

/// RBAC Manager
pub struct RbacManager {
    config: Arc<RwLock<RbacConfig>>,
    user_roles: Arc<RwLock<HashMap<String, Role>>>,
    policy_engine: Arc<PolicyEngine>,
}

impl RbacManager {
    /// Create a new RBAC manager
    pub fn new(config: RbacConfig) -> Self {
        let mode = config.mode;
        let mut user_roles = HashMap::new();

        // Initialize default roles based on mode
        for (user, role) in mode.default_roles() {
            user_roles.insert(user, role);
        }

        Self {
            config: Arc::new(RwLock::new(config)),
            user_roles: Arc::new(RwLock::new(user_roles)),
            policy_engine: Arc::new(PolicyEngine::new(mode)),
        }
    }

    /// Get current execution mode
    pub async fn get_mode(&self) -> ExecutionMode {
        self.config.read().await.mode
    }

    /// Switch execution mode
    pub async fn switch_mode(&self, new_mode: ExecutionMode) -> Result<(), RbacError> {
        let mut config = self.config.write().await;

        if !config.allow_mode_switch {
            return Err(RbacError::ModeSwitchDisabled);
        }

        config.mode = new_mode;

        // Reinitialize roles for new mode
        let mut user_roles = self.user_roles.write().await;
        user_roles.clear();
        for (user, role) in new_mode.default_roles() {
            user_roles.insert(user, role);
        }

        Ok(())
    }

    /// Check if a user has permission for a resource/action
    pub async fn check_permission(
        &self,
        user: &User,
        resource: Resource,
        action: Action,
    ) -> bool {
        // In Solo mode, always allow
        let mode = self.get_mode().await;
        if mode == ExecutionMode::Solo {
            return true;
        }

        // Get user's role
        let user_roles = self.user_roles.read().await;
        let role = user_roles.get(&user.username)
            .unwrap_or(&self.config.read().await.default_role);

        // Check permission via policy engine
        self.policy_engine.check_permission(*role, resource, action).await
    }

    /// Get all permissions for a user
    pub async fn get_user_permissions(&self, user: &User) -> Vec<Permission> {
        let mode = self.get_mode().await;

        // In Solo mode, return all permissions
        if mode == ExecutionMode::Solo {
            return Permission::all_permissions();
        }

        let user_roles = self.user_roles.read().await;
        let role = user_roles.get(&user.username)
            .unwrap_or(&self.config.read().await.default_role);

        self.policy_engine.get_role_permissions(*role).await
    }

    /// Assign a role to a user
    pub async fn assign_role(&self, user_id: &str, role: Role) -> Result<(), RbacError> {
        let mode = self.get_mode().await;

        // Cannot assign roles in Solo mode
        if mode == ExecutionMode::Solo {
            return Err(RbacError::RoleAssignmentNotAllowed);
        }

        let mut user_roles = self.user_roles.write().await;
        user_roles.insert(user_id.to_string(), role);

        Ok(())
    }

    /// Get role for a user
    pub async fn get_user_role(&self, user_id: &str) -> Role {
        let user_roles = self.user_roles.read().await;
        user_roles.get(user_id)
            .copied()
            .unwrap_or(self.config.read().await.default_role)
    }

    /// List all users and their roles
    pub async fn list_user_roles(&self) -> HashMap<String, Role> {
        self.user_roles.read().await.clone()
    }
}

/// User information for RBAC
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct User {
    pub id: String,
    pub username: String,
    pub email: Option<String>,
    pub created_at: chrono::DateTime<chrono::Utc>,
}

/// RBAC errors
#[derive(Debug, thiserror::Error)]
pub enum RbacError {
    #[error("Mode switching is disabled")]
    ModeSwitchDisabled,

    #[error("Role assignment not allowed in current mode")]
    RoleAssignmentNotAllowed,

    #[error("Permission denied")]
    PermissionDenied,

    #[error("Invalid role")]
    InvalidRole,

    #[error("User not found: {0}")]
    UserNotFound(String),
}

#[cfg(test)]
mod tests {
    use super::*;

    #[tokio::test]
    async fn test_solo_mode_permissions() {
        let config = RbacConfig {
            mode: ExecutionMode::Solo,
            ..Default::default()
        };
        let rbac = RbacManager::new(config);

        let user = User {
            id: "test".to_string(),
            username: "test".to_string(),
            email: None,
            created_at: chrono::Utc::now(),
        };

        // Solo mode should allow all permissions
        assert!(rbac.check_permission(&user, Resource::Server, Action::Create).await);
        assert!(rbac.check_permission(&user, Resource::Taskserv, Action::Delete).await);
    }

    #[tokio::test]
    async fn test_mode_switching() {
        let config = RbacConfig::default();
        let rbac = RbacManager::new(config);

        assert_eq!(rbac.get_mode().await, ExecutionMode::Solo);

        rbac.switch_mode(ExecutionMode::MultiUser).await.unwrap();
        assert_eq!(rbac.get_mode().await, ExecutionMode::MultiUser);
    }
}
