"""The role vocabulary — one authority for it (plan D4).

Roles are the APP's decision. BW never assigns one; `bw_auth.report_role` only
mirrors the app's decision to the BW hub for display, and grants nothing
anywhere. Real permission checks happen here and are keyed on the role slug.

Adding a role means adding it here and nowhere else: the API exposes these
values, the SPA renders `ROLE_LABELS` via /api/me, and the hub shows the label.
"""

from __future__ import annotations

# Slugs stored in users.role / invites.role. Wire format and DB value.
ROLE_SUPERADMIN = "superadmin"
ROLE_USER = "user"
# Signed in with a valid BW account but NOT invited. The explicit default-deny
# state — stored, not implied, so "no access" is visible in the database.
ROLE_NONE = "none"

# Human-readable labels. Single source for the UI and the BW hub display.
ROLE_LABELS: dict[str, str] = {
    ROLE_SUPERADMIN: "Super Admin",
    ROLE_USER: "User",
    ROLE_NONE: "Not invited",
}

# Roles an invite may grant. ROLE_NONE is deliberately absent — it is a state
# the app assigns, never something an invite hands out.
ASSIGNABLE_ROLES: frozenset[str] = frozenset({ROLE_SUPERADMIN, ROLE_USER})

# Roles that may use the app at all. Everything a signed-in user can reach is
# gated on membership here, so a not-invited account reaches nothing.
ADMITTED_ROLES: frozenset[str] = frozenset({ROLE_SUPERADMIN, ROLE_USER})


def label_for(role: str) -> str:
    return ROLE_LABELS.get(role, ROLE_LABELS[ROLE_NONE])


def is_admitted(role: str) -> bool:
    """Has this account been invited? Default-deny: an unknown role is not."""
    return role in ADMITTED_ROLES


def is_superadmin(role: str) -> bool:
    return role == ROLE_SUPERADMIN
