"""Reusable role groupings for widget enablement predicates.

These mirror the tuples scattered through the codebase (apps/core/views.py,
apps/smehub/_shared/role_gates.py) so widget files don't have to re-derive them.
"""
from __future__ import annotations

from apps.core.permissions.roles import UserRole

ADMIN_ROLES: tuple[str, ...] = (UserRole.SYSTEM_ADMIN, UserRole.ADMIN)

# RIMS — managers / programme staff / finance
GRANTS_MANAGEMENT_ROLES: tuple[str, ...] = (
    UserRole.GRANTS_MANAGER,
    UserRole.PROGRAM_MANAGER,
    UserRole.PROGRAM_DIRECTOR,
)

FINANCE_ROLES: tuple[str, ...] = (
    UserRole.FINANCE_OFFICER,
    UserRole.FINANCE_DIRECTOR,
)

RIMS_MANAGEMENT_ROLES: tuple[str, ...] = (
    *ADMIN_ROLES,
    *GRANTS_MANAGEMENT_ROLES,
    *FINANCE_ROLES,
    UserRole.MEL_OFFICER,
)

# Dashboard-only: MEL officers have their own MEL widgets, so we don't stack
# the (heavy) RIMS-management widget set on top of theirs. Permission checks
# elsewhere still use RIMS_MANAGEMENT_ROLES.
RIMS_DASHBOARD_MANAGER_ROLES: tuple[str, ...] = (
    *ADMIN_ROLES,
    *GRANTS_MANAGEMENT_ROLES,
    *FINANCE_ROLES,
)

RIMS_APPLICANT_ROLES: tuple[str, ...] = (
    UserRole.APPLICANT,
    UserRole.SCHOLAR,
    UserRole.RESEARCHER,
)

REVIEWER_ROLES: tuple[str, ...] = (UserRole.REVIEWER, UserRole.JUDGE)

# REP — learning
LEARNER_ROLES: tuple[str, ...] = (UserRole.LEARNER, UserRole.SCHOLAR)
INSTRUCTOR_ROLES: tuple[str, ...] = (UserRole.INSTRUCTOR, UserRole.REP_LEARNING_ADMIN)

# Repository
RESEARCHER_ROLES: tuple[str, ...] = (UserRole.RESEARCHER, UserRole.SCHOLAR)
# Dashboard-only: scholars already see learner + applicant widgets, so we
# don't stack repository widgets on top — they reach the repo via the menu.
RESEARCHER_DASHBOARD_ROLES: tuple[str, ...] = (UserRole.RESEARCHER,)
REPO_MANAGEMENT_ROLES: tuple[str, ...] = (
    UserRole.REPO_MANAGER,
    UserRole.KHUB_MANAGER,
    UserRole.REPOSITORY_ADMIN,
    UserRole.EDITOR_IN_CHIEF,
    # The Knowledge Management Officer is the SRS "Collection Manager" — the
    # reviewer who receives submissions and records the QA decision. They carry
    # collection-management/QA rights (scoped per collection via
    # Collection.managed_by), so they belong in the management tier, not just
    # the contributor tier.
    UserRole.KNOWLEDGE_MANAGEMENT_OFFICER,
)
# Read + contribute tier: submit/browse the repository without QA/collection
# management rights (those stay on REPO_MANAGEMENT_ROLES above). KMO also
# appears here so it keeps the contributor widgets (submit/browse) on top of
# its management widgets.
REPO_CONTRIBUTOR_ROLES: tuple[str, ...] = (
    UserRole.KNOWLEDGE_MANAGEMENT_OFFICER,
    UserRole.DIGITAL_ACADEMY_OFFICER,
    UserRole.COLLABORATOR,
    # SP6 compliance checker — works the QA queue but holds no
    # collection-management rights, so contributor tier only.
    UserRole.PUBLICATION_ASSISTANT,
)

# MEL
MEL_ROLES: tuple[str, ...] = (UserRole.MEL_OFFICER,)
# MEL — Progress Reporting field roles. These users submit implementation data
# for the indicators they're the responsible officer on (validation stays with
# the M&E Officer). "Program Implementer" has no dedicated UserRole in the SRS —
# it is any user assigned as ``Indicator.responsible`` — so the implementer
# dashboard also lights up via that implicit signal (see mel_implementer_widgets).
MEL_IMPLEMENTER_ROLES: tuple[str, ...] = (
    UserRole.PROGRAM_COORDINATOR,
    UserRole.PROJECT_MANAGER,
    UserRole.PRINCIPAL_INVESTIGATOR,
)

# SME-Hub
SME_ADMIN_ROLES: tuple[str, ...] = (
    *ADMIN_ROLES,
    UserRole.SME_ADMIN,
    UserRole.PROGRAMME_OFFICER,
)
ENTREPRENEUR_ROLES: tuple[str, ...] = (UserRole.ENTREPRENEUR,)
INVESTOR_ROLES: tuple[str, ...] = (UserRole.INVESTOR, UserRole.FUNDER)
MENTOR_ROLES: tuple[str, ...] = (UserRole.MENTOR,)

# Alumni
ALUMNI_ROLES: tuple[str, ...] = (UserRole.ALUMNI,)
ALUMNI_OFFICER_ROLES: tuple[str, ...] = (UserRole.ALUMNI_OFFICER,)


def role_of(user) -> str:
    """The user's effective role string for widget gating.

    A superuser without an explicit role is treated as `admin` so they see
    admin-tier and management widgets. A user with a real `role` keeps it
    even if they're also a superuser (so we don't lose persona context).
    """
    role = getattr(user, "role", "") or ""
    if role:
        return role
    if getattr(user, "is_superuser", False):
        return UserRole.ADMIN
    return ""


def is_admin(user) -> bool:
    if not getattr(user, "is_authenticated", False):
        return False
    if user.is_superuser:
        return True
    return role_of(user) in ADMIN_ROLES


def has_role(user, roles: tuple[str, ...]) -> bool:
    """True when the user's role is in `roles`.

    Note: superuser does NOT auto-pass here — superusers are treated as
    administrators only (see `is_admin`). Otherwise every superuser would
    see all 80+ widgets stacked together. Persona-specific gates rely on
    the actual `role` field.
    """
    if not getattr(user, "is_authenticated", False):
        return False
    role = role_of(user)
    if not role:
        return False
    return role in roles
