# -*- coding: utf-8 -*-
"""
Activities Middleware

This module provides middleware for the activities app to capture
request context and make it available to signal handlers for
activity logging.

Middleware Classes:
    - ActivityLoggingMiddleware: Captures request context
    - SecurityMonitoringMiddleware: Monitors for suspicious activity
    - RequestTrackingMiddleware: Tracks request patterns

Author: AdTlas Development Team
Version: 1.0.0
Last Updated: 2024
"""

import logging
import time
from django.utils.deprecation import MiddlewareMixin
from django.contrib.auth import get_user_model
from django.core.cache import cache
from django.conf import settings
from django.utils import timezone
from django.http import HttpResponseForbidden
from django.shortcuts import render
from django.urls import reverse
from django.contrib.auth.models import AnonymousUser
from ipware import get_client_ip
import json

from .signals import set_current_request, log_security_event, log_suspicious_activity
from apps.core.utils import get_client_ip as core_get_client_ip, get_user_agent

User = get_user_model()
logger = logging.getLogger(__name__)


class ActivityLoggingMiddleware(MiddlewareMixin):
    """
    Middleware to capture request context for activity logging.
    
    This middleware sets the current request in thread-local storage
    so that signal handlers can access request information when
    logging activities.
    """
    
    def process_request(self, request):
        """
        Process incoming request and set it in thread-local storage.
        
        Args:
            request: The HTTP request
            
        Returns:
            None
        """
        # Set request in thread-local storage for signal handlers
        set_current_request(request)
        
        # Add request start time for performance monitoring
        request._activity_start_time = time.time()
        
        return None
    
    def process_response(self, request, response):
        """
        Process response and log request completion.
        
        Args:
            request: The HTTP request
            response: The HTTP response
            
        Returns:
            HttpResponse: The response
        """
        try:
            # Calculate request duration
            if hasattr(request, '_activity_start_time'):
                duration = time.time() - request._activity_start_time
                
                # Log slow requests
                slow_request_threshold = getattr(settings, 'SLOW_REQUEST_THRESHOLD', 5.0)
                if duration > slow_request_threshold:
                    logger.warning(
                        f'Slow request detected: {request.method} {request.path} '
                        f'took {duration:.2f}s for user {getattr(request.user, "email", "anonymous")}'
                    )
            
            # Clear request from thread-local storage
            set_current_request(None)
            
        except Exception as e:
            logger.error(f'Error in ActivityLoggingMiddleware.process_response: {e}')
        
        return response
    
    def process_exception(self, request, exception):
        """
        Process exceptions and log them.
        
        Args:
            request: The HTTP request
            exception: The exception that occurred
            
        Returns:
            None
        """
        try:
            user = getattr(request, 'user', None)
            
            # Log the exception as a security event if it's suspicious
            suspicious_exceptions = [
                'PermissionDenied',
                'SuspiciousOperation',
                'DisallowedHost',
                'ValidationError',
            ]
            
            if exception.__class__.__name__ in suspicious_exceptions:
                log_security_event(
                    event_type='exception',
                    severity='medium',
                    description=f'Suspicious exception: {exception.__class__.__name__}',
                    user=user if user and user.is_authenticated else None,
                    details={
                        'exception_type': exception.__class__.__name__,
                        'exception_message': str(exception),
                        'path': request.path,
                        'method': request.method,
                    },
                    request=request
                )
            
            # Clear request from thread-local storage
            set_current_request(None)
            
        except Exception as e:
            logger.error(f'Error in ActivityLoggingMiddleware.process_exception: {e}')
        
        return None


class SecurityMonitoringMiddleware(MiddlewareMixin):
    """
    Middleware to monitor for suspicious security activities.
    
    This middleware tracks various security-related patterns
    and logs suspicious activities for further investigation.
    """
    
    def __init__(self, get_response):
        """
        Initialize the middleware.
        
        Args:
            get_response: The next middleware or view function
        """
        self.get_response = get_response
        
        # Security monitoring settings
        self.max_requests_per_minute = getattr(settings, 'MAX_REQUESTS_PER_MINUTE', 60)
        self.max_failed_logins_per_hour = getattr(settings, 'MAX_FAILED_LOGINS_PER_HOUR', 5)
        self.suspicious_paths = getattr(settings, 'SUSPICIOUS_PATHS', [
            '/admin/',
            '/api/',
            '/.env',
            '/wp-admin/',
            '/phpmyadmin/',
            '/config/',
            '/backup/',
        ])
        
        super().__init__(get_response)
    
    def process_request(self, request):
        """
        Process incoming request for security monitoring.
        
        Args:
            request: The HTTP request
            
        Returns:
            HttpResponse or None
        """
        try:
            client_ip = core_get_client_ip(request)
            user_agent = get_user_agent(request)
            
            # Check for rate limiting
            if self._check_rate_limit(client_ip, request):
                return self._handle_rate_limit_exceeded(request, client_ip)
            
            # Check for suspicious paths
            if self._check_suspicious_path(request.path, client_ip, request):
                pass  # Already logged in the check function
            
            # Check for suspicious user agents
            if self._check_suspicious_user_agent(user_agent, client_ip, request):
                pass  # Already logged in the check function
            
            # Check for SQL injection attempts
            if self._check_sql_injection(request, client_ip):
                pass  # Already logged in the check function
            
        except Exception as e:
            logger.error(f'Error in SecurityMonitoringMiddleware.process_request: {e}')
        
        return None
    
    def _check_rate_limit(self, client_ip, request):
        """
        Check if the client IP has exceeded rate limits.
        
        Args:
            client_ip (str): Client IP address
            request: The HTTP request
            
        Returns:
            bool: True if rate limit exceeded
        """
        cache_key = f'rate_limit:{client_ip}'
        current_requests = cache.get(cache_key, 0)
        
        if current_requests >= self.max_requests_per_minute:
            log_suspicious_activity(
                description=f'Rate limit exceeded for IP {client_ip}',
                severity='high',
                details={
                    'ip_address': client_ip,
                    'requests_count': current_requests,
                    'limit': self.max_requests_per_minute,
                    'path': request.path,
                    'method': request.method,
                },
                request=request
            )
            return True
        
        # Increment counter
        cache.set(cache_key, current_requests + 1, 60)  # 1 minute timeout
        return False
    
    def _check_suspicious_path(self, path, client_ip, request):
        """
        Check if the requested path is suspicious.
        
        Args:
            path (str): Request path
            client_ip (str): Client IP address
            request: The HTTP request
            
        Returns:
            bool: True if path is suspicious
        """
        for suspicious_path in self.suspicious_paths:
            if suspicious_path in path.lower():
                log_suspicious_activity(
                    description=f'Access to suspicious path: {path}',
                    severity='medium',
                    details={
                        'ip_address': client_ip,
                        'path': path,
                        'method': request.method,
                        'suspicious_pattern': suspicious_path,
                    },
                    request=request
                )
                return True
        
        return False
    
    def _check_suspicious_user_agent(self, user_agent, client_ip, request):
        """
        Check if the user agent is suspicious.
        
        Args:
            user_agent (str): User agent string
            client_ip (str): Client IP address
            request: The HTTP request
            
        Returns:
            bool: True if user agent is suspicious
        """
        suspicious_patterns = [
            'sqlmap',
            'nikto',
            'nmap',
            'masscan',
            'burp',
            'owasp',
            'scanner',
            'bot',
            'crawler',
            'spider',
        ]
        
        user_agent_lower = user_agent.lower()
        
        for pattern in suspicious_patterns:
            if pattern in user_agent_lower:
                log_suspicious_activity(
                    description=f'Suspicious user agent detected: {user_agent}',
                    severity='medium',
                    details={
                        'ip_address': client_ip,
                        'user_agent': user_agent,
                        'path': request.path,
                        'method': request.method,
                        'suspicious_pattern': pattern,
                    },
                    request=request
                )
                return True
        
        return False
    
    def _check_sql_injection(self, request, client_ip):
        """
        Check for potential SQL injection attempts.
        
        Args:
            request: The HTTP request
            client_ip (str): Client IP address
            
        Returns:
            bool: True if SQL injection detected
        """
        sql_patterns = [
            'union select',
            'drop table',
            'insert into',
            'delete from',
            'update set',
            'exec(',
            'execute(',
            'sp_',
            'xp_',
            '--',
            ';--',
            '/*',
            '*/',
            'char(',
            'ascii(',
            'substring(',
            'waitfor delay',
        ]
        
        # Check GET parameters
        query_string = request.META.get('QUERY_STRING', '').lower()
        
        # Check POST data
        post_data = ''
        if request.method == 'POST':
            try:
                post_data = request.body.decode('utf-8', errors='ignore').lower()
            except:
                post_data = ''
        
        combined_data = f'{query_string} {post_data}'
        
        for pattern in sql_patterns:
            if pattern in combined_data:
                log_suspicious_activity(
                    description=f'Potential SQL injection attempt detected',
                    severity='high',
                    details={
                        'ip_address': client_ip,
                        'path': request.path,
                        'method': request.method,
                        'suspicious_pattern': pattern,
                        'query_string': request.META.get('QUERY_STRING', ''),
                    },
                    request=request
                )
                return True
        
        return False
    
    def _handle_rate_limit_exceeded(self, request, client_ip):
        """
        Handle rate limit exceeded scenario.
        
        Args:
            request: The HTTP request
            client_ip (str): Client IP address
            
        Returns:
            HttpResponse: Rate limit response
        """
        # Return 429 Too Many Requests
        from django.http import JsonResponse
        
        if request.path.startswith('/api/'):
            return JsonResponse(
                {'error': 'Rate limit exceeded. Please try again later.'},
                status=429
            )
        else:
            return render(
                request,
                'errors/rate_limit.html',
                {'client_ip': client_ip},
                status=429
            )


class RequestTrackingMiddleware(MiddlewareMixin):
    """
    Middleware to track request patterns and performance.
    
    This middleware collects metrics about requests for
    monitoring and analytics purposes.
    """
    
    def process_request(self, request):
        """
        Process incoming request for tracking.
        
        Args:
            request: The HTTP request
            
        Returns:
            None
        """
        # Add tracking data to request
        request._tracking_data = {
            'start_time': time.time(),
            'path': request.path,
            'method': request.method,
            'user_agent': get_user_agent(request),
            'ip_address': core_get_client_ip(request),
            'user_id': request.user.id if hasattr(request, 'user') and request.user.is_authenticated else None,
        }
        
        return None
    
    def process_response(self, request, response):
        """
        Process response and collect metrics.
        
        Args:
            request: The HTTP request
            response: The HTTP response
            
        Returns:
            HttpResponse: The response
        """
        try:
            if hasattr(request, '_tracking_data'):
                tracking_data = request._tracking_data
                duration = time.time() - tracking_data['start_time']
                
                # Store metrics in cache for monitoring
                metrics_key = f'request_metrics:{timezone.now().strftime("%Y%m%d%H%M")}'
                metrics = cache.get(metrics_key, [])
                
                metrics.append({
                    'timestamp': timezone.now().isoformat(),
                    'path': tracking_data['path'],
                    'method': tracking_data['method'],
                    'status_code': response.status_code,
                    'duration': duration,
                    'user_id': tracking_data['user_id'],
                    'ip_address': tracking_data['ip_address'],
                })
                
                # Keep only last 1000 metrics per minute
                if len(metrics) > 1000:
                    metrics = metrics[-1000:]
                
                cache.set(metrics_key, metrics, 3600)  # 1 hour timeout
                
                # Log performance issues
                if duration > 2.0:  # Slow request threshold
                    logger.warning(
                        f'Slow request: {tracking_data["method"]} {tracking_data["path"]} '
                        f'took {duration:.2f}s (status: {response.status_code})'
                    )
                
                # Log error responses
                if response.status_code >= 400:
                    logger.info(
                        f'Error response: {tracking_data["method"]} {tracking_data["path"]} '
                        f'returned {response.status_code} in {duration:.2f}s'
                    )
        
        except Exception as e:
            logger.error(f'Error in RequestTrackingMiddleware.process_response: {e}')
        
        return response


class IPWhitelistMiddleware(MiddlewareMixin):
    """
    Middleware to restrict access based on IP whitelist.
    
    This middleware can be used to restrict admin access
    or API access to specific IP addresses.
    """
    
    def __init__(self, get_response):
        """
        Initialize the middleware.
        
        Args:
            get_response: The next middleware or view function
        """
        self.get_response = get_response
        
        # Get whitelist settings
        self.admin_whitelist = getattr(settings, 'ADMIN_IP_WHITELIST', [])
        self.api_whitelist = getattr(settings, 'API_IP_WHITELIST', [])
        self.whitelist_enabled = getattr(settings, 'IP_WHITELIST_ENABLED', False)
        
        super().__init__(get_response)
    
    def process_request(self, request):
        """
        Process incoming request for IP whitelist check.
        
        Args:
            request: The HTTP request
            
        Returns:
            HttpResponse or None
        """
        if not self.whitelist_enabled:
            return None
        
        client_ip = core_get_client_ip(request)
        path = request.path
        
        # Check admin whitelist
        if path.startswith('/admin/') and self.admin_whitelist:
            if client_ip not in self.admin_whitelist:
                log_security_event(
                    event_type='ip_blocked',
                    severity='high',
                    description=f'Admin access blocked for IP {client_ip}',
                    details={
                        'ip_address': client_ip,
                        'path': path,
                        'whitelist_type': 'admin',
                    },
                    request=request
                )
                return HttpResponseForbidden('Access denied: IP not whitelisted for admin access')
        
        # Check API whitelist
        if path.startswith('/api/') and self.api_whitelist:
            if client_ip not in self.api_whitelist:
                log_security_event(
                    event_type='ip_blocked',
                    severity='medium',
                    description=f'API access blocked for IP {client_ip}',
                    details={
                        'ip_address': client_ip,
                        'path': path,
                        'whitelist_type': 'api',
                    },
                    request=request
                )
                from django.http import JsonResponse
                return JsonResponse(
                    {'error': 'Access denied: IP not whitelisted for API access'},
                    status=403
                )
        
        return None