"""Authentication session middleware for Z8ter.

This middleware inspects incoming requests for the `z8_auth_sid` cookie and, if
present, attempts to resolve it into a user object via the configured session
and user repositories. It attaches the resolved user (or None) to
`request.state.user`, making it accessible to guards and handlers.

Flow:
1. Initialize `request.state.user = None`.
2. Read `sid` (session id) from the `z8_auth_sid` cookie.
3. Look up the session in the configured `SessionRepo`.
4. If valid, fetch the user from `UserRepo`.
5. Attach the user object to `request.state.user`.
6. Call the next middleware/handler in the stack.

Assumptions:
- `request.app.state.session_repo` implements `SessionRepo`.
- `request.app.state.user_repo` implements `UserRepo`.
- Cookie handling (secure flags, expiry) is managed by response helpers.

Security notes:
- Cookies should be set with `HttpOnly`, `Secure`, and `SameSite` flags.
- Invalid/expired session IDs should be handled silently — the user is simply
  treated as anonymous.
- Consider rate-limiting or logging repeated invalid SID attempts.

Performance:
- Every request with a cookie performs two lookups (session + user).
- Apps may introduce caching layers if performance becomes a bottleneck.

Future extensions:
- Support header-based tokens (e.g., for API clients).
- Handle session rotation transparently.
"""

from typing import Any

from starlette.middleware.base import BaseHTTPMiddleware

from z8ter.auth.contracts import SessionRepo, UserRepo


class AuthSessionMiddleware(BaseHTTPMiddleware):
    """Attach authenticated user to request state based on `z8_auth_sid` cookie.

    Attributes:
        session_repo: Provides session validation and user_id resolution.
        user_repo: Provides user lookup by id.

    Request state:
        - Sets `request.state.user` to a user object (dict-like) if session valid.
        - Otherwise, sets `request.state.user = None`.

    Usage:
        app.add_middleware(AuthSessionMiddleware)

    """

    async def dispatch(self, request, call_next) -> Any:
        """Process incoming requests to attach authenticated user to request state.

        This method implements the Starlette `BaseHTTPMiddleware` dispatch
        contract. It inspects the `z8_auth_sid` cookie, validates the session,
        and attaches the resolved user object to `request.state.user`.

        Attributes:
        request : Request
            The incoming HTTP request. Assumes `app.state.session_repo` and
            `app.state.user_repo` have been configured at startup.
        call_next : Callable
            The next middleware or route handler in the stack.

        Returns:
        Any
            The response generated by the next middleware or route handler.

        Notes:
        - Sets `request.state.user = None` by default (anonymous user).
        - If a valid session is found, `request.state.user` will be populated
          with the user object (dict-like) returned by `UserRepo.get_user_by_id`.
        - Invalid, expired, or missing sessions are treated as anonymous.
        - Always calls `call_next(request)` to continue processing.

        Security:
        - The `z8_auth_sid` cookie must be set with `HttpOnly`, `Secure`,
          and `SameSite` flags.
        - Session IDs should be stored hashed at rest by `SessionRepo`.

        """
        request.state.user = None
        sid = request.cookies.get("z8_auth_sid")
        session_repo: SessionRepo = request.app.state.session_repo
        user_repo: UserRepo = request.app.state.user_repo
        if sid:
            user_id = session_repo.get_user_id(sid)
            if user_id:
                request.state.user = user_repo.get_user_by_id(user_id)
        return await call_next(request)
