from fastapi import Request, HTTPException, status
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.responses import JSONResponse
from ...shared.config.logging_config import get_logger
from ...shared.config.settings import settings

logger = get_logger(__name__)


class RequestSizeLimitMiddleware(BaseHTTPMiddleware):
    """
    Middleware to enforce maximum request body size limits.
    
    Prevents DoS attacks by rejecting requests that exceed the configured
    maximum body size before they are processed.
    """
    
    def __init__(self, app, max_size: int = None):
        super().__init__(app)
        self.max_size = max_size or settings.MAX_REQUEST_BODY_SIZE
    
    async def dispatch(self, request: Request, call_next):
        # Skip size check for methods that don't have bodies
        if request.method in ['GET', 'HEAD', 'OPTIONS', 'DELETE']:
            return await call_next(request)
        
        # Check Content-Length header if available
        content_length = request.headers.get('content-length')
        if content_length:
            try:
                size = int(content_length)
                if size > self.max_size:
                    logger.warning(
                        f"Request body size {size} bytes exceeds maximum {self.max_size} bytes "
                        f"from {request.client.host if request.client else 'unknown'}"
                    )
                    return JSONResponse(
                        status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE,
                        content={
                            'status': 'error',
                            'message': f'Request body too large. Maximum size: {self.max_size // 1024 // 1024}MB'
                        }
                    )
            except (ValueError, TypeError):
                # Invalid content-length header, let it pass and let FastAPI handle it
                pass
        
        # For streaming requests without Content-Length, we need to check the body
        # This is handled by limiting the body read size
        response = await call_next(request)
        
        return response

