from django.template.response import TemplateResponse
from django.core.exceptions import MultipleObjectsReturned

from viewer.models import UserSettings


class UserSettingsMiddleware:
    """Middleware that injects `theme` and `sort` into template response context.

    On each request it looks for a UserSettings instance for the authenticated
    user. The found values (or model defaults) are attached to the request and
    then added to any TemplateResponse's context under the `theme` and `sort`
    keys.
    """

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

    def __call__(self, request):
        # Populate request attributes with user settings or defaults.
        request.theme = None
        request.sort = None

        user = getattr(request, "user", None)
        if user and getattr(user, "is_authenticated", False):
            # Use filter().first() to avoid exceptions if no settings exist.
            try:
                settings_obj = UserSettings.objects.get(user=user)
            except UserSettings.DoesNotExist:
                settings_obj = UserSettings(user=user)
                settings_obj.save()
            except MultipleObjectsReturned:
                settings_obj = UserSettings.objects.filter(user=user).first()
            finally:
                request.theme = settings_obj.theme
                request.sort = settings_obj.sort

        # Fall back to the model defaults when not set from DB.
        if request.theme is None:
            request.theme = UserSettings._meta.get_field("theme").get_default()
        if request.sort is None:
            request.sort = UserSettings._meta.get_field("sort").get_default()

        response = self.get_response(request)
        return response

    def process_template_response(self, request, response):
        """Add `theme` and `sort` to TemplateResponse.context_data.

        This method is only called for responses that implement
        `render()` (TemplateResponse-like). We update or create
        `context_data` so templates can access `theme` and `sort`.
        """
        if not isinstance(response, TemplateResponse):
            return response

        ctx = getattr(response, "context_data", None)
        if ctx is None:
            response.context_data = {"theme": request.theme, "sort": request.sort}
        else:
            # Do not overwrite existing keys if templates or other processors set them
            ctx.setdefault("theme", request.theme)
            ctx.setdefault("sort", request.sort)

        return response
