"""
Audit middleware for automatic logging of user actions and security monitoring
"""
from django.utils.deprecation import MiddlewareMixin
from django.utils import timezone
from django.contrib.auth import get_user_model
from django.urls import resolve
from django.http import JsonResponse
import time
import logging
from .audit_service import audit_service
from .audit_models import AuditEventType, SecurityEventSeverity

User = get_user_model()
logger = logging.getLogger(__name__)


class AuditMiddleware(MiddlewareMixin):
    """
    Middleware to automatically log user actions and monitor security events
    """
    
    def __init__(self, get_response):
        self.get_response = get_response
        super().__init__(get_response)
        
        # URLs to exclude from logging (to avoid noise)
        self.excluded_paths = [
            '/static/',
            '/media/',
            '/favicon.ico',
            '/health/',
            '/ping/',
        ]
        
        # Sensitive URLs that require special attention
        self.sensitive_paths = [
            '/admin/',
            '/users/permissions/',
            '/users/roles/',
            '/reports/export/',
            '/api/',
        ]
    
    def process_request(self, request):
        """Process incoming request"""
        # Store request start time for performance tracking
        request._audit_start_time = time.time()
        
        # Skip excluded paths
        if any(request.path.startswith(path) for path in self.excluded_paths):
            return None
        
        # Log request start for sensitive paths
        if any(request.path.startswith(path) for path in self.sensitive_paths):
            if request.user.is_authenticated:
                audit_service.log_user_action(
                    user=request.user,
                    action='access_sensitive_path',
                    module='security',
                    request=request,
                    description=f"Accessing sensitive path: {request.path}"
                )
        
        return None
    
    def process_response(self, request, response):
        """Process response and log completed actions"""
        # Skip excluded paths
        if any(request.path.startswith(path) for path in self.excluded_paths):
            return response
        
        # Calculate response time
        response_time = None
        if hasattr(request, '_audit_start_time'):
            response_time = (time.time() - request._audit_start_time) * 1000  # Convert to milliseconds
        
        # Only log for authenticated users
        if not request.user.is_authenticated:
            return response
        
        try:
            # Resolve URL to get view information
            resolved = resolve(request.path)
            view_name = resolved.view_name or 'unknown'
            module = self._extract_module_from_path(request.path)
            
            # Determine action based on HTTP method and path
            action = self._determine_action(request.method, request.path, view_name)
            
            # Log the action
            audit_service.log_user_action(
                user=request.user,
                action=action,
                module=module,
                request=request,
                description=f"{request.method} {request.path}",
                view_name=view_name,
                response_time=response_time,
                status_code=response.status_code
            )
            
            # Check for security events based on response
            self._check_response_security_events(request, response)
            
        except Exception as e:
            logger.error(f"Audit middleware error: {e}")
        
        return response
    
    def process_exception(self, request, exception):
        """Process exceptions and log security events"""
        if not request.user.is_authenticated:
            return None
        
        try:
            # Log the exception as a security event if it's suspicious
            if self._is_suspicious_exception(exception):
                audit_service.log_security_event(
                    user=request.user,
                    action='suspicious_exception',
                    severity=SecurityEventSeverity.MEDIUM,
                    description=f"Suspicious exception occurred: {str(exception)}",
                    request=request,
                    exception_type=type(exception).__name__,
                    exception_message=str(exception)
                )
        
        except Exception as e:
            logger.error(f"Error logging exception in audit middleware: {e}")
        
        return None
    
    def _extract_module_from_path(self, path):
        """Extract module name from URL path"""
        path_parts = path.strip('/').split('/')
        if path_parts:
            return path_parts[0] or 'root'
        return 'unknown'
    
    def _determine_action(self, method, path, view_name):
        """Determine action based on HTTP method and path"""
        method_lower = method.lower()
        path_lower = path.lower()
        
        # Map HTTP methods to actions
        method_actions = {
            'get': 'view',
            'post': 'create',
            'put': 'update',
            'patch': 'update',
            'delete': 'delete'
        }
        
        base_action = method_actions.get(method_lower, method_lower)
        
        # Enhance action based on path patterns
        if 'login' in path_lower:
            return 'login'
        elif 'logout' in path_lower:
            return 'logout'
        elif 'export' in path_lower or 'download' in path_lower:
            return 'export_data'
        elif 'report' in path_lower:
            return 'generate_report'
        elif 'permission' in path_lower:
            return f'{base_action}_permission'
        elif 'user' in path_lower:
            return f'{base_action}_user'
        elif 'client' in path_lower:
            return f'{base_action}_client'
        elif 'loan' in path_lower:
            return f'{base_action}_loan'
        else:
            return f'{base_action}_{view_name.replace(":", "_")}'
    
    def _check_response_security_events(self, request, response):
        """Check response for security events"""
        try:
            # Check for unauthorized access attempts (403, 401)
            if response.status_code in [401, 403]:
                audit_service.log_security_event(
                    user=request.user,
                    action='unauthorized_access_attempt',
                    severity=SecurityEventSeverity.MEDIUM,
                    description=f"Unauthorized access attempt to {request.path}",
                    request=request,
                    status_code=response.status_code
                )
            
            # Check for server errors that might indicate attacks
            elif response.status_code >= 500:
                audit_service.log_security_event(
                    user=request.user,
                    action='server_error',
                    severity=SecurityEventSeverity.LOW,
                    description=f"Server error occurred for {request.path}",
                    request=request,
                    status_code=response.status_code
                )
            
            # Check for suspicious response patterns
            elif response.status_code == 404 and self._is_suspicious_404(request):
                audit_service.log_security_event(
                    user=request.user,
                    action='suspicious_path_access',
                    severity=SecurityEventSeverity.LOW,
                    description=f"Access to suspicious path: {request.path}",
                    request=request,
                    status_code=response.status_code
                )
        
        except Exception as e:
            logger.error(f"Error checking response security events: {e}")
    
    def _is_suspicious_exception(self, exception):
        """Check if exception is suspicious"""
        suspicious_exceptions = [
            'PermissionDenied',
            'SuspiciousOperation',
            'ValidationError',
            'IntegrityError'
        ]
        
        return type(exception).__name__ in suspicious_exceptions
    
    def _is_suspicious_404(self, request):
        """Check if 404 is suspicious (potential probing)"""
        suspicious_patterns = [
            'admin',
            'wp-admin',
            'phpmyadmin',
            '.env',
            'config',
            'backup',
            'test',
            'debug'
        ]
        
        path_lower = request.path.lower()
        return any(pattern in path_lower for pattern in suspicious_patterns)


class PermissionAuditMiddleware(MiddlewareMixin):
    """
    Specialized middleware for permission checking audit
    """
    
    def __init__(self, get_response):
        self.get_response = get_response
        super().__init__(get_response)
    
    def process_view(self, request, view_func, view_args, view_kwargs):
        """Process view and check permissions"""
        if not request.user.is_authenticated:
            return None
        
        try:
            # Store original user method for permission checking
            if hasattr(request.user, 'has_perm'):
                original_has_perm = request.user.has_perm
                
                def audited_has_perm(perm, obj=None):
                    """Wrapper for has_perm that logs permission checks"""
                    result = original_has_perm(perm, obj)
                    
                    # Log the permission check
                    audit_service.log_permission_check(
                        user=request.user,
                        permission=perm,
                        granted=result,
                        request=request,
                        object_type=type(obj).__name__ if obj else None,
                        object_id=getattr(obj, 'id', None) if obj else None
                    )
                    
                    return result
                
                # Replace the method temporarily
                request.user.has_perm = audited_has_perm
        
        except Exception as e:
            logger.error(f"Error in permission audit middleware: {e}")
        
        return None


class DataAccessAuditMixin:
    """
    Mixin for Django models to automatically log data access
    """
    
    def save(self, *args, **kwargs):
        """Override save to log data modifications"""
        is_new = self.pk is None
        action = 'create' if is_new else 'update'
        
        # Get current user from thread local storage if available
        user = getattr(self, '_current_user', None)
        
        if user:
            audit_service.log_data_access(
                user=user,
                model_class=self.__class__,
                object_id=self.pk,
                action=action,
                description=f"{action.title()} {self.__class__.__name__} record"
            )
        
        return super().save(*args, **kwargs)
    
    def delete(self, *args, **kwargs):
        """Override delete to log data deletions"""
        user = getattr(self, '_current_user', None)
        
        if user:
            audit_service.log_data_access(
                user=user,
                model_class=self.__class__,
                object_id=self.pk,
                action='delete',
                description=f"Delete {self.__class__.__name__} record"
            )
        
        return super().delete(*args, **kwargs)


class ThreadLocalUserMiddleware(MiddlewareMixin):
    """
    Middleware to store current user in thread local storage for audit logging
    """
    
    def __init__(self, get_response):
        self.get_response = get_response
        super().__init__(get_response)
    
    def process_request(self, request):
        """Store user in thread local storage"""
        if hasattr(request, 'user') and request.user.is_authenticated:
            # Store user in a way that can be accessed by models
            import threading
            if not hasattr(threading.current_thread(), 'audit_user'):
                threading.current_thread().audit_user = request.user
        
        return None
    
    def process_response(self, request, response):
        """Clean up thread local storage"""
        import threading
        if hasattr(threading.current_thread(), 'audit_user'):
            delattr(threading.current_thread(), 'audit_user')
        
        return response