"""JWT auth for WebSocket connections.

Browsers can't set an Authorization header on a WebSocket handshake, so the
client passes its access token as `?token=...` on the ws:// URL. This
middleware validates it with the same rules as the HTTP API (signature,
expiry, active account, token_version) and puts the resolved user + role
into the consumer's scope. Connections without a valid token still get a
scope (user=None) - each consumer decides whether anonymous is allowed.
"""
from urllib.parse import parse_qs

from channels.db import database_sync_to_async


@database_sync_to_async
def _resolve(token_raw):
    from rest_framework_simplejwt.exceptions import TokenError
    from rest_framework_simplejwt.tokens import AccessToken

    from testing_app.models import User

    try:
        token = AccessToken(token_raw)
    except TokenError:
        return None, None
    user = User.objects.select_related('role').filter(id=token.get('user_id')).first()
    if user is None or user.status != 'ACTIVE' or token.get('tv') != user.token_version:
        return None, None
    return user, token.get('role')


class JWTAuthMiddleware:
    def __init__(self, inner):
        self.inner = inner

    async def __call__(self, scope, receive, send):
        qs = parse_qs(scope.get('query_string', b'').decode())
        token_raw = (qs.get('token') or [''])[0]
        user, role = (None, None)
        if token_raw:
            user, role = await _resolve(token_raw)
        scope['jwt_user'] = user
        scope['jwt_role'] = role
        return await self.inner(scope, receive, send)
