"""
NotificationService – the core business-logic layer.

Responsibilities:
  1. Persist notification records via NotificationRepository (no direct ORM).
  2. Dispatch delivery via the configured backends or Celery tasks.
  3. Expose a clean API consumed by NotificationHub (Facade).

Single Responsibility: this class does NOT know about signals or HTTP.
"""
from __future__ import annotations

import logging
from typing import TYPE_CHECKING, Any

from django_notify_hub.conf import app_settings
from django_notify_hub.repositories import NotificationRepository
from django_notify_hub.types import NotificationPayload, NotificationStatus

if TYPE_CHECKING:
    from django.db.models import QuerySet

logger = logging.getLogger("django_notify_hub")


class NotificationService:
    """
    Orchestrates persistence + delivery for a single notification payload.

    Delivery is either synchronous (direct backend call) or asynchronous
    (Celery task), controlled by ``DJANGO_NOTIFY["ASYNC"]``.
    """

    def __init__(
        self,
        repository: type[NotificationRepository] | None = None,
    ) -> None:
        self._repo = repository or NotificationRepository

    # ── Public API ────────────────────────────────────────────────────────

    def send(self, payload: NotificationPayload, recipient: Any) -> None:
        """Persist and deliver one notification to one user."""
        if app_settings.ASYNC:
            self._dispatch_async(payload, recipient)
        else:
            self._send_sync(payload, recipient)

    def send_many(
        self, payload: NotificationPayload, recipients: "QuerySet | list[Any]"
    ) -> None:
        """Persist and deliver one notification to many users."""
        recipient_list = list(recipients)
        if not recipient_list:
            return

        if app_settings.ASYNC:
            self._dispatch_many_async(payload, recipient_list)
        else:
            self._send_many_sync(payload, recipient_list)

    def mark_read(self, pk: int, user: Any) -> bool:
        return self._repo.mark_read(pk, user)

    def mark_many_read(self, pks: list[int], user: Any) -> int:
        return self._repo.mark_many_read(pks, user)

    # ── Synchronous delivery ──────────────────────────────────────────────

    def _send_sync(self, payload: NotificationPayload, recipient: Any) -> None:
        from django_notify_hub.backends import BackendFactory

        channel = payload.channels[0].value if payload.channels else "fcm"
        notification = self._repo.create(payload, recipient, channel=channel)

        try:
            backend = BackendFactory.get(channel)
            backend.send(payload, recipient)
            self._repo.update_status(notification.pk, NotificationStatus.SENT.value)
        except Exception as exc:
            logger.error("NotificationService: delivery failed for pk=%s: %s", notification.pk, exc)
            self._repo.update_status(notification.pk, NotificationStatus.FAILED.value)

    def _send_many_sync(
        self, payload: NotificationPayload, recipients: list[Any]
    ) -> None:
        from django_notify_hub.backends import BackendFactory

        channel = payload.channels[0].value if payload.channels else "fcm"
        self._repo.bulk_create(payload, recipients, channel=channel)

        try:
            backend = BackendFactory.get(channel)
            backend.send_many(payload, recipients)
        except Exception as exc:
            logger.error("NotificationService: bulk delivery failed: %s", exc)

    # ── Async dispatch ────────────────────────────────────────────────────

    def _dispatch_async(self, payload: NotificationPayload, recipient: Any) -> None:
        try:
            from django_notify_hub.tasks import send_notification_task

            send_notification_task.apply_async(
                kwargs={
                    "title": payload.title,
                    "body": payload.body,
                    "data": payload.data,
                    "priority": payload.priority.value,
                    "channels": [c.value for c in payload.channels],
                    "recipient_id": recipient.pk,
                },
                queue=app_settings.CELERY_QUEUE,
            )
        except ImportError:
            logger.warning("Celery not installed; falling back to sync delivery.")
            self._send_sync(payload, recipient)

    def _dispatch_many_async(
        self, payload: NotificationPayload, recipients: list[Any]
    ) -> None:
        try:
            from django_notify_hub.tasks import send_many_notifications_task

            send_many_notifications_task.apply_async(
                kwargs={
                    "title": payload.title,
                    "body": payload.body,
                    "data": payload.data,
                    "priority": payload.priority.value,
                    "channels": [c.value for c in payload.channels],
                    "recipient_ids": [r.pk for r in recipients],
                },
                queue=app_settings.CELERY_QUEUE,
            )
        except ImportError:
            logger.warning("Celery not installed; falling back to sync bulk delivery.")
            self._send_many_sync(payload, recipients)

