from celery import shared_task
from django.core.management import call_command


@shared_task
def run_send_pending_notifications():
    call_command("send_pending_notifications")


@shared_task(name="apps.core.tasks.run_sync_moodle_roles")
def run_sync_moodle_roles():
    """Nightly IILMP→Moodle role reconcile (assign + demote).

    The login-time sync is assign-only; this sweep is what catches role changes
    made in IILMP user management after first login, including revocations.
    """
    call_command("sync_moodle_roles", demote=True)


@shared_task(
    name="apps.core.sync_moodle_role",
    bind=True,
    max_retries=5,
)
def sync_moodle_role(self, user_id: int):
    """Assign the mapped Moodle system role after an SSO login.

    Fired from the OIDC claims hook (see ``authentication/oidc.py``) — at that
    moment Moodle may not have committed the new ``mdl_user`` row yet (first
    login), so a not-found retries with backoff. Assign-only by design: a login
    never demotes; demotions run via ``manage.py sync_moodle_roles --demote``.
    """
    import requests

    from django.contrib.auth import get_user_model

    from apps.core.authentication import moodle_roles

    user = get_user_model().objects.filter(pk=user_id).first()
    if user is None:
        return moodle_roles.OUTCOME_UNMAPPED

    try:
        outcome = moodle_roles.sync_user(user)
    except (moodle_roles.MoodleError, requests.RequestException) as exc:
        raise self.retry(exc=exc, countdown=20 * (2 ** self.request.retries))

    if outcome == moodle_roles.OUTCOME_NOT_IN_MOODLE:
        # Moodle hasn't created the account yet — first-login race.
        raise self.retry(countdown=20 * (2 ** self.request.retries))
    return outcome
