"""
Resource Tracking Middleware
Tracks CPU, memory, and I/O usage per request for admin debugging.
"""
import psutil
import time
import threading
import logging
from django.utils.deprecation import MiddlewareMixin
from django.core.cache import cache

logger = logging.getLogger(__name__)


class ResourceTrackingMiddleware(MiddlewareMixin):
    """
    Middleware to track resource usage per request.
    Stores metrics in cache with request ID for retrieval.
    Only tracks for authenticated admin/developer users.
    """
    thread_local = threading.local()
    
    def process_request(self, request):
        """Start tracking resources for this request"""
        # Only track if user is authenticated
        if not hasattr(request, 'user') or not request.user.is_authenticated:
            return None
            
        # Check if user is admin/developer
        if not (hasattr(request.user, 'is_admin_or_developer') and request.user.is_admin_or_developer()):
            return None
        
        # Check if debug tracking is enabled via header (set by frontend)
        debug_enabled = request.headers.get('X-Debug-Resource-Tracking', '').lower() == 'true'
        if not debug_enabled:
            return None
        
        try:
            # Generate request ID
            request_id = f"req_{int(time.time() * 1000000)}"
            request.resource_tracking_id = request_id
            
            # Get initial process stats
            process = psutil.Process()
            initial_cpu_times = process.cpu_times()
            initial_memory = process.memory_info()
            initial_io = process.io_counters() if hasattr(process, 'io_counters') else None
            
            # Store initial state
            self.thread_local.start_time = time.time()
            self.thread_local.initial_cpu_times = initial_cpu_times
            self.thread_local.initial_memory = initial_memory
            self.thread_local.initial_io = initial_io
            self.thread_local.process = process
            self.thread_local.request_id = request_id
            
        except Exception as e:
            logger.warning(f"Error starting resource tracking: {str(e)}")
            # Don't break the request if tracking fails
        
        return None
    
    def process_response(self, request, response):
        """Calculate and store resource usage for this request"""
        if not hasattr(request, 'resource_tracking_id'):
            return response
        
        try:
            # Calculate elapsed time
            elapsed_time = time.time() - self.thread_local.start_time
            
            # Get final process stats
            process = self.thread_local.process
            final_cpu_times = process.cpu_times()
            final_memory = process.memory_info()
            final_io = process.io_counters() if hasattr(process, 'io_counters') else None
            
            # Calculate CPU usage (user + system time)
            cpu_user_time = (final_cpu_times.user - self.thread_local.initial_cpu_times.user) * 1000  # ms
            cpu_system_time = (final_cpu_times.system - self.thread_local.initial_cpu_times.system) * 1000  # ms
            cpu_total_time = cpu_user_time + cpu_system_time
            
            # Calculate memory delta
            memory_delta = final_memory.rss - self.thread_local.initial_memory.rss
            
            # Calculate I/O
            io_read = 0
            io_write = 0
            if final_io and self.thread_local.initial_io:
                io_read = final_io.read_bytes - self.thread_local.initial_io.read_bytes
                io_write = final_io.write_bytes - self.thread_local.initial_io.write_bytes
            
            # Get system-wide stats
            cpu_percent = psutil.cpu_percent(interval=0.1)
            memory = psutil.virtual_memory()
            
            # Store metrics in cache (expire after 5 minutes)
            metrics = {
                'request_id': request.resource_tracking_id,
                'path': request.path,
                'method': request.method,
                'elapsed_time_ms': round(elapsed_time * 1000, 2),
                'cpu': {
                    'user_time_ms': round(cpu_user_time, 2),
                    'system_time_ms': round(cpu_system_time, 2),
                    'total_time_ms': round(cpu_total_time, 2),
                    'system_percent': round(cpu_percent, 2),
                },
                'memory': {
                    'delta_bytes': memory_delta,
                    'delta_mb': round(memory_delta / (1024**2), 2),
                    'final_rss_mb': round(final_memory.rss / (1024**2), 2),
                    'system_used_percent': round(memory.percent, 2),
                },
                'io': {
                    'read_bytes': io_read,
                    'read_mb': round(io_read / (1024**2), 2),
                    'write_bytes': io_write,
                    'write_mb': round(io_write / (1024**2), 2),
                },
                'timestamp': time.time(),
            }
            
            # Store in cache with 5 minute expiry
            cache.set(f"resource_tracking_{request.resource_tracking_id}", metrics, 300)
            
            # Add request ID to response header
            response['X-Resource-Tracking-ID'] = request.resource_tracking_id
            
        except Exception as e:
            logger.warning(f"Error calculating resource tracking: {str(e)}")
            # Don't break the response if tracking fails
        
        return response

