from rest_framework.permissions import BasePermission

from .models import Capture, Page, Space


class IsOwner(BasePermission):
    """
    The single ownership rule: a row belongs to the user who owns the space
    that owns the page that owns the row.

    THIS IS THE BACKSTOP, NOT THE MECHANISM. Object permissions only run on
    detail routes, so a list endpoint that filters by `?page=` and forgets the
    user filter is an IDOR that returns somebody else's rows for a guessed id.
    The queryset in `PageScopedViewSet` is what actually enforces this, and it
    is written once so no view can forget it.

    The walk up to a user is by TYPE rather than by a `user` attribute, because
    a feature row reaches one through two hops — and a row that answered
    `obj.user` would be a row carrying a second copy of the answer that its
    page already holds.

    **`Capture` IS THE EXCEPTION, AND IT IS A REAL ONE.** Phase 11's inbox is
    not scoped by a page at all: there is exactly one per user, deliberately,
    because asking "which Space?" while a thought is still warm is the friction
    the feature exists to remove. So it carries its own `user` and is checked
    directly. The `user` branch below is written to match `Capture` and
    `Space` — two models that genuinely own their user — rather than as a
    general `getattr(obj, 'user', ...)` fallback, which would silently start
    accepting any future model that happened to grow the attribute.
    """

    def has_object_permission(self, request, view, obj):
        return self._owner(obj) == request.user

    @staticmethod
    def _owner(obj):
        if isinstance(obj, (Capture, Space)):
            return obj.user
        if isinstance(obj, Page):
            return obj.space.user
        page = getattr(obj, 'page', None)
        if page is not None:
            return page.space.user
        # An object this cannot place is not an object it can clear. Returning
        # None denies, because `request.user` is authenticated by the time this
        # runs and can never equal it.
        return None
