import logging
from typing import TYPE_CHECKING, Any

from django.db import transaction

if TYPE_CHECKING:
    from .types import NotificationType


def send_notification(
    recipient: Any,
    notification_type: "type[NotificationType]",
    actor: Any | None = None,
    target: Any | None = None,
    subject: str = "",
    text: str = "",
    url: str = "",
    metadata: dict[str, Any] | None = None,
    **kwargs,
):
    """
    Send a notification to a user through all registered channels.

    Args:
        recipient: User to send notification to
        notification_type: Type of notification (must be registered in registry)
        actor: User who triggered the notification (optional)
        target: Object the notification is about (optional)
        subject: Notification subject line (optional, can be generated by notification type)
        text: Notification body text (optional, can be generated by notification type)
        url: URL associated with the notification (optional)
        metadata: Additional JSON-serializable data to store with the notification (optional)
        **kwargs: Any additional fields for the notification model

    Returns:
        Notification instance or None if no channels are enabled

    Raises:
        ValueError: If notification_type is not registered
    """
    from .models import Notification, NotificationChannel
    from .registry import registry

    # Validate notification type is registered
    try:
        registry.get_type(notification_type.key)
    except KeyError:
        available_types = [t.key for t in registry.get_all_types()]
        if available_types:
            raise ValueError(
                f"Notification type '{notification_type}' not registered. Available types: {available_types}"
            )
        else:
            raise ValueError(
                f"Notification type '{notification_type}' not registered. No notification types are currently registered."
            )

    # Determine which channels are enabled for this user/notification type
    enabled_channel_classes = notification_type.get_enabled_channels(recipient)

    # Don't create notification if no channels are enabled
    if not enabled_channel_classes:
        return None

    # Create the notification record
    notification = Notification(
        recipient=recipient,
        notification_type=notification_type.key,
        actor=actor,
        target=target,
        subject=subject,
        text=text,
        url=url,
        metadata=metadata or {},
        **kwargs,
    )

    # Use transaction to ensure atomicity when checking/updating existing notifications
    with transaction.atomic():
        if notification_type.should_save(notification):
            notification.save()

            # Check should_send and process for each enabled channel
            for channel_cls in enabled_channel_classes:
                if channel_cls.should_send(notification):
                    # Create NotificationChannel entry only for channels that can send
                    NotificationChannel.objects.create(
                        notification=notification,
                        channel=channel_cls.key,
                    )

                    # Process the notification through this channel
                    try:
                        channel_instance = channel_cls()
                        channel_instance.process(notification)
                    except Exception as e:
                        # Log error but don't crash - other channels should still work
                        logger = logging.getLogger(__name__)
                        logger.error(f"Channel {channel_cls.key} failed to process notification {notification.id}: {e}")

            return notification
