"""
Ticket Email Notification Service

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

Uses email templates from the EmailTemplate model with ticket-specific context variables.
"""

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

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 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 EmailTemplate model for customizable templates.
    Falls back to default templates if none configured.
    """

    # Default template names (should match seed_email_templates.py)
    TEMPLATE_TICKET_ASSIGNED = 'Ticket Assigned'
    TEMPLATE_STATUS_CHANGED = 'Ticket Status Changed'
    TEMPLATE_REPLY_STAFF = 'Ticket Reply - Staff Notification'
    TEMPLATE_REPLY_CUSTOMER = 'Ticket Reply - Customer Notification'
    TEMPLATE_RESOLVED = '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_email_template(self, template_name: str):
        """
        Get an email template by name.

        Looks up templates in the schedule app's EmailTemplate model.
        Returns None if template not found.
        """
        try:
            from schedule.models import EmailTemplate
            return EmailTemplate.objects.filter(
                name=template_name,
                scope=EmailTemplate.Scope.BUSINESS
            ).first()
        except Exception as e:
            logger.warning(f"Could not load email template '{template_name}': {e}")
            return None

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

        Returns:
            Dictionary of context variables for template rendering
        """
        # 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
        business_name = self.tenant.name if self.tenant else 'SmoothSchedule Platform'
        business_email = getattr(self.tenant, 'contact_email', '') if self.tenant else settings.DEFAULT_FROM_EMAIL
        business_phone = getattr(self.tenant, 'phone', '') if self.tenant else ''

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

        return {
            # Business context
            'BUSINESS_NAME': business_name,
            'BUSINESS_EMAIL': business_email,
            'BUSINESS_PHONE': business_phone,
            # Customer context
            '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_CUSTOMER_NAME': customer_name,
            'TICKET_URL': ticket_url,
            # Date/time
            'TODAY': timezone.now().strftime('%B %d, %Y'),
            'NOW': timezone.now().strftime('%B %d, %Y at %I:%M %p'),
        }

    def _render_template_variables(self, text: str, context: Dict[str, Any]) -> str:
        """
        Replace {{VARIABLE}} placeholders with actual values.

        Args:
            text: Template text with {{VARIABLE}} placeholders
            context: Dictionary of variable values

        Returns:
            Text with variables replaced
        """
        import re

        def replace_var(match):
            var_name = match.group(1)
            return str(context.get(var_name, match.group(0)))

        return re.sub(r'\{\{(\w+)\}\}', replace_var, text)

    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.

        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']

        # Try to get custom template
        template = self._get_email_template(self.TEMPLATE_TICKET_ASSIGNED)

        if template:
            subject = self._render_template_variables(template.subject, context)
            html_content = self._render_template_variables(template.html_content, context)
            text_content = self._render_template_variables(template.text_content, context)
        else:
            # Fallback to default
            subject = f"[Ticket #{self.ticket.id}] You have been assigned: {self.ticket.subject}"
            text_content = self._get_default_assignment_text(context)
            html_content = ''

        return self._send_email(
            to_email=assignee.email,
            subject=subject,
            html_content=html_content,
            text_content=text_content
        )

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

        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)

        template = self._get_email_template(self.TEMPLATE_STATUS_CHANGED)

        if template:
            subject = self._render_template_variables(template.subject, context)
            html_content = self._render_template_variables(template.html_content, context)
            text_content = self._render_template_variables(template.text_content, context)
        else:
            subject = f"[Ticket #{self.ticket.id}] Status updated: {self.ticket.get_status_display()}"
            text_content = self._get_default_status_change_text(context)
            html_content = ''

        return self._send_email(
            to_email=self.ticket.creator.email,
            subject=subject,
            html_content=html_content,
            text_content=text_content
        )

    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

        template = self._get_email_template(self.TEMPLATE_REPLY_STAFF)

        if template:
            subject = self._render_template_variables(template.subject, context)
            html_content = self._render_template_variables(template.html_content, context)
            text_content = self._render_template_variables(template.text_content, context)
        else:
            subject = f"[Ticket #{self.ticket.id}] New reply from customer: {self.ticket.subject}"
            text_content = self._get_default_reply_staff_text(context)
            html_content = ''

        return self._send_email(
            to_email=self.ticket.assignee.email,
            subject=subject,
            html_content=html_content,
            text_content=text_content
        )

    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

        template = self._get_email_template(self.TEMPLATE_REPLY_CUSTOMER)

        if template:
            subject = self._render_template_variables(template.subject, context)
            html_content = self._render_template_variables(template.html_content, context)
            text_content = self._render_template_variables(template.text_content, context)
        else:
            business_name = context['BUSINESS_NAME']
            subject = f"[Ticket #{self.ticket.id}] {business_name} has responded to your request"
            text_content = self._get_default_reply_customer_text(context)
            html_content = ''

        return self._send_email(
            to_email=recipient_email,
            subject=subject,
            html_content=html_content,
            text_content=text_content
        )

    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

        if self.ticket.creator and self.ticket.creator.email:
            recipient_email = self.ticket.creator.email
        elif self.ticket.external_email:
            recipient_email = 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.'

        template = self._get_email_template(self.TEMPLATE_RESOLVED)

        if template:
            subject = self._render_template_variables(template.subject, context)
            html_content = self._render_template_variables(template.html_content, context)
            text_content = self._render_template_variables(template.text_content, context)
        else:
            subject = f"[Ticket #{self.ticket.id}] Your request has been resolved"
            text_content = self._get_default_resolution_text(context)
            html_content = ''

        return self._send_email(
            to_email=recipient_email,
            subject=subject,
            html_content=html_content,
            text_content=text_content
        )

    # ========== Default Text Templates (fallback) ==========

    def _get_default_assignment_text(self, context: Dict[str, Any]) -> str:
        return f"""New Ticket Assigned to You

Hi {context['ASSIGNEE_NAME']},

A ticket has been assigned to you and requires your attention.

TICKET DETAILS
--------------
Ticket: #{context['TICKET_ID']}
Subject: {context['TICKET_SUBJECT']}
Priority: {context['TICKET_PRIORITY']}
From: {context['TICKET_CUSTOMER_NAME']}

Message:
{context['TICKET_MESSAGE']}

View ticket: {context['TICKET_URL']}

Please respond as soon as possible.

---
{context['BUSINESS_NAME']}
"""

    def _get_default_status_change_text(self, context: Dict[str, Any]) -> str:
        return f"""Ticket Status Updated

Hi {context['RECIPIENT_NAME']},

The status of ticket #{context['TICKET_ID']} has been updated.

TICKET DETAILS
--------------
Ticket: #{context['TICKET_ID']}
Subject: {context['TICKET_SUBJECT']}
New Status: {context['TICKET_STATUS']}

View ticket: {context['TICKET_URL']}

---
{context['BUSINESS_NAME']}
"""

    def _get_default_reply_staff_text(self, context: Dict[str, Any]) -> str:
        return f"""New Reply on Ticket #{context['TICKET_ID']}

Hi {context['ASSIGNEE_NAME']},

{context['TICKET_CUSTOMER_NAME']} has replied to ticket #{context['TICKET_ID']}.

Subject: {context['TICKET_SUBJECT']}

Reply:
{context['REPLY_MESSAGE']}

View & reply: {context['TICKET_URL']}

---
{context['BUSINESS_NAME']}
"""

    def _get_default_reply_customer_text(self, context: Dict[str, Any]) -> str:
        return f"""We've Responded to Your Request

Hi {context['CUSTOMER_NAME']},

We've replied to your support request.

TICKET DETAILS
--------------
Ticket: #{context['TICKET_ID']}
Subject: {context['TICKET_SUBJECT']}

Our Response:
{context['REPLY_MESSAGE']}

Need to reply?
Simply reply to this email or visit: {context['TICKET_URL']}

Thank you for contacting us!
The {context['BUSINESS_NAME']} Team

---
{context['BUSINESS_NAME']}
{context['BUSINESS_EMAIL']}
{context['BUSINESS_PHONE']}
"""

    def _get_default_resolution_text(self, context: Dict[str, Any]) -> str:
        return f"""Your Request Has Been Resolved

Hi {context['CUSTOMER_NAME']},

Great news! Your support request has been resolved.

Ticket #{context['TICKET_ID']} - RESOLVED

Subject: {context['TICKET_SUBJECT']}
Resolution: {context['RESOLUTION_MESSAGE']}

Not satisfied with the resolution?
You can reopen this ticket by replying to this email within the next 7 days.

View ticket history: {context['TICKET_URL']}

Thank you for your patience!
The {context['BUSINESS_NAME']} Team

---
{context['BUSINESS_NAME']}
{context['BUSINESS_EMAIL']}
{context['BUSINESS_PHONE']}
"""


# ========== 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)
