"""
Ticket Email Notification Service

Sends email notifications for ticket events using Puck-based email templates.
Handles:
- Ticket assignment notifications
- Status change notifications
- Reply notifications (to both staff and customers)
- Resolution notifications

Uses PuckEmailTemplate with the messaging.email_service for rendering and sending.
"""

import logging
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from typing import Optional, Dict, Any
from django.conf import settings
from django.core.mail import EmailMultiAlternatives
from django.utils import timezone

from .models import Ticket, TicketComment
from smoothschedule.communication.messaging.email_types import EmailType
from smoothschedule.communication.messaging.email_service import send_system_email

logger = logging.getLogger(__name__)


def get_default_platform_email():
    """
    Get the default PlatformEmailAddress for sending platform emails.
    Returns None if no default is configured.
    """
    try:
        from smoothschedule.platform.admin.models import PlatformEmailAddress
        return PlatformEmailAddress.objects.filter(
            is_default=True,
            is_active=True,
            mail_server_synced=True
        ).first()
    except Exception as e:
        logger.warning(f"Could not get default platform email: {e}")
        return None


class TicketEmailService:
    """
    Service for sending ticket-related email notifications.

    Uses PuckEmailTemplate system for customizable templates.
    Maps ticket events to EmailType enum values.
    """

    # Map ticket events to EmailType enum values
    EMAIL_TYPE_MAPPING = {
        'assigned': EmailType.TICKET_ASSIGNED,
        'reply': EmailType.TICKET_REPLY,
        'resolved': EmailType.TICKET_RESOLVED,
    }

    def __init__(self, ticket: Ticket):
        """
        Initialize with a ticket instance.

        Args:
            ticket: The Ticket model instance
        """
        self.ticket = ticket
        self.tenant = ticket.tenant

    def _get_base_context(self) -> Dict[str, Any]:
        """
        Get base context variables for all ticket emails.

        Returns:
            Dictionary of context variables for template rendering.
            Uses snake_case names to match PuckEmailTemplate tag system.
        """
        # Build ticket URL
        base_url = getattr(settings, 'FRONTEND_URL', 'http://localhost:5173')
        if self.tenant:
            ticket_url = f"{base_url}/tickets/{self.ticket.id}"
        else:
            ticket_url = f"{base_url}/platform/tickets/{self.ticket.id}"

        # Get business context if tenant exists
        tenant_name = self.tenant.name if self.tenant else 'SmoothSchedule Platform'
        tenant_email = getattr(self.tenant, 'contact_email', '') if self.tenant else settings.DEFAULT_FROM_EMAIL
        tenant_phone = getattr(self.tenant, 'phone', '') if self.tenant else ''

        # Get creator/customer info
        creator = self.ticket.creator
        customer_first_name = creator.first_name if creator else 'Customer'
        customer_name = creator.get_full_name() if creator else 'Customer'
        customer_email = creator.email if creator else ''

        return {
            # Tenant/business context
            'tenant_name': tenant_name,
            'tenant_email': tenant_email,
            'tenant_phone': tenant_phone,
            # Customer context
            'customer_first_name': customer_first_name,
            'customer_name': customer_name,
            'customer_email': customer_email,
            # Ticket context
            'ticket_id': str(self.ticket.id),
            'ticket_subject': self.ticket.subject,
            'ticket_message': self.ticket.description,
            'ticket_status': self.ticket.get_status_display(),
            'ticket_priority': self.ticket.get_priority_display(),
            'ticket_link': ticket_url,
            # Date/time
            'current_date': timezone.now().strftime('%B %d, %Y'),
            'current_time': timezone.now().strftime('%I:%M %p'),
        }

    def _send_email(
        self,
        to_email: str,
        subject: str,
        html_content: str,
        text_content: str,
        reply_to: Optional[str] = None
    ) -> bool:
        """
        Send an email with both HTML and plain text versions.

        For platform-level tickets (no tenant), uses the default PlatformEmailAddress
        with direct SMTP. For tenant tickets, uses Django's email backend.

        Args:
            to_email: Recipient email address
            subject: Email subject
            html_content: HTML email body
            text_content: Plain text email body
            reply_to: Optional Reply-To address for threading

        Returns:
            True if email sent successfully, False otherwise
        """
        if not to_email:
            logger.warning("Cannot send email: no recipient address")
            return False

        # Generate reply-to address
        if not reply_to:
            reply_domain = None
            try:
                from .models import TicketEmailSettings
                email_settings = TicketEmailSettings.get_instance()
                if email_settings.support_email_domain:
                    reply_domain = email_settings.support_email_domain
            except Exception:
                pass

            if not reply_domain:
                reply_domain = getattr(settings, 'SUPPORT_EMAIL_DOMAIN', 'smoothschedule.com')

            reply_to = f"support+ticket-{self.ticket.id}@{reply_domain}"

        # For platform-level tickets, try to use the default PlatformEmailAddress
        if not self.tenant:
            platform_email = get_default_platform_email()
            if platform_email:
                return self._send_via_platform_smtp(
                    platform_email=platform_email,
                    to_email=to_email,
                    subject=subject,
                    html_content=html_content,
                    text_content=text_content,
                    reply_to=reply_to
                )

        # Fall back to Django's email backend
        try:
            from_email = getattr(settings, 'DEFAULT_FROM_EMAIL', 'noreply@smoothschedule.com')

            msg = EmailMultiAlternatives(
                subject=subject,
                body=text_content,
                from_email=from_email,
                to=[to_email],
            )

            if html_content:
                msg.attach_alternative(html_content, 'text/html')

            msg.reply_to = [reply_to]
            msg.extra_headers = {
                'X-Ticket-ID': str(self.ticket.id),
                'X-Ticket-Type': self.ticket.ticket_type,
            }

            msg.send(fail_silently=False)
            logger.info(f"Sent ticket email to {to_email}: {subject}")
            return True

        except Exception as e:
            logger.error(f"Failed to send ticket email to {to_email}: {e}")
            return False

    def _send_via_platform_smtp(
        self,
        platform_email,
        to_email: str,
        subject: str,
        html_content: str,
        text_content: str,
        reply_to: str
    ) -> bool:
        """
        Send email using the PlatformEmailAddress SMTP settings.

        Args:
            platform_email: PlatformEmailAddress instance
            to_email: Recipient email address
            subject: Email subject
            html_content: HTML email body
            text_content: Plain text email body
            reply_to: Reply-To address

        Returns:
            True if email sent successfully, False otherwise
        """
        try:
            smtp_settings = platform_email.get_smtp_settings()

            # Build the email
            if html_content:
                msg = MIMEMultipart('alternative')
                msg.attach(MIMEText(text_content, 'plain'))
                msg.attach(MIMEText(html_content, 'html'))
            else:
                msg = MIMEText(text_content, 'plain')

            # Set headers
            from_addr = f"{platform_email.effective_sender_name} <{platform_email.email_address}>"
            msg['Subject'] = subject
            msg['From'] = from_addr
            msg['To'] = to_email
            msg['Reply-To'] = reply_to
            msg['X-Ticket-ID'] = str(self.ticket.id)
            msg['X-Ticket-Type'] = self.ticket.ticket_type

            # Connect and send
            if smtp_settings['use_ssl']:
                server = smtplib.SMTP_SSL(smtp_settings['host'], smtp_settings['port'])
            else:
                server = smtplib.SMTP(smtp_settings['host'], smtp_settings['port'])
                if smtp_settings['use_tls']:
                    server.starttls()

            server.login(smtp_settings['username'], smtp_settings['password'])
            server.sendmail(platform_email.email_address, [to_email], msg.as_string())
            server.quit()

            logger.info(f"Sent platform ticket email from {platform_email.email_address} to {to_email}: {subject}")
            return True

        except Exception as e:
            logger.error(f"Failed to send platform email via SMTP to {to_email}: {e}")
            return False

    def send_assignment_notification(self) -> bool:
        """
        Send notification when ticket is assigned to someone.

        Sends email to the assignee with ticket details using PuckEmailTemplate.

        Returns:
            True if email sent successfully
        """
        if not self.ticket.assignee:
            logger.warning(f"Ticket {self.ticket.id} has no assignee")
            return False

        assignee = self.ticket.assignee
        if not assignee.email:
            logger.warning(f"Assignee {assignee.id} has no email address")
            return False

        context = self._get_base_context()
        context['assignee_name'] = assignee.get_full_name() or assignee.email
        context['recipient_name'] = context['assignee_name']

        return send_system_email(
            email_type=EmailType.TICKET_ASSIGNED,
            to_email=assignee.email,
            context=context,
            extra_headers={
                'X-Ticket-ID': str(self.ticket.id),
                'X-Ticket-Type': self.ticket.ticket_type,
            },
            fail_silently=True,
        )

    def send_status_change_notification(self, old_status: str, notify_customer: bool = True) -> bool:
        """
        Send notification when ticket status changes.

        Note: Status change emails are not a separate email type in the new system.
        We use the ticket_reply type as a general notification.

        Args:
            old_status: Previous status value
            notify_customer: Whether to notify the ticket creator

        Returns:
            True if email sent successfully
        """
        if not notify_customer or not self.ticket.creator or not self.ticket.creator.email:
            return False

        context = self._get_base_context()
        context['recipient_name'] = self.ticket.creator.get_full_name() or 'Customer'
        context['old_status'] = dict(Ticket.Status.choices).get(old_status, old_status)
        context['reply_message'] = f"Your ticket status has been updated from {context['old_status']} to {context['ticket_status']}."

        return send_system_email(
            email_type=EmailType.TICKET_REPLY,
            to_email=self.ticket.creator.email,
            context=context,
            extra_headers={
                'X-Ticket-ID': str(self.ticket.id),
                'X-Ticket-Type': self.ticket.ticket_type,
            },
            fail_silently=True,
        )

    def send_reply_notification_to_staff(self, comment: TicketComment) -> bool:
        """
        Send notification to assigned staff when customer replies.

        Args:
            comment: The TicketComment that was just created

        Returns:
            True if email sent successfully
        """
        if not self.ticket.assignee or not self.ticket.assignee.email:
            logger.info(f"No assignee to notify for ticket {self.ticket.id}")
            return False

        # Don't notify if the assignee wrote the comment
        if comment.author == self.ticket.assignee:
            return False

        context = self._get_base_context()
        context['assignee_name'] = self.ticket.assignee.get_full_name() or self.ticket.assignee.email
        context['reply_message'] = comment.comment_text
        context['recipient_name'] = context['assignee_name']

        return send_system_email(
            email_type=EmailType.TICKET_REPLY,
            to_email=self.ticket.assignee.email,
            context=context,
            extra_headers={
                'X-Ticket-ID': str(self.ticket.id),
                'X-Ticket-Type': self.ticket.ticket_type,
            },
            fail_silently=True,
        )

    def send_reply_notification_to_customer(self, comment: TicketComment) -> bool:
        """
        Send notification to customer when staff replies.
        Supports both registered users (creator) and external email senders.

        Args:
            comment: The TicketComment that was just created

        Returns:
            True if email sent successfully
        """
        # Determine recipient email - either from creator or external_email
        recipient_email = None
        recipient_name = None

        if self.ticket.creator and self.ticket.creator.email:
            recipient_email = self.ticket.creator.email
            recipient_name = self.ticket.creator.get_full_name() or self.ticket.creator.email
        elif self.ticket.external_email:
            recipient_email = self.ticket.external_email
            recipient_name = self.ticket.external_name or self.ticket.external_email

        if not recipient_email:
            logger.info(f"No recipient email for ticket {self.ticket.id}")
            return False

        # Don't notify if the customer/external sender wrote the comment
        if comment.author and comment.author == self.ticket.creator:
            return False
        # Also check if comment was from external sender matching ticket's external_email
        if comment.external_author_email and self.ticket.external_email:
            if comment.external_author_email.lower() == self.ticket.external_email.lower():
                return False

        # Don't send internal comments to customers
        if comment.is_internal:
            return False

        context = self._get_base_context()
        context['reply_message'] = comment.comment_text
        context['customer_name'] = recipient_name
        context['recipient_name'] = recipient_name

        return send_system_email(
            email_type=EmailType.TICKET_REPLY,
            to_email=recipient_email,
            context=context,
            extra_headers={
                'X-Ticket-ID': str(self.ticket.id),
                'X-Ticket-Type': self.ticket.ticket_type,
            },
            fail_silently=True,
        )

    def send_resolution_notification(self, resolution_message: str = '') -> bool:
        """
        Send notification when ticket is resolved.
        Supports both registered users (creator) and external email senders.

        Args:
            resolution_message: Summary of the resolution

        Returns:
            True if email sent successfully
        """
        # Determine recipient email - either from creator or external_email
        recipient_email = None
        recipient_name = None

        if self.ticket.creator and self.ticket.creator.email:
            recipient_email = self.ticket.creator.email
            recipient_name = self.ticket.creator.get_full_name() or self.ticket.creator.email
        elif self.ticket.external_email:
            recipient_email = self.ticket.external_email
            recipient_name = self.ticket.external_name or self.ticket.external_email

        if not recipient_email:
            return False

        context = self._get_base_context()
        context['resolution_message'] = resolution_message or 'Your request has been resolved.'
        context['customer_name'] = recipient_name
        context['recipient_name'] = recipient_name

        return send_system_email(
            email_type=EmailType.TICKET_RESOLVED,
            to_email=recipient_email,
            context=context,
            extra_headers={
                'X-Ticket-ID': str(self.ticket.id),
                'X-Ticket-Type': self.ticket.ticket_type,
            },
            fail_silently=True,
        )

# ========== Convenience Functions ==========

def notify_ticket_assigned(ticket: Ticket) -> bool:
    """
    Send notification when a ticket is assigned.

    Args:
        ticket: The Ticket that was assigned

    Returns:
        True if notification sent successfully
    """
    service = TicketEmailService(ticket)
    return service.send_assignment_notification()


def notify_ticket_status_changed(ticket: Ticket, old_status: str) -> bool:
    """
    Send notification when ticket status changes.

    Args:
        ticket: The Ticket with updated status
        old_status: Previous status value

    Returns:
        True if notification sent successfully
    """
    service = TicketEmailService(ticket)
    return service.send_status_change_notification(old_status)


def notify_ticket_reply(ticket: Ticket, comment: TicketComment) -> tuple:
    """
    Send reply notifications to appropriate parties.

    Determines whether to notify staff or customer based on who
    authored the comment.

    Args:
        ticket: The Ticket being replied to
        comment: The TicketComment that was created

    Returns:
        Tuple of (staff_notified, customer_notified)
    """
    service = TicketEmailService(ticket)

    # If comment is from the ticket creator (customer), notify staff
    # If comment is from staff (assignee or other), notify customer
    is_customer_reply = comment.author == ticket.creator

    staff_notified = False
    customer_notified = False

    if is_customer_reply:
        staff_notified = service.send_reply_notification_to_staff(comment)
    else:
        customer_notified = service.send_reply_notification_to_customer(comment)

    return (staff_notified, customer_notified)


def notify_ticket_resolved(ticket: Ticket, resolution_message: str = '') -> bool:
    """
    Send notification when ticket is resolved.

    Args:
        ticket: The resolved Ticket
        resolution_message: Optional resolution summary

    Returns:
        True if notification sent successfully
    """
    service = TicketEmailService(ticket)
    return service.send_resolution_notification(resolution_message)
