"""Web Push delivery -- the push channel for notifications.Notification.

Wired in from mobile.signals (a post_save on Notification), deliberately kept
out of the notifications app itself, which stays channel-agnostic (see that
app's models.py docstring: "the whole point is reusing this for other kinds
of activity later"). A backward dependency the other way -- notifications
importing mobile -- would be the wrong direction: notifications has no
reason to know a PWA exists.
"""

import json
import logging

from django.conf import settings
from pywebpush import WebPushException, webpush

from .. import models

logger = logging.getLogger(__name__)


def send_push_to_member(member, *, title: str, body: str, url: str = "/app/") -> None:
    if not settings.VAPID_PRIVATE_KEY:
        # Dev/no-config default -- see the settings.py comment next to VAPID_PRIVATE_KEY.
        return

    payload = json.dumps({"title": title, "body": body, "url": url})
    for subscription in models.PushSubscription.objects.filter(member=member):
        try:
            webpush(
                subscription_info=subscription.as_subscription_info(),
                data=payload,
                vapid_private_key=settings.VAPID_PRIVATE_KEY,
                vapid_claims={"sub": f"mailto:{settings.VAPID_ADMIN_EMAIL}"},
            )
        except WebPushException as exc:
            status = exc.response.status_code if exc.response is not None else None
            if status in (404, 410):
                # The browser's push service says this registration is gone for good --
                # not a transient failure, so keeping it around would only mean retrying
                # a subscription that will never accept a push again.
                subscription.delete()
            else:
                logger.warning("Push send failed for %s: %s", member, exc)
        except OSError as exc:
            # Never fatal -- same reasoning as every other branded send in this app
            # (see e.g. notifications.services._send_email).
            logger.warning("Push send failed for %s: %s", member, exc)
