"""Role-based authorization (spec admin-auth, PRD §2.2).

One table, consulted by FastAPI dependencies on every admin route. The frontend hides
buttons; this decides.

**This is only half of the answer.** `can()` says whether a role may do a *kind* of thing.
`services/scope.py` says which *records* it may do it to, and a Host is refused by either one
independently. Reading this file alone will tell you a Host may delete guests, which is true
and not the whole truth: only the guests of events they own.
"""

import enum

from app.models.enums import AdminRole


class Action(enum.StrEnum):
    """Every capability in the PRD's permission matrix, named once."""

    VIEW_DASHBOARD = "view_dashboard"
    EXPORT_CSV = "export_csv"
    ADD_EDIT_GUESTS = "add_edit_guests"
    IMPORT_GUESTS = "import_guests"
    DELETE_GUESTS = "delete_guests"
    SEND_MESSAGES = "send_messages"
    EDIT_CONTENT = "edit_content"
    MANAGE_REMINDERS = "manage_reminders"
    CONFIGURE_GATEWAYS = "configure_gateways"
    MANAGE_ADMINS = "manage_admins"
    VIEW_AUDIT_LOG = "view_audit_log"
    GENERATE_QR = "generate_qr"


#: What a Host may never do, whatever they own. Written as the exclusion rather than as the
#: inclusion so that a capability added to `Action` later is denied to a Host by default:
#: a new admin power should have to be granted on purpose, not inherited by being new.
_HOST_DENIED: frozenset[Action] = frozenset(
    {
        # Gateway credentials are system-wide. One host holding them could redirect or read
        # every other host's mail.
        Action.CONFIGURE_GATEWAYS,
        # The roster is how scope itself is assigned. A host who could edit it could grant
        # themselves any event in the system, which would make the scope decorative.
        Action.MANAGE_ADMINS,
        # The audit log spans every event, so it names guests and admins a host cannot see.
        Action.VIEW_AUDIT_LOG,
    }
)

_MATRIX: dict[AdminRole, frozenset[Action]] = {
    AdminRole.SUPER_ADMIN: frozenset(Action),
    # Everything an event owner needs — including deleting guests, which the old Co-host was
    # denied. That denial made sense when one guest list was shared by every admin; a host
    # who owns the event owns the mistake, and cannot reach anyone else's list to make it.
    AdminRole.HOST: frozenset(Action) - _HOST_DENIED,
}


def can(role: AdminRole, action: Action) -> bool:
    return action in _MATRIX.get(role, frozenset())


def actions_for(role: AdminRole) -> frozenset[Action]:
    """What this role may do — used to drive role-aware navigation in the frontend."""
    return _MATRIX.get(role, frozenset())
