"""
The minimal identity contract the dispatch core reads.

Auth-guard evaluation and per-user cache scoping need exactly these four
attributes — nothing about how the identity was established. Django's session
`User`, `JWTUser`, `MWTUser`, and any token-user an adapter constructs all
satisfy this structurally; no inheritance required.

`get_all_permissions()` (Django ORM) is deliberately NOT here — the MWT
permission-key is a Django-side concern, and adding it would force every
adapter to implement a Django-shaped method.
"""

from __future__ import annotations

from typing import Protocol, runtime_checkable


@runtime_checkable
class Identity(Protocol):
    is_authenticated: bool
    is_staff: bool
    is_superuser: bool

    @property
    def pk(self) -> object | None: ...  # str | int | None; cache scoping stringifies it


def user_id_of(identity: Identity | None) -> str | None:
    """The cache-scoping user id — `str(pk)`, or None for anonymous/no-pk."""
    pk = getattr(identity, "pk", None)
    return str(pk) if pk is not None else None
