"""Authentication backend that enforces `User.account_status`.

The user model has carried pending_activation/active/suspended/locked since the
access-lifecycle migration, but nothing read it: authentication only consulted
`is_active`, so suspending an account changed a label and nothing else, and any
session minted before the change stayed usable (SEC-12).

`ModelBackend.user_can_authenticate` is the documented hook for exactly this,
and it runs after the password check, so it does not turn the login endpoint
into an account-status oracle. Session-bearing requests are covered separately
by `apps.core.middleware.AccountStatusMiddleware`.
"""

from django.contrib.auth.backends import ModelBackend

# Statuses that may hold a session. `pending_activation` is included because
# invited-but-not-yet-activated users complete setup through authenticated
# endpoints; only the two punitive states are refused.
ALLOWED_ACCOUNT_STATUSES = frozenset({"pending_activation", "active"})


def account_status_allows_access(user) -> bool:
    # getattr keeps this safe for AnonymousUser and for any auth backend that
    # yields a user-like object without the field.
    return getattr(user, "account_status", "active") in ALLOWED_ACCOUNT_STATUSES


class AccountStatusModelBackend(ModelBackend):
    """ModelBackend that also refuses suspended and locked accounts."""

    def user_can_authenticate(self, user):
        return super().user_can_authenticate(user) and account_status_allows_access(user)
