import logging
from django.conf import settings
from django.db import transaction
from django.db.models.signals import post_save, pre_save
from django.dispatch import receiver
from django.utils import timezone
from channels.layers import get_channel_layer
from asgiref.sync import async_to_sync

from .models import Ticket, TicketComment
from smoothschedule.identity.users.models import User

logger = logging.getLogger(__name__)

# Store pre-save state for detecting changes
_ticket_pre_save_state = {}


# Flag to check if notifications app is available
_notifications_available = None


def is_notifications_available():
    """Check if the notifications app is installed and migrated."""
    global _notifications_available
    if _notifications_available is None:
        try:
            from smoothschedule.communication.notifications.models import Notification
            # Check if the table exists by doing a simple query
            Notification.objects.exists()
            _notifications_available = True
        except Exception:
            _notifications_available = False
    return _notifications_available


def send_websocket_notification(group_name, message_data):
    """Safely send a WebSocket notification, handling errors gracefully."""
    try:
        channel_layer = get_channel_layer()
        if channel_layer is None:
            logger.warning("Channel layer not configured, skipping WebSocket notification")
            return
        async_to_sync(channel_layer.group_send)(
            group_name,
            {
                "type": "notification_message",
                "message": message_data
            }
        )
    except Exception as e:
        logger.error(f"Failed to send WebSocket notification to {group_name}: {e}")


def create_notification(recipient, actor, verb, action_object, target, data):
    """Safely create a notification, handling import and creation errors."""
    # Skip notification creation if the notifications app is not available
    if not is_notifications_available():
        logger.debug("notifications app not available, skipping notification creation")
        return

    try:
        from smoothschedule.communication.notifications.models import Notification
        Notification.objects.create(
            recipient=recipient,
            actor=actor,
            verb=verb,
            action_object=action_object,
            target=target,
            data=data
        )
    except ImportError:
        logger.warning("notifications app not installed, skipping notification creation")
    except Exception as e:
        logger.error(f"Failed to create notification for {recipient}: {e}")


def get_platform_support_team():
    """Get all platform support team members."""
    try:
        return User.objects.filter(
            role__in=[User.Role.PLATFORM_SUPPORT, User.Role.PLATFORM_MANAGER, User.Role.SUPERUSER],
            is_active=True
        )
    except Exception as e:
        logger.error(f"Failed to fetch platform support team: {e}")
        return User.objects.none()


def get_tenant_managers(tenant):
    """Get all owners and managers for a tenant."""
    try:
        if not tenant:
            return User.objects.none()
        return User.objects.filter(
            tenant=tenant,
            role__in=[User.Role.TENANT_OWNER, User.Role.TENANT_MANAGER],
            is_active=True
        )
    except Exception as e:
        logger.error(f"Failed to fetch tenant managers for {tenant}: {e}")
        return User.objects.none()


@receiver(pre_save, sender=Ticket)
def ticket_pre_save_handler(sender, instance, **kwargs):
    """
    Capture ticket state before save for change detection.

    This allows us to compare old vs new values in post_save
    to determine what changed (assignee, status, etc.) for email notifications.
    """
    if instance.pk:
        try:
            old_instance = Ticket.objects.get(pk=instance.pk)
            _ticket_pre_save_state[instance.pk] = {
                'assignee_id': old_instance.assignee_id,
                'status': old_instance.status,
            }
        except Ticket.DoesNotExist:
            pass


@receiver(post_save, sender=Ticket)
def ticket_notification_handler(sender, instance, created, **kwargs):
    """Handle ticket save events and send notifications."""
    try:
        if created:
            # Handle ticket creation notifications
            _handle_ticket_creation(instance)
        else:
            # Handle ticket update notifications
            _handle_ticket_update(instance)
    except Exception as e:
        logger.error(f"Error in ticket_notification_handler for ticket {instance.id}: {e}")


def _send_ticket_email_notification(notification_type, ticket, **kwargs):
    """
    Safely send ticket email notifications.

    Args:
        notification_type: One of 'assigned', 'status_changed', 'resolved'
        ticket: The Ticket instance
        **kwargs: Additional arguments for the notification function
    """
    # Check if email notifications are enabled (default: True)
    if not getattr(settings, 'TICKET_EMAIL_NOTIFICATIONS_ENABLED', True):
        return

    try:
        from .email_notifications import (
            notify_ticket_assigned,
            notify_ticket_status_changed,
            notify_ticket_resolved,
        )

        if notification_type == 'assigned':
            notify_ticket_assigned(ticket)
            logger.info(f"Sent email: assignment notification for ticket #{ticket.id}")
        elif notification_type == 'status_changed':
            old_status = kwargs.get('old_status')
            notify_ticket_status_changed(ticket, old_status)
            logger.info(f"Sent email: status change notification for ticket #{ticket.id}")
        elif notification_type == 'resolved':
            resolution_message = kwargs.get('resolution_message', '')
            notify_ticket_resolved(ticket, resolution_message)
            logger.info(f"Sent email: resolution notification for ticket #{ticket.id}")
    except ImportError as e:
        logger.warning(f"Email notifications module not available: {e}")
    except Exception as e:
        logger.error(f"Failed to send email notification for ticket #{ticket.id}: {e}")


def _send_comment_email_notification(ticket, comment):
    """
    Safely send comment/reply email notifications.

    Args:
        ticket: The Ticket instance
        comment: The TicketComment instance
    """
    # Check if email notifications are enabled (default: True)
    if not getattr(settings, 'TICKET_EMAIL_NOTIFICATIONS_ENABLED', True):
        return

    # Don't send emails for internal comments
    if comment.is_internal:
        return

    try:
        from .email_notifications import notify_ticket_reply
        staff_notified, customer_notified = notify_ticket_reply(ticket, comment)
        if staff_notified:
            logger.info(f"Sent email: reply notification to staff for ticket #{ticket.id}")
        if customer_notified:
            logger.info(f"Sent email: reply notification to customer for ticket #{ticket.id}")
    except ImportError as e:
        logger.warning(f"Email notifications module not available: {e}")
    except Exception as e:
        logger.error(f"Failed to send reply email notification for ticket #{ticket.id}: {e}")


def _handle_ticket_creation(ticket):
    """Send notifications when a ticket is created."""
    try:
        creator_name = ticket.creator.full_name if ticket.creator else "Someone"

        # Send email notification if ticket is assigned on creation
        if ticket.assignee_id:
            _send_ticket_email_notification('assigned', ticket)

        if ticket.ticket_type == Ticket.TicketType.PLATFORM:
            # PLATFORM tickets: Notify platform support team
            platform_team = get_platform_support_team()
            for member in platform_team:
                create_notification(
                    recipient=member,
                    actor=ticket.creator,
                    verb=f"New platform support ticket #{ticket.id}: '{ticket.subject}'",
                    action_object=ticket,
                    target=ticket,
                    data={
                        'ticket_id': ticket.id,
                        'subject': ticket.subject,
                        'priority': ticket.priority,
                        'category': ticket.category
                    }
                )
                send_websocket_notification(
                    f"user_{member.id}",
                    {
                        "type": "new_ticket",
                        "ticket_id": ticket.id,
                        "subject": ticket.subject,
                        "ticket_type": ticket.ticket_type,
                        "priority": ticket.priority,
                        "creator_name": creator_name,
                        "message": f"New platform support ticket from {creator_name}: {ticket.subject}"
                    }
                )

        elif ticket.ticket_type in [
            Ticket.TicketType.CUSTOMER,
            Ticket.TicketType.STAFF_REQUEST,
            Ticket.TicketType.INTERNAL
        ]:
            # CUSTOMER, STAFF_REQUEST, INTERNAL tickets: Notify tenant owner/managers
            tenant_managers = get_tenant_managers(ticket.tenant)
            ticket_type_display = ticket.get_ticket_type_display()

            for manager in tenant_managers:
                create_notification(
                    recipient=manager,
                    actor=ticket.creator,
                    verb=f"New {ticket_type_display.lower()} ticket #{ticket.id}: '{ticket.subject}'",
                    action_object=ticket,
                    target=ticket,
                    data={
                        'ticket_id': ticket.id,
                        'subject': ticket.subject,
                        'priority': ticket.priority,
                        'category': ticket.category,
                        'ticket_type': ticket.ticket_type
                    }
                )
                send_websocket_notification(
                    f"user_{manager.id}",
                    {
                        "type": "new_ticket",
                        "ticket_id": ticket.id,
                        "subject": ticket.subject,
                        "ticket_type": ticket.ticket_type,
                        "priority": ticket.priority,
                        "creator_name": creator_name,
                        "message": f"New {ticket_type_display.lower()} from {creator_name}: {ticket.subject}"
                    }
                )
    except Exception as e:
        logger.error(f"Error handling ticket creation for ticket {ticket.id}: {e}")


def _handle_ticket_update(ticket):
    """Send notifications when a ticket is updated."""
    try:
        # Check for state changes to trigger email notifications
        old_state = _ticket_pre_save_state.pop(ticket.pk, None)

        if old_state:
            # Check for assignee change
            if old_state['assignee_id'] != ticket.assignee_id and ticket.assignee_id:
                _send_ticket_email_notification('assigned', ticket)

            # Check for status change
            if old_state['status'] != ticket.status:
                # If status changed to RESOLVED or CLOSED, send resolution notification
                if ticket.status in [Ticket.Status.RESOLVED, Ticket.Status.CLOSED]:
                    _send_ticket_email_notification('resolved', ticket)
                else:
                    # Regular status change notification
                    _send_ticket_email_notification(
                        'status_changed',
                        ticket,
                        old_status=old_state['status']
                    )

        update_message = {
            "type": "ticket_update",
            "ticket_id": ticket.id,
            "subject": ticket.subject,
            "status": ticket.status,
            "priority": ticket.priority,
            "ticket_type": ticket.ticket_type,
            "message": f"Ticket #{ticket.id} '{ticket.subject}' updated. Status: {ticket.status}"
        }

        # For PLATFORM tickets, notify platform support team
        if ticket.ticket_type == Ticket.TicketType.PLATFORM:
            platform_team = get_platform_support_team()
            for member in platform_team:
                send_websocket_notification(f"user_{member.id}", update_message)

        # For tenant tickets, notify tenant managers and staff with ticket access
        if ticket.tenant:
            tenant_managers = get_tenant_managers(ticket.tenant)
            for manager in tenant_managers:
                send_websocket_notification(f"user_{manager.id}", update_message)

        # Notify creator (if different from managers already notified)
        if ticket.creator:
            send_websocket_notification(f"user_{ticket.creator.id}", update_message)

        # Notify assignee if one exists
        if ticket.assignee:
            # Create Notification object for the assignee
            create_notification(
                recipient=ticket.assignee,
                actor=ticket.creator,
                verb=f"Ticket #{ticket.id} '{ticket.subject}' was updated.",
                action_object=ticket,
                target=ticket,
                data={'ticket_id': ticket.id, 'subject': ticket.subject, 'status': ticket.status}
            )
            # Send WebSocket to assignee
            send_websocket_notification(f"user_{ticket.assignee.id}", update_message)

    except Exception as e:
        logger.error(f"Error handling ticket update for ticket {ticket.id}: {e}")


@receiver(post_save, sender=TicketComment)
def comment_notification_handler(sender, instance, created, **kwargs):
    """Handle comment creation and send notifications to relevant parties."""
    if not created:
        return

    try:
        ticket = instance.ticket
        author_name = instance.author.full_name if instance.author else "Someone"

        # Send email notification for the comment
        _send_comment_email_notification(ticket, instance)

        # Track first_response_at: when a comment is added by someone other than the ticket creator
        if not ticket.first_response_at and instance.author and instance.author != ticket.creator:
            try:
                ticket.first_response_at = timezone.now()
                ticket.save(update_fields=['first_response_at'])
                logger.info(f"Set first_response_at for ticket {ticket.id}")
            except Exception as e:
                logger.error(f"Failed to set first_response_at for ticket {ticket.id}: {e}")

        # Notify creator of the ticket (if not the commenter)
        if ticket.creator and ticket.creator != instance.author:
            create_notification(
                recipient=ticket.creator,
                actor=instance.author,
                verb=f"New comment on your ticket #{ticket.id} '{ticket.subject}'.",
                action_object=instance,
                target=ticket,
                data={'ticket_id': ticket.id, 'subject': ticket.subject, 'comment_id': instance.id}
            )

            send_websocket_notification(
                f"user_{ticket.creator.id}",
                {
                    "type": "new_comment",
                    "ticket_id": ticket.id,
                    "subject": ticket.subject,
                    "comment_id": instance.id,
                    "author_name": author_name,
                    "message": f"New comment on your ticket #{ticket.id} from {author_name}."
                }
            )

        # Notify assignee of the ticket (if not the commenter and not the creator)
        if ticket.assignee and ticket.assignee != instance.author and ticket.assignee != ticket.creator:
            create_notification(
                recipient=ticket.assignee,
                actor=instance.author,
                verb=f"New comment on ticket #{ticket.id} '{ticket.subject}' you are assigned to.",
                action_object=instance,
                target=ticket,
                data={'ticket_id': ticket.id, 'subject': ticket.subject, 'comment_id': instance.id}
            )

            send_websocket_notification(
                f"user_{ticket.assignee.id}",
                {
                    "type": "new_comment",
                    "ticket_id": ticket.id,
                    "subject": ticket.subject,
                    "comment_id": instance.id,
                    "author_name": author_name,
                    "message": f"New comment on ticket #{ticket.id} you are assigned to from {author_name}."
                }
            )
    except Exception as e:
        logger.error(f"Error in comment_notification_handler for comment {instance.id}: {e}")
