"""Firebase Cloud Messaging sender.

Lazy-initialises a single firebase-admin app from the service-account JSON at
settings.FIREBASE_CREDENTIALS_FILE, then pushes notifications to a user's
registered FCMDevice tokens. Invalid/unregistered tokens are deactivated so we
stop targeting dead devices.

Everything is best-effort: any failure (missing creds, network, firebase import)
is swallowed so a push never breaks the DB-notification flow that calls it.
"""

import json
import logging
import threading

from django.conf import settings

logger = logging.getLogger(__name__)

_app = None
_init_lock = threading.Lock()
_init_failed = False


def _get_app():
    """Return the initialised firebase app, or None if unavailable."""
    global _app, _init_failed

    if _app is not None:
        return _app
    if _init_failed or not getattr(settings, "FCM_ENABLED", False):
        return None

    cred_file = getattr(settings, "FIREBASE_CREDENTIALS_FILE", "")
    if not cred_file:
        _init_failed = True
        logger.info("FCM disabled: FIREBASE_CREDENTIALS_FILE not set")
        return None

    with _init_lock:
        if _app is not None:
            return _app
        try:
            import firebase_admin
            from firebase_admin import credentials

            cred = credentials.Certificate(cred_file)
            _app = firebase_admin.initialize_app(cred, name="khub-fcm")
        except Exception:
            _init_failed = True
            logger.exception("FCM init failed")
            return None

    return _app


def _build_message(token, title, body, str_data):
    from firebase_admin import messaging

    return messaging.Message(
        token=token,
        notification=messaging.Notification(title=title, body=body),
        data=str_data,
        webpush=messaging.WebpushConfig(
            notification=messaging.WebpushNotification(
                title=title, body=body, icon="/icon-192.png"
            ),
            fcm_options=messaging.WebpushFCMOptions(
                link=str_data.get("link", "/")
            ) if str_data.get("link") else None,
        ),
    )


def send_to_user(user, *, title, body, data=None):
    """Push a notification to all of a user's active devices.

    `data` values must be strings (FCM requirement); non-strings are coerced.
    Returns the number of messages FCM accepted (0 if FCM is unavailable).
    """
    app = _get_app()
    if app is None:
        return 0

    from .models import FCMDevice

    devices = list(FCMDevice.objects.filter(user=user, is_active=True))
    if not devices:
        return 0

    str_data = {k: str(v) for k, v in (data or {}).items()}
    messages = [_build_message(d.token, title, body, str_data) for d in devices]

    try:
        from firebase_admin import messaging

        response = messaging.send_each(messages, app=app)
    except Exception:
        logger.exception("FCM send failed for user %s", getattr(user, "id", "?"))
        return 0

    _prune_failed(devices, response.responses)
    return response.success_count


def send_to_users(payloads):
    """Push one notification per payload in a single batched dispatch.

    Each payload is a dict with keys: user, title, body, data. Fans out to
    every active device of every user with one FCM call instead of one
    round-trip per user. Returns the number of messages FCM accepted.
    """
    app = _get_app()
    if app is None or not payloads:
        return 0

    from .models import FCMDevice

    devices_by_user = {}
    user_ids = {p["user"].id for p in payloads}
    for device in FCMDevice.objects.filter(user_id__in=user_ids, is_active=True):
        devices_by_user.setdefault(device.user_id, []).append(device)

    messages, targets = [], []
    for p in payloads:
        str_data = {k: str(v) for k, v in (p.get("data") or {}).items()}
        for device in devices_by_user.get(p["user"].id, []):
            messages.append(_build_message(device.token, p["title"], p["body"], str_data))
            targets.append(device)

    if not messages:
        return 0

    try:
        from firebase_admin import messaging

        success = 0
        responses = []
        # send_each accepts at most 500 messages per call.
        for i in range(0, len(messages), 500):
            response = messaging.send_each(messages[i:i + 500], app=app)
            success += response.success_count
            responses.extend(response.responses)
    except Exception:
        logger.exception("FCM batch send failed (%d messages)", len(messages))
        return 0

    _prune_failed(targets[:len(responses)], responses)
    return success


def _prune_failed(devices, responses):
    """Deactivate tokens FCM reports as unregistered/invalid."""
    from firebase_admin import messaging

    dead_ids = []
    for device, result in zip(devices, responses):
        if result.success:
            continue
        exc = result.exception
        if isinstance(
            exc,
            (messaging.UnregisteredError, messaging.SenderIdMismatchError),
        ):
            dead_ids.append(device.id)

    if dead_ids:
        from .models import FCMDevice
        FCMDevice.objects.filter(id__in=dead_ids).update(is_active=False)
