"""
Themis notification utilities.

The ``notify_user()`` function is the standard interface for creating
notifications. Consuming apps should always use this function rather
than creating ``UserNotification`` objects directly, because it handles
user preference checks, deduplication, and level filtering.
"""
import logging
from datetime import timedelta

from django.conf import settings
from django.db import models
from django.utils import timezone

from themis.models import UserNotification

logger = logging.getLogger(__name__)


def notify_user(
    user,
    title,
    message="",
    level="info",
    url="",
    source_app="",
    source_model="",
    source_id="",
    deduplicate=False,
    expires_at=None,
):
    """
    Create an in-app notification for a user.

    This is the standard entry point for all notification creation.
    It respects the user's notification preferences (enabled flag,
    minimum level) and optionally deduplicates based on source fields.

    Args:
        user: Django User instance (the recipient).
        title: Short headline (max 200 chars).
        message: Optional body text.
        level: One of "info", "success", "warning", "danger".
        url: Optional URL the notification links to.
        source_app: App label of the sender (for tracking/cleanup).
        source_model: Model name of the sender.
        source_id: PK of the source object (as string).
        deduplicate: If True, skip creation when an unread notification
                     with the same source_app/source_model/source_id
                     already exists for this user.
        expires_at: Optional datetime when notification auto-expires.

    Returns:
        UserNotification instance, or None if skipped.
    """
    # Check if user has a profile with notifications enabled
    profile = getattr(user, "profile", None)
    if profile:
        if not profile.notifications_enabled:
            logger.debug(
                "Notification skipped for %s: notifications disabled", user.username
            )
            return None

        # Check minimum level
        min_level = profile.notifications_min_level
        level_weights = UserNotification.LEVEL_WEIGHTS
        if level_weights.get(level, 0) < level_weights.get(min_level, 0):
            logger.debug(
                "Notification skipped for %s: level %s below minimum %s",
                user.username,
                level,
                min_level,
            )
            return None

    # Deduplication check
    if deduplicate and source_app and source_model and source_id:
        existing = UserNotification.objects.filter(
            user=user,
            source_app=source_app,
            source_model=source_model,
            source_id=str(source_id),
            is_read=False,
            is_dismissed=False,
        ).exists()
        if existing:
            logger.debug(
                "Notification skipped for %s: duplicate %s/%s/%s",
                user.username,
                source_app,
                source_model,
                source_id,
            )
            return None

    notification = UserNotification.objects.create(
        user=user,
        title=title[:200],
        message=message,
        level=level,
        url=url,
        source_app=source_app,
        source_model=source_model,
        source_id=str(source_id) if source_id else "",
        expires_at=expires_at,
    )

    logger.info(
        "Notification created: [%s] %s → %s", level, title[:50], user.username
    )
    return notification


def get_unread_count(user):
    """
    Return the number of unread, non-dismissed notifications for a user.

    Respects the user's minimum notification level preference.
    """
    if not user.is_authenticated:
        return 0

    qs = UserNotification.objects.filter(
        user=user,
        is_read=False,
        is_dismissed=False,
    )

    # Filter expired
    qs = qs.filter(
        models.Q(expires_at__isnull=True) | models.Q(expires_at__gt=timezone.now())
    )

    # Respect minimum level preference
    profile = getattr(user, "profile", None)
    if profile and profile.notifications_min_level != "info":
        min_weight = UserNotification.LEVEL_WEIGHTS.get(
            profile.notifications_min_level, 0
        )
        eligible_levels = [
            lvl
            for lvl, weight in UserNotification.LEVEL_WEIGHTS.items()
            if weight >= min_weight
        ]
        qs = qs.filter(level__in=eligible_levels)

    return qs.count()


def get_notifications_for_user(user, include_read=False, limit=50):
    """
    Return notifications for a user, respecting preferences.

    Args:
        user: Django User instance.
        include_read: If True, include read notifications.
        limit: Maximum number of notifications to return.

    Returns:
        QuerySet of UserNotification instances.
    """
    qs = UserNotification.objects.filter(user=user, is_dismissed=False)

    if not include_read:
        qs = qs.filter(is_read=False)

    # Filter expired
    qs = qs.filter(
        models.Q(expires_at__isnull=True) | models.Q(expires_at__gt=timezone.now())
    )

    # Respect minimum level preference
    profile = getattr(user, "profile", None)
    if profile and profile.notifications_min_level != "info":
        min_weight = UserNotification.LEVEL_WEIGHTS.get(
            profile.notifications_min_level, 0
        )
        eligible_levels = [
            lvl
            for lvl, weight in UserNotification.LEVEL_WEIGHTS.items()
            if weight >= min_weight
        ]
        qs = qs.filter(level__in=eligible_levels)

    return qs[:limit]


def cleanup_notifications(user=None, max_age_days=None):
    """
    Delete old read/dismissed notifications.

    If ``user`` is provided, only cleans up that user's notifications
    using their ``notification_retention_days`` preference. If ``user``
    is None, cleans up all users using the global max age setting.

    Args:
        user: Optional User instance.
        max_age_days: Override retention period in days.

    Returns:
        Number of notifications deleted.
    """
    global_max_age = getattr(settings, "THEMIS_NOTIFICATION_MAX_AGE_DAYS", 90)
    now = timezone.now()
    total_deleted = 0

    if user:
        # Per-user cleanup
        profile = getattr(user, "profile", None)
        retention = max_age_days or (
            profile.notification_retention_days if profile else global_max_age
        )
        cutoff = now - timedelta(days=retention)
        deleted, _ = UserNotification.objects.filter(
            user=user,
            is_read=True,
            created_at__lt=cutoff,
        ).delete()
        total_deleted += deleted

        # Also delete dismissed notifications older than cutoff
        deleted, _ = UserNotification.objects.filter(
            user=user,
            is_dismissed=True,
            created_at__lt=cutoff,
        ).delete()
        total_deleted += deleted

        # Delete expired notifications
        deleted, _ = UserNotification.objects.filter(
            user=user,
            expires_at__lt=now,
        ).delete()
        total_deleted += deleted
    else:
        # Global cleanup — hard ceiling
        cutoff = now - timedelta(days=max_age_days or global_max_age)
        deleted, _ = UserNotification.objects.filter(
            is_read=True,
            created_at__lt=cutoff,
        ).delete()
        total_deleted += deleted

        deleted, _ = UserNotification.objects.filter(
            is_dismissed=True,
            created_at__lt=cutoff,
        ).delete()
        total_deleted += deleted

        # Delete expired notifications
        deleted, _ = UserNotification.objects.filter(
            expires_at__lt=now,
        ).delete()
        total_deleted += deleted

    return total_deleted
