"""Accounts Permissions

This module contains custom permission classes for the accounts app.
These permissions control access to user-related views and API endpoints.
"""

from rest_framework import permissions
from django.core.exceptions import PermissionDenied
from django.utils.translation import gettext_lazy as _
from django.contrib.auth.mixins import UserPassesTestMixin

from apps.accounts.models import User, UserProfile, UserSession


class IsOwnerOrReadOnly(permissions.BasePermission):
    """Permission that allows owners to edit their own objects.
    
    Read permissions are allowed for any request,
    but write permissions are only allowed to the owner of the object.
    """
    
    def has_object_permission(self, request, view, obj):
        # Read permissions for any request
        if request.method in permissions.SAFE_METHODS:
            return True
        
        # Write permissions only to the owner
        if hasattr(obj, 'user'):
            return obj.user == request.user
        elif hasattr(obj, 'owner'):
            return obj.owner == request.user
        else:
            return obj == request.user


class IsOwnerOnly(permissions.BasePermission):
    """Permission that only allows owners to access their own objects."""
    
    def has_object_permission(self, request, view, obj):
        # All permissions only to the owner
        if hasattr(obj, 'user'):
            return obj.user == request.user
        elif hasattr(obj, 'owner'):
            return obj.owner == request.user
        else:
            return obj == request.user


class IsProfileOwner(permissions.BasePermission):
    """Permission for user profile access."""
    
    def has_object_permission(self, request, view, obj):
        # Allow users to access their own profile
        if isinstance(obj, (User, UserProfile)):
            if isinstance(obj, UserProfile):
                return obj.user == request.user
            else:
                return obj == request.user
        return False


class IsSessionOwner(permissions.BasePermission):
    """Permission for user session access."""
    
    def has_object_permission(self, request, view, obj):
        # Allow users to access their own sessions
        if isinstance(obj, UserSession):
            return obj.user == request.user
        return False


class IsVerifiedUser(permissions.BasePermission):
    """Permission that requires user to be verified."""
    
    message = _('You must verify your email address to access this resource.')
    
    def has_permission(self, request, view):
        return (
            request.user and
            request.user.is_authenticated and
            getattr(request.user, 'is_verified', False)
        )


class IsActiveUser(permissions.BasePermission):
    """Permission that requires user to be active."""
    
    message = _('Your account has been deactivated.')
    
    def has_permission(self, request, view):
        return (
            request.user and
            request.user.is_authenticated and
            request.user.is_active
        )


class CanManageUsers(permissions.BasePermission):
    """Permission for user management operations."""
    
    message = _('You do not have permission to manage users.')
    
    def has_permission(self, request, view):
        return (
            request.user and
            request.user.is_authenticated and
            (request.user.is_staff or request.user.is_superuser)
        )


class CanViewUserList(permissions.BasePermission):
    """Permission to view user lists."""
    
    message = _('You do not have permission to view user lists.')
    
    def has_permission(self, request, view):
        return (
            request.user and
            request.user.is_authenticated and
            (request.user.is_staff or 
             request.user.has_perm('accounts.view_user'))
        )


class IsAdminOrOwner(permissions.BasePermission):
    """Permission that allows admin users or owners to access objects."""
    
    def has_object_permission(self, request, view, obj):
        # Admin users can access everything
        if request.user.is_staff or request.user.is_superuser:
            return True
        
        # Owners can access their own objects
        if hasattr(obj, 'user'):
            return obj.user == request.user
        elif hasattr(obj, 'owner'):
            return obj.owner == request.user
        else:
            return obj == request.user


# Django Class-Based View Mixins

class OwnerRequiredMixin(UserPassesTestMixin):
    """Mixin that requires the user to be the owner of the object."""
    
    def test_func(self):
        obj = self.get_object()
        if hasattr(obj, 'user'):
            return obj.user == self.request.user
        elif hasattr(obj, 'owner'):
            return obj.owner == self.request.user
        else:
            return obj == self.request.user
    
    def handle_no_permission(self):
        raise PermissionDenied(_('You do not have permission to access this resource.'))


class VerifiedUserRequiredMixin(UserPassesTestMixin):
    """Mixin that requires the user to be verified."""
    
    def test_func(self):
        return (
            self.request.user.is_authenticated and
            getattr(self.request.user, 'is_verified', False)
        )
    
    def handle_no_permission(self):
        raise PermissionDenied(_('You must verify your email address to access this page.'))


class ActiveUserRequiredMixin(UserPassesTestMixin):
    """Mixin that requires the user to be active."""
    
    def test_func(self):
        return (
            self.request.user.is_authenticated and
            self.request.user.is_active
        )
    
    def handle_no_permission(self):
        raise PermissionDenied(_('Your account has been deactivated.'))


class StaffRequiredMixin(UserPassesTestMixin):
    """Mixin that requires the user to be staff."""
    
    def test_func(self):
        return (
            self.request.user.is_authenticated and
            self.request.user.is_staff
        )
    
    def handle_no_permission(self):
        raise PermissionDenied(_('You must be a staff member to access this page.'))


class SuperuserRequiredMixin(UserPassesTestMixin):
    """Mixin that requires the user to be a superuser."""
    
    def test_func(self):
        return (
            self.request.user.is_authenticated and
            self.request.user.is_superuser
        )
    
    def handle_no_permission(self):
        raise PermissionDenied(_('You must be a superuser to access this page.'))


class ProfileOwnerMixin(UserPassesTestMixin):
    """Mixin for profile-related views that requires ownership."""
    
    def test_func(self):
        # Allow access to own profile or if user is staff
        if self.request.user.is_staff:
            return True
        
        # Check if accessing own profile
        pk = self.kwargs.get('pk')
        if pk:
            try:
                user = User.objects.get(pk=pk)
                return user == self.request.user
            except User.DoesNotExist:
                return False
        
        # If no pk provided, assume accessing own profile
        return True
    
    def handle_no_permission(self):
        raise PermissionDenied(_('You can only access your own profile.'))


# Utility functions for permission checking

def user_can_edit_profile(user, profile_user):
    """Check if user can edit a profile.
    
    Args:
        user: The requesting user
        profile_user: The user whose profile is being edited
        
    Returns:
        Boolean indicating if user can edit the profile
    """
    if not user.is_authenticated:
        return False
    
    # Users can edit their own profile
    if user == profile_user:
        return True
    
    # Staff can edit any profile
    if user.is_staff:
        return True
    
    return False


def user_can_view_profile(user, profile_user):
    """Check if user can view a profile.
    
    Args:
        user: The requesting user
        profile_user: The user whose profile is being viewed
        
    Returns:
        Boolean indicating if user can view the profile
    """
    if not user.is_authenticated:
        return False
    
    # Users can view their own profile
    if user == profile_user:
        return True
    
    # Staff can view any profile
    if user.is_staff:
        return True
    
    # Check if profile is public (if such field exists)
    if hasattr(profile_user, 'profile') and hasattr(profile_user.profile, 'is_public'):
        return profile_user.profile.is_public
    
    return False


def user_can_manage_sessions(user, session_user):
    """Check if user can manage sessions.
    
    Args:
        user: The requesting user
        session_user: The user whose sessions are being managed
        
    Returns:
        Boolean indicating if user can manage sessions
    """
    if not user.is_authenticated:
        return False
    
    # Users can manage their own sessions
    if user == session_user:
        return True
    
    # Staff can manage any sessions
    if user.is_staff:
        return True
    
    return False