"""Middleware ASGI PUR: plafon global pe dimensiunea corpului cererii (P1-5).

`BaseHTTPMiddleware` nu poate refuza devreme: pentru a construi un `Request`
reconstituie intregul flux `receive()`, iar FastAPI parseaza deja COMPLET
multipart-ul (spooled pe disc peste prag) INAINTE sa ruleze endpointul — o
verificare per-endpoint (chiar si citire pe chunk-uri) nu repara cauza, doar
simptomul. Un middleware ASGI pur intercepteaza `receive` inaintea oricarui
parser: Content-Length peste limita -> 413 fara sa citeasca nimic din body;
altfel numara cumulativ octetii mesajelor `http.request` si intrerupe la
depasire. Cererile fara body (GET etc.) trec netaxate.

Verificarea per-endpoint existenta (`import_router.py`, `import_parse.MAX_BYTES`)
ramane NEATINSA ca strat 2 — acopera orice cale care ar ocoli acest middleware.
"""

from __future__ import annotations

import json

from starlette.datastructures import Headers

from .. import errors
from ..config import get_settings


class _BodyTooLarge(Exception):
    pass


def _error_body() -> bytes:
    payload = errors.eroare(
        "CERERE_PREA_MARE",
        cauza="Corpul cererii depaseste limita permisa.",
    )
    return json.dumps(payload, ensure_ascii=False).encode("utf-8")


class BodyCapMiddleware:
    """Limiteaza corpul cererii la `settings.max_request_bytes`, pe TOATE rutele.

    Limita se citeste din `get_settings()` LA FIECARE CERERE (nu o data la
    construirea middleware-ului): `app.add_middleware` ruleaza o singura data per
    proces (modulul `app.main` e importat o singura data si in teste), deci un
    `max_bytes` fixat in `__init__` ar ramane inghetat la valoarea de la primul
    import si ar ignora `AUTOPASS_MAX_REQUEST_BYTES` schimbat ulterior (teste).
    """

    def __init__(self, app):
        self.app = app

    async def __call__(self, scope, receive, send):
        if scope["type"] != "http":
            await self.app(scope, receive, send)
            return

        max_bytes = get_settings().max_request_bytes

        content_length = Headers(scope=scope).get("content-length")
        if content_length is not None:
            try:
                declared = int(content_length)
            except ValueError:
                declared = None
            if declared is not None and declared > max_bytes:
                await self._send_413(send)
                return

        total = 0

        async def capped_receive():
            nonlocal total
            message = await receive()
            if message["type"] == "http.request":
                total += len(message.get("body") or b"")
                if total > max_bytes:
                    raise _BodyTooLarge()
            return message

        try:
            await self.app(scope, capped_receive, send)
        except _BodyTooLarge:
            await self._send_413(send)

    @staticmethod
    async def _send_413(send) -> None:
        await send(
            {
                "type": "http.response.start",
                "status": 413,
                "headers": [(b"content-type", b"application/json")],
            }
        )
        await send({"type": "http.response.body", "body": _error_body()})
