"""
Notification service for creating and managing user notifications.

This service handles the creation, delivery, and management of notifications
for various events including submission approvals/rejections.
"""

import logging
from datetime import datetime, timedelta
from typing import Any

from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.template.loader import render_to_string
from django.utils import timezone
from django_forwardemail.services import EmailService

from apps.accounts.models import NotificationPreference, User, UserNotification
from apps.core.utils import capture_and_log

logger = logging.getLogger(__name__)


class NotificationService:
    """Service for creating and managing user notifications."""

    @staticmethod
    def create_notification(
        user: User,
        notification_type: str,
        title: str,
        message: str,
        related_object: Any | None = None,
        priority: str = UserNotification.Priority.NORMAL,
        extra_data: dict[str, Any] | None = None,
        expires_at: datetime | None = None,
    ) -> UserNotification:
        """
        Create a new notification for a user.

        Args:
            user: The user to notify
            notification_type: Type of notification (from UserNotification.NotificationType)
            title: Notification title
            message: Notification message
            related_object: Optional related object (submission, review, etc.)
            priority: Notification priority
            extra_data: Additional data to store with notification
            expires_at: When the notification expires

        Returns:
            UserNotification: The created notification
        """
        # Get content type and object ID if related object provided
        content_type = None
        object_id = None
        if related_object:
            content_type = ContentType.objects.get_for_model(related_object)
            object_id = related_object.pk

        # Create the notification
        notification = UserNotification.objects.create(
            user=user,
            notification_type=notification_type,
            title=title,
            message=message,
            content_type=content_type,
            object_id=object_id,
            priority=priority,
            extra_data=extra_data or {},
            expires_at=expires_at,
        )

        # Send notification through appropriate channels
        NotificationService._send_notification(notification)

        return notification

    @staticmethod
    def create_submission_approved_notification(
        user: User,
        submission_object: Any,
        submission_type: str,
        additional_message: str = "",
    ) -> UserNotification:
        """
        Create a notification for submission approval.

        Args:
            user: User who submitted the content
            submission_object: The approved submission object
            submission_type: Type of submission (e.g., "park photo", "ride review")
            additional_message: Additional message from moderator

        Returns:
            UserNotification: The created notification
        """
        title = f"Your {submission_type} has been approved!"
        message = f"Great news! Your {submission_type} submission has been approved and is now live on ThrillWiki."

        if additional_message:
            message += f"\n\nModerator note: {additional_message}"

        extra_data = {
            "submission_type": submission_type,
            "moderator_message": additional_message,
            "approved_at": timezone.now().isoformat(),
        }

        return NotificationService.create_notification(
            user=user,
            notification_type=UserNotification.NotificationType.SUBMISSION_APPROVED,
            title=title,
            message=message,
            related_object=submission_object,
            priority=UserNotification.Priority.NORMAL,
            extra_data=extra_data,
        )

    @staticmethod
    def create_submission_rejected_notification(
        user: User,
        submission_object: Any,
        submission_type: str,
        rejection_reason: str,
        additional_message: str = "",
    ) -> UserNotification:
        """
        Create a notification for submission rejection.

        Args:
            user: User who submitted the content
            submission_object: The rejected submission object
            submission_type: Type of submission (e.g., "park photo", "ride review")
            rejection_reason: Reason for rejection
            additional_message: Additional message from moderator

        Returns:
            UserNotification: The created notification
        """
        title = f"Your {submission_type} needs attention"
        message = (
            f"Your {submission_type} submission has been reviewed and needs some changes before it can be approved."
        )
        message += f"\n\nReason: {rejection_reason}"

        if additional_message:
            message += f"\n\nModerator note: {additional_message}"

        message += "\n\nYou can edit and resubmit your content from your profile page."

        extra_data = {
            "submission_type": submission_type,
            "rejection_reason": rejection_reason,
            "moderator_message": additional_message,
            "rejected_at": timezone.now().isoformat(),
        }

        return NotificationService.create_notification(
            user=user,
            notification_type=UserNotification.NotificationType.SUBMISSION_REJECTED,
            title=title,
            message=message,
            related_object=submission_object,
            priority=UserNotification.Priority.HIGH,
            extra_data=extra_data,
        )

    @staticmethod
    def create_submission_pending_notification(
        user: User, submission_object: Any, submission_type: str
    ) -> UserNotification:
        """
        Create a notification for submission pending review.

        Args:
            user: User who submitted the content
            submission_object: The pending submission object
            submission_type: Type of submission (e.g., "park photo", "ride review")

        Returns:
            UserNotification: The created notification
        """
        title = f"Your {submission_type} is under review"
        message = f"Thanks for your {submission_type} submission! It's now under review by our moderation team."
        message += "\n\nWe'll notify you once it's been reviewed. This usually takes 1-2 business days."

        extra_data = {
            "submission_type": submission_type,
            "submitted_at": timezone.now().isoformat(),
        }

        return NotificationService.create_notification(
            user=user,
            notification_type=UserNotification.NotificationType.SUBMISSION_PENDING,
            title=title,
            message=message,
            related_object=submission_object,
            priority=UserNotification.Priority.LOW,
            extra_data=extra_data,
        )

    @staticmethod
    def _send_notification(notification: UserNotification) -> None:
        """
        Send notification through appropriate channels based on user preferences.

        Args:
            notification: The notification to send
        """
        user = notification.user

        # Get user's notification preferences
        try:
            preferences = user.notification_preference
        except NotificationPreference.DoesNotExist:
            # Create default preferences if they don't exist
            preferences = NotificationPreference.objects.create(user=user)

        # Send email notification if enabled
        if preferences.should_send_notification(notification.notification_type, "email"):
            NotificationService._send_email_notification(notification)

        # Toast notifications are always created (the notification object itself)
        # The frontend will display them as toast notifications based on preferences

    @staticmethod
    def _send_email_notification(notification: UserNotification) -> None:
        """
        Send email notification to user using the custom ForwardEmail service.

        Args:
            notification: The notification to send via email
        """
        try:
            user = notification.user

            # Prepare email context
            context = {
                "user": user,
                "notification": notification,
                "site_name": "ThrillWiki",
                "site_url": getattr(settings, "SITE_URL", "https://thrillwiki.com"),
            }

            # Render email templates
            subject = f"ThrillWiki: {notification.title}"
            html_message = render_to_string("emails/notification.html", context)
            plain_message = render_to_string("emails/notification.txt", context)

            # Send email using custom ForwardEmail service
            EmailService.send_email(
                to=user.email,
                subject=subject,
                text=plain_message,
                html=html_message,
            )

            # Mark as sent
            notification.email_sent = True
            notification.email_sent_at = timezone.now()
            notification.save(update_fields=["email_sent", "email_sent_at"])

            logger.info(f"Email notification sent to {user.email} for notification {notification.id}")

        except Exception as e:
            capture_and_log(e, f'Send email notification {notification.id}', source='service')

    @staticmethod
    def get_user_notifications(
        user: User,
        unread_only: bool = False,
        notification_types: list[str] | None = None,
        limit: int | None = None,
    ) -> list[UserNotification]:
        """
        Get notifications for a user.

        Args:
            user: User to get notifications for
            unread_only: Only return unread notifications
            notification_types: Filter by notification types
            limit: Limit number of results

        Returns:
            List[UserNotification]: List of notifications
        """
        queryset = UserNotification.objects.filter(user=user)

        if unread_only:
            queryset = queryset.filter(is_read=False)

        if notification_types:
            queryset = queryset.filter(notification_type__in=notification_types)

        # Exclude expired notifications
        queryset = queryset.filter(models.Q(expires_at__isnull=True) | models.Q(expires_at__gt=timezone.now()))

        if limit:
            queryset = queryset[:limit]

        return list(queryset)

    @staticmethod
    def mark_notifications_read(user: User, notification_ids: list[int] | None = None) -> int:
        """
        Mark notifications as read for a user.

        Args:
            user: User whose notifications to mark as read
            notification_ids: Specific notification IDs to mark as read (if None, marks all)

        Returns:
            int: Number of notifications marked as read
        """
        queryset = UserNotification.objects.filter(user=user, is_read=False)

        if notification_ids:
            queryset = queryset.filter(id__in=notification_ids)

        return queryset.update(is_read=True, read_at=timezone.now())

    @staticmethod
    def cleanup_old_notifications(days: int = 90) -> int:
        """
        Clean up old read notifications.

        Args:
            days: Number of days to keep read notifications

        Returns:
            int: Number of notifications deleted
        """
        cutoff_date = timezone.now() - timedelta(days=days)

        old_notifications = UserNotification.objects.filter(is_read=True, read_at__lt=cutoff_date)

        count = old_notifications.count()
        old_notifications.delete()

        logger.info(f"Cleaned up {count} old notifications")
        return count
