"""
Django signals for Support app
Handles automatic notifications when tickets are updated
"""

from django.db.models.signals import post_save, pre_save
from django.dispatch import receiver
from django.core.mail import EmailMultiAlternatives, get_connection
from django.template.loader import render_to_string
from django.conf import settings
import logging
import time

from .models import SupportTicket, TicketMessage, TicketActivity

logger = logging.getLogger(__name__)


def _send_email_with_retry(email_message, max_retries: int = 3, delay: float = 1.0) -> bool:
    """
    Send email with retry logic for production reliability.
    Uses Django settings from .env file.
    
    Args:
        email_message: EmailMultiAlternatives instance
        max_retries: Maximum number of retry attempts
        delay: Delay between retries in seconds
        
    Returns:
        bool: True if email was sent successfully, False otherwise
    """
    for attempt in range(max_retries + 1):
        try:
            # Test connection before sending (uses EMAIL_BACKEND from settings)
            connection = get_connection()
            connection.open()
            connection.close()
            
            # Send the email (uses EMAIL_BACKEND and credentials from settings)
            email_message.send()
            return True
            
        except Exception as e:
            logger.warning(f"Email send attempt {attempt + 1} failed: {str(e)}")
            
            if attempt < max_retries:
                time.sleep(delay * (2 ** attempt))  # Exponential backoff
                continue
            else:
                logger.error(f"Failed to send email after {max_retries + 1} attempts: {str(e)}")
                return False
    
    return False


class TicketUpdateNotifier:
    """Service for sending ticket update notifications"""
    
    @staticmethod
    def send_status_change_notification(ticket, old_status, new_status):
        """Send email when ticket status changes"""
        try:
            subject = f'Ticket Status Updated: {ticket.ticket_number}'
            
            context = {
                'ticket': ticket,
                'ticket_number': ticket.ticket_number,
                'user_name': ticket.user_name,
                'title': ticket.title,
                'old_status': old_status,
                'new_status': new_status,
                'status_color': ticket.status.color if ticket.status else '#3b82f6',
                'updated_at': ticket.updated_at.strftime('%B %d, %Y at %I:%M %p'),
                'support_url': f'{settings.SITE_URL}/support-center',
                'logo_url': f'{settings.SITE_URL}/images/logo.png',
                'site_url': settings.SITE_URL,
            }
            
            # Render HTML email
            html_message = render_to_string(
                'support/ticket_status_update.html',
                context
            )
            
            # Create plain text version
            text_message = render_to_string(
                'support/ticket_status_update.txt',
                context
            )
            
            # Create email (uses DEFAULT_FROM_EMAIL from settings)
            email = EmailMultiAlternatives(
                subject=subject,
                body=text_message,
                from_email=settings.DEFAULT_FROM_EMAIL,  # From .env file
                to=[ticket.user_email],
            )
            
            email.attach_alternative(html_message, "text/html")
            
            # Send email with retry logic (uses EMAIL_BACKEND and credentials from settings)
            success = _send_email_with_retry(email)
            
            if success:
                logger.info(f'Status change notification sent for ticket {ticket.ticket_number}')
            else:
                logger.error(f'Failed to send status change notification for ticket {ticket.ticket_number} after retries')
            return success
            
        except Exception as e:
            logger.error(f'Failed to send status change notification: {str(e)}')
            return False
    
    @staticmethod
    def send_message_notification(ticket, message):
        """Send email when a new message is added to the ticket"""
        try:
            # Only send if it's an agent response (not user message)
            if message.message_type != 'agent_response':
                return False
            
            subject = f'New Response on Ticket: {ticket.ticket_number}'
            
            context = {
                'ticket': ticket,
                'ticket_number': ticket.ticket_number,
                'user_name': ticket.user_name,
                'title': ticket.title,
                'message': message.content,
                'message_author': message.author_name or 'Support Team',
                'created_at': message.created_at.strftime('%B %d, %Y at %I:%M %p'),
                'support_url': f'{settings.SITE_URL}/support-center',
                'logo_url': f'{settings.SITE_URL}/images/logo.png',
                'site_url': settings.SITE_URL,
            }
            
            # Render HTML email
            html_message = render_to_string(
                'support/ticket_message_notification.html',
                context
            )
            
            # Create plain text version
            text_message = render_to_string(
                'support/ticket_message_notification.txt',
                context
            )
            
            # Create email (uses DEFAULT_FROM_EMAIL from settings)
            email = EmailMultiAlternatives(
                subject=subject,
                body=text_message,
                from_email=settings.DEFAULT_FROM_EMAIL,  # From .env file
                to=[ticket.user_email],
            )
            
            email.attach_alternative(html_message, "text/html")
            
            # Send email with retry logic (uses EMAIL_BACKEND and credentials from settings)
            success = _send_email_with_retry(email)
            
            if success:
                logger.info(f'Message notification sent for ticket {ticket.ticket_number}')
            else:
                logger.error(f'Failed to send message notification for ticket {ticket.ticket_number} after retries')
            return success
            
        except Exception as e:
            logger.error(f'Failed to send message notification: {str(e)}')
            return False
    
    @staticmethod
    def send_assignment_notification(ticket, assigned_to):
        """Send email when ticket is assigned"""
        try:
            subject = f'Ticket Assigned: {ticket.ticket_number}'
            
            context = {
                'ticket': ticket,
                'ticket_number': ticket.ticket_number,
                'user_name': ticket.user_name,
                'title': ticket.title,
                'assigned_to': assigned_to.get_full_name() or assigned_to.username,
                'updated_at': ticket.updated_at.strftime('%B %d, %Y at %I:%M %p'),
                'support_url': f'{settings.SITE_URL}/support-center',
                'logo_url': f'{settings.SITE_URL}/images/logo.png',
                'site_url': settings.SITE_URL,
            }
            
            # Render HTML email
            html_message = render_to_string(
                'support/ticket_assigned_notification.html',
                context
            )
            
            # Create plain text version
            text_message = render_to_string(
                'support/ticket_assigned_notification.txt',
                context
            )
            
            # Create email (uses DEFAULT_FROM_EMAIL from settings)
            email = EmailMultiAlternatives(
                subject=subject,
                body=text_message,
                from_email=settings.DEFAULT_FROM_EMAIL,  # From .env file
                to=[ticket.user_email],
            )
            
            email.attach_alternative(html_message, "text/html")
            
            # Send email with retry logic (uses EMAIL_BACKEND and credentials from settings)
            success = _send_email_with_retry(email)
            
            if success:
                logger.info(f'Assignment notification sent for ticket {ticket.ticket_number}')
            else:
                logger.error(f'Failed to send assignment notification for ticket {ticket.ticket_number} after retries')
            return success
            
        except Exception as e:
            logger.error(f'Failed to send assignment notification: {str(e)}')
            return False


# Store original values before save
@receiver(pre_save, sender=SupportTicket)
def store_ticket_original_values(sender, instance, **kwargs):
    """Store original values before ticket is updated"""
    if instance.pk:
        try:
            original = SupportTicket.objects.get(pk=instance.pk)
            instance._original_status = original.status
            instance._original_assigned_to = original.assigned_to
        except SupportTicket.DoesNotExist:
            pass


# Send notifications after ticket is saved
@receiver(post_save, sender=SupportTicket)
def send_ticket_update_notifications(sender, instance, created, **kwargs):
    """Send notifications when ticket is updated"""
    if created:
        # Ticket creation is handled in serializer
        return
    
    # Check for status change
    if hasattr(instance, '_original_status') and instance._original_status != instance.status:
        old_status = instance._original_status.name if instance._original_status else 'Unknown'
        new_status = instance.status.name if instance.status else 'Unknown'
        
        TicketUpdateNotifier.send_status_change_notification(
            instance, 
            old_status, 
            new_status
        )
        
        # Create activity log
        TicketActivity.objects.create(
            ticket=instance,
            activity_type='status_changed',
            description=f'Status changed from {old_status} to {new_status}',
            old_value=old_status,
            new_value=new_status
        )
    
    # Check for assignment change
    if hasattr(instance, '_original_assigned_to') and instance._original_assigned_to != instance.assigned_to:
        if instance.assigned_to:
            TicketUpdateNotifier.send_assignment_notification(
                instance,
                instance.assigned_to
            )
            
            # Create activity log
            TicketActivity.objects.create(
                ticket=instance,
                activity_type='assigned',
                description=f'Ticket assigned to {instance.assigned_to.get_full_name() or instance.assigned_to.username}',
                new_value=str(instance.assigned_to)
            )


# Send notification when message is added
@receiver(post_save, sender=TicketMessage)
def send_message_notification(sender, instance, created, **kwargs):
    """Send notification when a new message is added"""
    if created and not instance.is_internal:
        # Only send if it's a new message that's not internal
        TicketUpdateNotifier.send_message_notification(
            instance.ticket,
            instance
        )

