"""
FCMBackend – Firebase Cloud Messaging delivery backend.

Requires optional extras: ``pip install django-notify-hub[fcm]``

The backend is safe to import even when firebase-admin / fcm-django are not
installed; it raises a clear ImportError only when actually instantiated.
"""
from __future__ import annotations

import json
import logging
from typing import TYPE_CHECKING, Any

from django_notify_hub.backends.base import AbstractNotificationBackend
from django_notify_hub.exceptions import BackendError
from django_notify_hub.types import NotificationChannel

if TYPE_CHECKING:
    from django_notify_hub.types import NotificationPayload

logger = logging.getLogger("django_notify_hub")


class FCMBackend(AbstractNotificationBackend):
    """
    Delivers push notifications via Firebase Cloud Messaging.

    Reads credentials from ``DJANGO_NOTIFY["FCM_CREDENTIALS_FILE"]``
    (path to a service-account JSON file) or from the
    ``GOOGLE_APPLICATION_CREDENTIALS`` environment variable.
    """

    channel = NotificationChannel.FCM

    def setup(self, **kwargs: Any) -> None:
        try:
            import firebase_admin  # noqa: F401
            from fcm_django.models import FCMDevice  # noqa: F401
        except ImportError as exc:
            raise ImportError(
                "FCMBackend requires 'django-notify-hub[fcm]'. "
                "Run: pip install django-notify-hub[fcm]"
            ) from exc

        self._init_firebase()

    def _init_firebase(self) -> None:
        """Initialise the Firebase Admin SDK exactly once."""
        import firebase_admin
        from firebase_admin import credentials

        from django_notify_hub.conf import app_settings

        if firebase_admin._apps:
            return  # Already initialised by user or previous call

        cred_file = app_settings.FCM_CREDENTIALS_FILE
        if cred_file:
            cred = credentials.Certificate(cred_file)
            firebase_admin.initialize_app(cred)
        else:
            # Falls back to GOOGLE_APPLICATION_CREDENTIALS env var
            firebase_admin.initialize_app()

    # ── Internal delivery ──────────────────────────────────────────────

    def _deliver(self, payload: "NotificationPayload", recipient: Any) -> bool:
        from fcm_django.models import FCMDevice
        from firebase_admin.messaging import Message
        from firebase_admin.messaging import Notification as FCMNotification

        devices = FCMDevice.objects.filter(user=recipient)
        if not devices.exists():
            logger.info("FCMBackend: no device registered for user %s", recipient.pk)
            return False

        msg = self._build_message(payload)
        try:
            response = devices.send_message(msg)
            logger.info("FCMBackend: sent to %s | response=%s", recipient.pk, response)
            return True
        except Exception as exc:
            raise BackendError("FCMBackend", f"send failed for user {recipient.pk}", exc) from exc

    def _deliver_many(
        self, payload: "NotificationPayload", recipients: Any
    ) -> int:
        """Use FCM multicast where possible for better throughput."""
        from fcm_django.models import FCMDevice

        recipient_list = list(recipients)
        devices = FCMDevice.objects.filter(user__in=recipient_list)
        if not devices.exists():
            logger.info("FCMBackend: no devices found for bulk send")
            return 0

        msg = self._build_message(payload)
        try:
            devices.send_message(msg)
            return devices.count()
        except Exception as exc:
            raise BackendError("FCMBackend", "bulk send failed", exc) from exc

    @staticmethod
    def _build_message(payload: "NotificationPayload") -> Any:
        from firebase_admin.messaging import Message
        from firebase_admin.messaging import Notification as FCMNotification

        title_str = json.dumps(payload.title, ensure_ascii=False)
        body_str = json.dumps(payload.body, ensure_ascii=False)
        return Message(
            data={k: str(v) for k, v in payload.data.items()},
            notification=FCMNotification(title=title_str, body=body_str),
        )

