"""
Role-Based Access Control (RBAC) - Feature 6

Manages roles and permissions for fine-grained access control.
"""

from typing import List, Optional
from sqlalchemy.orm import Session

from .models import User, Role


class RBACManager:
    """
    Role-Based Access Control manager - Feature 6
    
    Manages roles and user-role associations for access control.
    
    Example:
        >>> rbac = RBACManager()
        >>> 
        >>> # Create roles
        >>> admin_role = rbac.create_role("admin", "Administrator", db)
        >>> user_role = rbac.create_role("user", "Regular user", db)
        >>> 
        >>> # Assign role to user
        >>> rbac.assign_role(user, "admin", db)
        >>> 
        >>> # Check permissions
        >>> if rbac.has_role(user, "admin"):
        ...     print("User is admin")
    """
    
    def create_role(
        self,
        name: str,
        description: Optional[str] = None,
        db: Session = None
    ) -> Role:
        """
        Create a new role
        
        Args:
            name: Role name (must be unique)
            description: Optional role description
            db: Database session
        
        Returns:
            Created Role object
        
        Raises:
            ValueError: If role with this name already exists
        """
        existing = db.query(Role).filter_by(name=name).first()
        if existing:
            raise ValueError(f"Role '{name}' already exists")
        
        role = Role(name=name, description=description)
        db.add(role)
        db.commit()
        db.refresh(role)
        
        return role
    
    def get_role(self, name: str, db: Session) -> Optional[Role]:
        """Get role by name"""
        return db.query(Role).filter_by(name=name).first()
    
    def get_all_roles(self, db: Session) -> List[Role]:
        """Get all roles"""
        return db.query(Role).all()
    
    def assign_role(self, user: User, role_name: str, db: Session) -> bool:
        """
        Assign role to user
        
        Args:
            user: User object
            role_name: Name of role to assign
            db: Database session
        
        Returns:
            True if role was assigned, False if user already has role
        
        Raises:
            ValueError: If role doesn't exist
        """
        role = self.get_role(role_name, db)
        if not role:
            raise ValueError(f"Role '{role_name}' does not exist")
        
        if role in user.roles:
            return False  # Already has role
        
        user.roles.append(role)
        db.commit()
        return True
    
    def remove_role(self, user: User, role_name: str, db: Session) -> bool:
        """
        Remove role from user
        
        Args:
            user: User object
            role_name: Name of role to remove
            db: Database session
        
        Returns:
            True if role was removed, False if user didn't have role
        """
        role = self.get_role(role_name, db)
        if not role or role not in user.roles:
            return False
        
        user.roles.remove(role)
        db.commit()
        return True
    
    def has_role(self, user: User, role_name: str) -> bool:
        """
        Check if user has specific role
        
        Args:
            user: User object
            role_name: Role name to check
        
        Returns:
            True if user has the role
        """
        return user.has_role(role_name)
    
    def has_any_role(self, user: User, role_names: List[str]) -> bool:
        """
        Check if user has any of the specified roles
        
        Args:
            user: User object
            role_names: List of role names to check
        
        Returns:
            True if user has at least one of the roles
        """
        return user.has_any_role(role_names)
    
    def get_user_roles(self, user: User) -> List[str]:
        """Get list of role names for user"""
        return [role.name for role in user.roles]
    
    def delete_role(self, role_name: str, db: Session) -> bool:
        """
        Delete a role
        
        Args:
            role_name: Name of role to delete
            db: Database session
        
        Returns:
            True if role was deleted, False if not found
        
        Note:
            This will remove the role from all users
        """
        role = self.get_role(role_name, db)
        if not role:
            return False
        
        db.delete(role)
        db.commit()
        return True
