"""
Notification callbacks for FSM state transitions.

This module provides callback implementations that send notifications
when state transitions occur.
"""

import logging
from typing import Any

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

from ..callback_base import PostTransitionCallback, TransitionContext

logger = logging.getLogger(__name__)


class NotificationCallback(PostTransitionCallback):
    """
    Generic notification callback for state transitions.

    Sends notifications using the NotificationService when a state
    transition completes successfully.
    """

    name: str = "NotificationCallback"

    def __init__(
        self,
        notification_type: str,
        recipient_field: str = "submitted_by",
        template_name: str | None = None,
        include_transition_data: bool = True,
        **kwargs,
    ):
        """
        Initialize the notification callback.

        Args:
            notification_type: The type of notification to create.
            recipient_field: The field name on the instance containing the recipient user.
            template_name: Optional template name for the notification.
            include_transition_data: Whether to include transition metadata in extra_data.
            **kwargs: Additional arguments passed to parent class.
        """
        super().__init__(**kwargs)
        self.notification_type = notification_type
        self.recipient_field = recipient_field
        self.template_name = template_name
        self.include_transition_data = include_transition_data

    def should_execute(self, context: TransitionContext) -> bool:
        """Check if notifications are enabled and recipient exists."""
        # Check if notifications are disabled in settings
        callback_settings = getattr(settings, "STATE_MACHINE_CALLBACKS", {})
        if not callback_settings.get("notifications_enabled", True):
            logger.debug("Notifications disabled via settings")
            return False

        # Check if recipient exists
        recipient = self._get_recipient(context.instance)
        if not recipient:
            logger.debug(f"No recipient found at {self.recipient_field} for {context}")
            return False

        return True

    def _get_recipient(self, instance: models.Model) -> Any | None:
        """Get the notification recipient from the instance."""
        return getattr(instance, self.recipient_field, None)

    def _get_notification_service(self):
        """Get the NotificationService instance."""
        try:
            from apps.accounts.services.notification_service import NotificationService

            return NotificationService()
        except ImportError:
            logger.warning("NotificationService not available")
            return None

    def _build_extra_data(self, context: TransitionContext) -> dict[str, Any]:
        """Build extra data for the notification."""
        extra_data = {}

        if self.include_transition_data:
            extra_data["transition"] = {
                "source_state": context.source_state,
                "target_state": context.target_state,
                "field_name": context.field_name,
                "timestamp": context.timestamp.isoformat(),
            }

            if context.user:
                extra_data["transition"]["by_user_id"] = context.user.id
                extra_data["transition"]["by_username"] = getattr(context.user, "username", str(context.user))

        # Include any extra data from the context
        extra_data.update(context.extra_data)

        return extra_data

    def _get_notification_title(self, context: TransitionContext) -> str:
        """Get the notification title based on context."""
        model_name = context.model_name
        return f"{model_name} status changed to {context.target_state}"

    def _get_notification_message(self, context: TransitionContext) -> str:
        """Get the notification message based on context."""
        model_name = context.model_name
        return f"The {model_name} has transitioned from {context.source_state} " f"to {context.target_state}."

    def execute(self, context: TransitionContext) -> bool:
        """Execute the notification callback."""
        notification_service = self._get_notification_service()
        if not notification_service:
            return False

        recipient = self._get_recipient(context.instance)
        if not recipient:
            return False

        try:
            extra_data = self._build_extra_data(context)

            # Create notification with required title and message
            notification_service.create_notification(
                user=recipient,
                notification_type=self.notification_type,
                title=self._get_notification_title(context),
                message=self._get_notification_message(context),
                related_object=context.instance,
                extra_data=extra_data,
            )

            logger.info(f"Created {self.notification_type} notification for " f"{recipient} on {context}")
            return True

        except Exception as e:
            logger.exception(f"Failed to create notification for {context}: {e}")
            return False


class SubmissionApprovedNotification(NotificationCallback):
    """Notification callback for approved submissions."""

    name: str = "SubmissionApprovedNotification"

    def __init__(self, submission_type: str = "submission", **kwargs):
        """
        Initialize the approval notification callback.

        Args:
            submission_type: Type of submission (e.g., "park photo", "ride review")
            **kwargs: Additional arguments passed to parent class.
        """
        super().__init__(
            notification_type="submission_approved",
            recipient_field="submitted_by",
            **kwargs,
        )
        self.submission_type = submission_type

    def _get_submission_type(self, context: TransitionContext) -> str:
        """Get the submission type from context or instance."""
        # Try to get from extra_data first
        if "submission_type" in context.extra_data:
            return context.extra_data["submission_type"]
        # Fall back to model name
        return self.submission_type or context.model_name.lower()

    def execute(self, context: TransitionContext) -> bool:
        """Execute the approval notification."""
        notification_service = self._get_notification_service()
        if not notification_service:
            return False

        recipient = self._get_recipient(context.instance)
        if not recipient:
            return False

        try:
            submission_type = self._get_submission_type(context)
            additional_message = context.extra_data.get("comment", "")

            # Use the specific method if available
            if hasattr(notification_service, "create_submission_approved_notification"):
                notification_service.create_submission_approved_notification(
                    user=recipient,
                    submission_object=context.instance,
                    submission_type=submission_type,
                    additional_message=additional_message,
                )
            else:
                # Fall back to generic notification
                extra_data = self._build_extra_data(context)
                notification_service.create_notification(
                    user=recipient,
                    notification_type=self.notification_type,
                    title=f"Your {submission_type} has been approved!",
                    message=f"Your {submission_type} submission has been approved.",
                    related_object=context.instance,
                    extra_data=extra_data,
                )

            logger.info(f"Created approval notification for {recipient} on {context}")
            return True

        except Exception as e:
            logger.exception(f"Failed to create approval notification for {context}: {e}")
            return False


class SubmissionRejectedNotification(NotificationCallback):
    """Notification callback for rejected submissions."""

    name: str = "SubmissionRejectedNotification"

    def __init__(self, submission_type: str = "submission", **kwargs):
        """
        Initialize the rejection notification callback.

        Args:
            submission_type: Type of submission (e.g., "park photo", "ride review")
            **kwargs: Additional arguments passed to parent class.
        """
        super().__init__(
            notification_type="submission_rejected",
            recipient_field="submitted_by",
            **kwargs,
        )
        self.submission_type = submission_type

    def _get_submission_type(self, context: TransitionContext) -> str:
        """Get the submission type from context or instance."""
        # Try to get from extra_data first
        if "submission_type" in context.extra_data:
            return context.extra_data["submission_type"]
        # Fall back to model name
        return self.submission_type or context.model_name.lower()

    def execute(self, context: TransitionContext) -> bool:
        """Execute the rejection notification."""
        notification_service = self._get_notification_service()
        if not notification_service:
            return False

        recipient = self._get_recipient(context.instance)
        if not recipient:
            return False

        try:
            submission_type = self._get_submission_type(context)
            # Extract rejection reason from extra_data
            rejection_reason = context.extra_data.get("reason", "No reason provided")
            additional_message = context.extra_data.get("comment", "")

            # Use the specific method if available
            if hasattr(notification_service, "create_submission_rejected_notification"):
                notification_service.create_submission_rejected_notification(
                    user=recipient,
                    submission_object=context.instance,
                    submission_type=submission_type,
                    rejection_reason=rejection_reason,
                    additional_message=additional_message,
                )
            else:
                extra_data = self._build_extra_data(context)
                notification_service.create_notification(
                    user=recipient,
                    notification_type=self.notification_type,
                    title=f"Your {submission_type} needs attention",
                    message=f"Your {submission_type} submission was rejected. Reason: {rejection_reason}",
                    related_object=context.instance,
                    extra_data=extra_data,
                )

            logger.info(f"Created rejection notification for {recipient} on {context}")
            return True

        except Exception as e:
            logger.exception(f"Failed to create rejection notification for {context}: {e}")
            return False


class SubmissionEscalatedNotification(NotificationCallback):
    """Notification callback for escalated submissions."""

    name: str = "SubmissionEscalatedNotification"

    def __init__(self, admin_recipient: bool = True, **kwargs):
        """
        Initialize escalation notification.

        Args:
            admin_recipient: If True, notify admins. If False, notify submitter.
        """
        super().__init__(
            notification_type="submission_escalated",
            recipient_field="submitted_by" if not admin_recipient else None,
            **kwargs,
        )
        self.admin_recipient = admin_recipient

    def _get_admin_users(self):
        """Get admin users to notify."""
        try:
            from django.contrib.auth import get_user_model

            user_model = get_user_model()
            return user_model.objects.filter(is_staff=True, is_active=True)
        except Exception as e:
            logger.exception(f"Failed to get admin users: {e}")
            return []

    def execute(self, context: TransitionContext) -> bool:
        """Execute the escalation notification."""
        notification_service = self._get_notification_service()
        if not notification_service:
            return False

        try:
            extra_data = self._build_extra_data(context)
            escalation_reason = context.extra_data.get("reason", "")
            if escalation_reason:
                extra_data["escalation_reason"] = escalation_reason

            title = f"{context.model_name} escalated for review"
            message = f"A {context.model_name} has been escalated and requires attention."
            if escalation_reason:
                message += f" Reason: {escalation_reason}"

            if self.admin_recipient:
                # Notify admin users
                admins = self._get_admin_users()
                for admin in admins:
                    notification_service.create_notification(
                        user=admin,
                        notification_type=self.notification_type,
                        title=title,
                        message=message,
                        related_object=context.instance,
                        extra_data=extra_data,
                    )
                logger.info(f"Created escalation notifications for {admins.count()} admins")
            else:
                # Notify the submitter
                recipient = self._get_recipient(context.instance)
                if recipient:
                    notification_service.create_notification(
                        user=recipient,
                        notification_type=self.notification_type,
                        title=title,
                        message=message,
                        related_object=context.instance,
                        extra_data=extra_data,
                    )
                    logger.info(f"Created escalation notification for {recipient}")

            return True

        except Exception as e:
            logger.exception(f"Failed to create escalation notification for {context}: {e}")
            return False


class StatusChangeNotification(NotificationCallback):
    """
    Generic notification for entity status changes.

    Used for Parks and Rides when their operational status changes.
    """

    name: str = "StatusChangeNotification"

    def __init__(
        self,
        significant_states: list[str] | None = None,
        notify_admins: bool = True,
        **kwargs,
    ):
        """
        Initialize status change notification.

        Args:
            significant_states: States that trigger admin notifications.
            notify_admins: Whether to notify admin users.
        """
        super().__init__(
            notification_type="status_change",
            **kwargs,
        )
        self.significant_states = significant_states or ["CLOSED_PERM", "DEMOLISHED", "RELOCATED"]
        self.notify_admins = notify_admins

    def should_execute(self, context: TransitionContext) -> bool:
        """Only execute for significant state changes."""
        # Check if notifications are disabled
        callback_settings = getattr(settings, "STATE_MACHINE_CALLBACKS", {})
        if not callback_settings.get("notifications_enabled", True):
            return False

        # Only notify for significant status changes
        return context.target_state in self.significant_states

    def execute(self, context: TransitionContext) -> bool:
        """Execute the status change notification."""
        if not self.notify_admins:
            return True

        notification_service = self._get_notification_service()
        if not notification_service:
            return False

        try:
            extra_data = self._build_extra_data(context)
            extra_data["entity_type"] = context.model_name
            extra_data["entity_id"] = context.instance.pk

            # Build title and message
            entity_name = getattr(context.instance, "name", str(context.instance))
            title = f"{context.model_name} status changed to {context.target_state}"
            message = f"{entity_name} has changed status from {context.source_state} " f"to {context.target_state}."

            # Notify admin users
            admins = self._get_admin_users()
            for admin in admins:
                notification_service.create_notification(
                    user=admin,
                    notification_type=self.notification_type,
                    title=title,
                    message=message,
                    related_object=context.instance,
                    extra_data=extra_data,
                )

            logger.info(
                f"Created status change notifications for {context.model_name} "
                f"({context.source_state} → {context.target_state})"
            )
            return True

        except Exception as e:
            logger.exception(f"Failed to create status change notification for {context}: {e}")
            return False

    def _get_admin_users(self):
        """Get admin users to notify."""
        try:
            from django.contrib.auth import get_user_model

            user_model = get_user_model()
            return user_model.objects.filter(is_staff=True, is_active=True)
        except Exception as e:
            logger.exception(f"Failed to get admin users: {e}")
            return []


class ModerationNotificationCallback(NotificationCallback):
    """
    Specialized callback for moderation-related notifications.

    Handles notifications for ModerationReport, ModerationQueue,
    and BulkOperation models.
    """

    name: str = "ModerationNotificationCallback"

    # Mapping of (model_name, target_state) to notification type
    NOTIFICATION_MAPPING = {
        ("ModerationReport", "UNDER_REVIEW"): "report_under_review",
        ("ModerationReport", "RESOLVED"): "report_resolved",
        ("ModerationQueue", "IN_PROGRESS"): "queue_in_progress",
        ("ModerationQueue", "COMPLETED"): "queue_completed",
        ("BulkOperation", "RUNNING"): "bulk_operation_started",
        ("BulkOperation", "COMPLETED"): "bulk_operation_completed",
        ("BulkOperation", "FAILED"): "bulk_operation_failed",
    }

    def __init__(self, **kwargs):
        super().__init__(
            notification_type="moderation",
            **kwargs,
        )

    def _get_notification_type(self, context: TransitionContext) -> str | None:
        """Get the specific notification type based on model and state."""
        key = (context.model_name, context.target_state)
        return self.NOTIFICATION_MAPPING.get(key)

    def _get_recipient(self, instance: models.Model) -> Any | None:
        """Get the appropriate recipient based on model type."""
        # Try common recipient fields
        for field in ["reporter", "assigned_to", "created_by", "submitted_by"]:
            recipient = getattr(instance, field, None)
            if recipient:
                return recipient
        return None

    def _get_notification_title(self, context: TransitionContext, notification_type: str) -> str:
        """Get the notification title based on notification type."""
        titles = {
            "report_under_review": "Your report is under review",
            "report_resolved": "Your report has been resolved",
            "queue_in_progress": "Moderation queue item in progress",
            "queue_completed": "Moderation queue item completed",
            "bulk_operation_started": "Bulk operation started",
            "bulk_operation_completed": "Bulk operation completed",
            "bulk_operation_failed": "Bulk operation failed",
        }
        return titles.get(notification_type, f"{context.model_name} status updated")

    def _get_notification_message(self, context: TransitionContext, notification_type: str) -> str:
        """Get the notification message based on notification type."""
        messages = {
            "report_under_review": "Your moderation report is now being reviewed by our team.",
            "report_resolved": "Your moderation report has been reviewed and resolved.",
            "queue_in_progress": "A moderation queue item is now being processed.",
            "queue_completed": "A moderation queue item has been completed.",
            "bulk_operation_started": "Your bulk operation has started processing.",
            "bulk_operation_completed": "Your bulk operation has completed successfully.",
            "bulk_operation_failed": "Your bulk operation encountered an error and could not complete.",
        }
        return messages.get(notification_type, f"The {context.model_name} has been updated to {context.target_state}.")

    def execute(self, context: TransitionContext) -> bool:
        """Execute the moderation notification."""
        notification_service = self._get_notification_service()
        if not notification_service:
            return False

        notification_type = self._get_notification_type(context)
        if not notification_type:
            logger.debug(f"No notification type defined for {context.model_name} " f"→ {context.target_state}")
            return True  # Not an error, just no notification needed

        recipient = self._get_recipient(context.instance)
        if not recipient:
            logger.debug(f"No recipient found for {context}")
            return True

        try:
            extra_data = self._build_extra_data(context)
            notification_service.create_notification(
                user=recipient,
                notification_type=notification_type,
                title=self._get_notification_title(context, notification_type),
                message=self._get_notification_message(context, notification_type),
                related_object=context.instance,
                extra_data=extra_data,
            )

            logger.info(f"Created {notification_type} notification for {recipient}")
            return True

        except Exception as e:
            logger.exception(f"Failed to create moderation notification for {context}: {e}")
            return False
