"""
Unit tests for Ticket Email Notification Service

Tests all notification functions with mocked dependencies.
No database access - all models and dependencies are mocked.
"""

import re
import smtplib
from unittest.mock import Mock, patch, MagicMock, call
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

import pytest
from django.conf import settings
from django.core.mail import EmailMultiAlternatives
from django.utils import timezone

from ..email_notifications import (
    get_default_platform_email,
    TicketEmailService,
    notify_ticket_assigned,
    notify_ticket_status_changed,
    notify_ticket_reply,
    notify_ticket_resolved,
)


# ========== Fixtures ==========

@pytest.fixture
def mock_ticket():
    """Create a mock Ticket instance with common attributes."""
    ticket = Mock()
    ticket.id = 123
    ticket.subject = "Test Ticket Subject"
    ticket.description = "Test ticket description"
    ticket.status = "OPEN"
    ticket.get_status_display.return_value = "Open"
    ticket.priority = "MEDIUM"
    ticket.get_priority_display.return_value = "Medium"
    ticket.ticket_type = "CUSTOMER"
    ticket.external_email = None
    ticket.external_name = ""

    # Mock tenant
    ticket.tenant = Mock()
    ticket.tenant.name = "Test Business"
    ticket.tenant.contact_email = "support@testbusiness.com"
    ticket.tenant.phone = "555-1234"

    # Mock creator
    ticket.creator = Mock()
    ticket.creator.email = "customer@example.com"
    ticket.creator.get_full_name.return_value = "John Customer"

    # Mock assignee
    ticket.assignee = None

    return ticket


@pytest.fixture
def mock_platform_ticket():
    """Create a mock platform-level Ticket (no tenant)."""
    ticket = Mock()
    ticket.id = 456
    ticket.subject = "Platform Support Request"
    ticket.description = "Platform issue description"
    ticket.status = "OPEN"
    ticket.get_status_display.return_value = "Open"
    ticket.priority = "HIGH"
    ticket.get_priority_display.return_value = "High"
    ticket.ticket_type = "PLATFORM"
    ticket.external_email = None
    ticket.external_name = ""
    ticket.tenant = None

    ticket.creator = Mock()
    ticket.creator.email = "owner@business.com"
    ticket.creator.get_full_name.return_value = "Business Owner"

    return ticket


@pytest.fixture
def mock_comment():
    """Create a mock TicketComment instance."""
    comment = Mock()
    comment.comment_text = "This is a test reply"
    comment.is_internal = False
    comment.external_author_email = None

    # Mock author
    comment.author = Mock()
    comment.author.email = "staff@business.com"
    comment.author.get_full_name.return_value = "Staff Member"

    return comment


@pytest.fixture
def mock_external_comment():
    """Create a mock TicketComment from external email."""
    comment = Mock()
    comment.comment_text = "External reply message"
    comment.is_internal = False
    comment.author = None
    comment.external_author_email = "external@example.com"

    return comment


# ========== Test get_default_platform_email ==========

@patch('smoothschedule.commerce.tickets.email_notifications.logger')
def test_get_default_platform_email_success(mock_logger):
    """Test successfully getting default platform email."""
    mock_platform_email = Mock()
    mock_platform_email.email_address = "platform@smoothschedule.com"

    with patch('smoothschedule.platform.admin.models.PlatformEmailAddress') as MockPlatformEmail:
        MockPlatformEmail.objects.filter.return_value.first.return_value = mock_platform_email

        result = get_default_platform_email()

        assert result == mock_platform_email
        MockPlatformEmail.objects.filter.assert_called_once_with(
            is_default=True,
            is_active=True,
            mail_server_synced=True
        )


@patch('smoothschedule.commerce.tickets.email_notifications.logger')
def test_get_default_platform_email_none_found(mock_logger):
    """Test when no default platform email is configured."""
    with patch('smoothschedule.platform.admin.models.PlatformEmailAddress') as MockPlatformEmail:
        MockPlatformEmail.objects.filter.return_value.first.return_value = None

        result = get_default_platform_email()

        assert result is None


@patch('smoothschedule.commerce.tickets.email_notifications.logger')
def test_get_default_platform_email_exception(mock_logger):
    """Test exception handling when getting platform email fails."""
    with patch('smoothschedule.platform.admin.models.PlatformEmailAddress') as MockPlatformEmail:
        MockPlatformEmail.objects.filter.side_effect = Exception("Database error")

        result = get_default_platform_email()

        assert result is None
        mock_logger.warning.assert_called_once()
        assert "Could not get default platform email" in str(mock_logger.warning.call_args)


# ========== Test TicketEmailService Initialization ==========

def test_ticket_email_service_init(mock_ticket):
    """Test service initialization with ticket."""
    service = TicketEmailService(mock_ticket)

    assert service.ticket == mock_ticket
    assert service.tenant == mock_ticket.tenant


def test_ticket_email_service_init_platform_ticket(mock_platform_ticket):
    """Test service initialization with platform ticket (no tenant)."""
    service = TicketEmailService(mock_platform_ticket)

    assert service.ticket == mock_platform_ticket
    assert service.tenant is None


# ========== Test _get_email_template ==========

def test_get_email_template_success(mock_ticket):
    """Test successfully retrieving email template."""
    service = TicketEmailService(mock_ticket)

    mock_template = Mock()
    mock_template.name = "Ticket Assigned"
    mock_template.subject = "Test Subject"

    with patch('smoothschedule.scheduling.schedule.models.EmailTemplate') as MockEmailTemplate:
        MockEmailTemplate.objects.filter.return_value.first.return_value = mock_template
        MockEmailTemplate.Scope.BUSINESS = "BUSINESS"

        result = service._get_email_template("Ticket Assigned")

        assert result == mock_template
        MockEmailTemplate.objects.filter.assert_called_once()


@patch('smoothschedule.commerce.tickets.email_notifications.logger')
def test_get_email_template_not_found(mock_logger, mock_ticket):
    """Test when template is not found."""
    service = TicketEmailService(mock_ticket)

    with patch('smoothschedule.scheduling.schedule.models.EmailTemplate') as MockEmailTemplate:
        MockEmailTemplate.objects.filter.return_value.first.return_value = None
        MockEmailTemplate.Scope.BUSINESS = "BUSINESS"

        result = service._get_email_template("NonExistent Template")

        assert result is None


@patch('smoothschedule.commerce.tickets.email_notifications.logger')
def test_get_email_template_exception(mock_logger, mock_ticket):
    """Test exception handling when getting template fails."""
    service = TicketEmailService(mock_ticket)

    with patch('smoothschedule.scheduling.schedule.models.EmailTemplate') as MockEmailTemplate:
        MockEmailTemplate.objects.filter.side_effect = Exception("Import error")

        result = service._get_email_template("Ticket Assigned")

        assert result is None
        mock_logger.warning.assert_called_once()
        assert "Could not load email template" in str(mock_logger.warning.call_args)


# ========== Test _get_base_context ==========

@patch('smoothschedule.commerce.tickets.email_notifications.timezone')
def test_get_base_context_with_tenant(mock_timezone, mock_ticket):
    """Test base context generation for tenant ticket."""
    mock_now = Mock()
    mock_now.strftime.side_effect = lambda fmt: {
        '%B %d, %Y': 'January 15, 2024',
        '%B %d, %Y at %I:%M %p': 'January 15, 2024 at 10:30 AM'
    }[fmt]
    mock_timezone.now.return_value = mock_now

    service = TicketEmailService(mock_ticket)

    with patch.object(settings, 'FRONTEND_URL', 'http://test.lvh.me:5173'):
        context = service._get_base_context()

    assert context['BUSINESS_NAME'] == "Test Business"
    assert context['BUSINESS_EMAIL'] == "support@testbusiness.com"
    assert context['BUSINESS_PHONE'] == "555-1234"
    assert context['CUSTOMER_NAME'] == "John Customer"
    assert context['CUSTOMER_EMAIL'] == "customer@example.com"
    assert context['TICKET_ID'] == "123"
    assert context['TICKET_SUBJECT'] == "Test Ticket Subject"
    assert context['TICKET_MESSAGE'] == "Test ticket description"
    assert context['TICKET_STATUS'] == "Open"
    assert context['TICKET_PRIORITY'] == "Medium"
    assert context['TICKET_URL'] == "http://test.lvh.me:5173/tickets/123"
    assert context['TODAY'] == 'January 15, 2024'
    assert context['NOW'] == 'January 15, 2024 at 10:30 AM'


@patch('smoothschedule.commerce.tickets.email_notifications.timezone')
def test_get_base_context_platform_ticket(mock_timezone, mock_platform_ticket):
    """Test base context for platform-level ticket (no tenant)."""
    mock_now = Mock()
    mock_now.strftime.side_effect = lambda fmt: {
        '%B %d, %Y': 'January 15, 2024',
        '%B %d, %Y at %I:%M %p': 'January 15, 2024 at 10:30 AM'
    }[fmt]
    mock_timezone.now.return_value = mock_now

    service = TicketEmailService(mock_platform_ticket)

    with patch.object(settings, 'FRONTEND_URL', 'http://platform.lvh.me:5173'):
        with patch.object(settings, 'DEFAULT_FROM_EMAIL', 'noreply@smoothschedule.com'):
            context = service._get_base_context()

    assert context['BUSINESS_NAME'] == "SmoothSchedule Platform"
    assert context['BUSINESS_EMAIL'] == "noreply@smoothschedule.com"
    assert context['BUSINESS_PHONE'] == ""
    assert context['TICKET_URL'] == "http://platform.lvh.me:5173/platform/tickets/456"


@patch('smoothschedule.commerce.tickets.email_notifications.timezone')
def test_get_base_context_no_creator(mock_timezone, mock_ticket):
    """Test base context when ticket has no creator."""
    mock_now = Mock()
    mock_now.strftime.return_value = 'January 15, 2024'
    mock_timezone.now.return_value = mock_now

    mock_ticket.creator = None
    service = TicketEmailService(mock_ticket)

    context = service._get_base_context()

    assert context['CUSTOMER_NAME'] == "Customer"
    assert context['CUSTOMER_EMAIL'] == ""


# ========== Test _render_template_variables ==========

def test_render_template_variables(mock_ticket):
    """Test variable replacement in templates."""
    service = TicketEmailService(mock_ticket)

    template = "Hello {{CUSTOMER_NAME}}, ticket #{{TICKET_ID}} status: {{TICKET_STATUS}}"
    context = {
        'CUSTOMER_NAME': 'John',
        'TICKET_ID': '123',
        'TICKET_STATUS': 'Open'
    }

    result = service._render_template_variables(template, context)

    assert result == "Hello John, ticket #123 status: Open"


def test_render_template_variables_missing_key(mock_ticket):
    """Test variable replacement with missing context key."""
    service = TicketEmailService(mock_ticket)

    template = "Hello {{CUSTOMER_NAME}}, unknown: {{UNKNOWN_VAR}}"
    context = {'CUSTOMER_NAME': 'John'}

    result = service._render_template_variables(template, context)

    assert result == "Hello John, unknown: {{UNKNOWN_VAR}}"


def test_render_template_variables_multiple_occurrences(mock_ticket):
    """Test replacing multiple occurrences of same variable."""
    service = TicketEmailService(mock_ticket)

    template = "{{NAME}} said hi. {{NAME}} is happy. {{NAME}}!"
    context = {'NAME': 'Alice'}

    result = service._render_template_variables(template, context)

    assert result == "Alice said hi. Alice is happy. Alice!"


# ========== Test _send_email (Django backend) ==========

@patch('smoothschedule.commerce.tickets.email_notifications.logger')
@patch('smoothschedule.commerce.tickets.email_notifications.EmailMultiAlternatives')
def test_send_email_success_with_tenant(MockEmailMulti, mock_logger, mock_ticket):
    """Test sending email via Django backend for tenant ticket."""
    service = TicketEmailService(mock_ticket)

    mock_msg = Mock()
    MockEmailMulti.return_value = mock_msg

    with patch.object(settings, 'DEFAULT_FROM_EMAIL', 'noreply@test.com', create=True):
        with patch.object(settings, 'SUPPORT_EMAIL_DOMAIN', 'test.com', create=True):
            result = service._send_email(
                to_email="recipient@example.com",
                subject="Test Subject",
                html_content="<p>HTML content</p>",
                text_content="Text content"
            )

    assert result is True
    MockEmailMulti.assert_called_once_with(
        subject="Test Subject",
        body="Text content",
        from_email="noreply@test.com",
        to=["recipient@example.com"]
    )
    mock_msg.attach_alternative.assert_called_once_with("<p>HTML content</p>", 'text/html')
    assert mock_msg.reply_to == ["support+ticket-123@test.com"]
    assert mock_msg.extra_headers['X-Ticket-ID'] == "123"
    assert mock_msg.extra_headers['X-Ticket-Type'] == "CUSTOMER"
    mock_msg.send.assert_called_once_with(fail_silently=False)
    mock_logger.info.assert_called_once()


@patch('smoothschedule.commerce.tickets.email_notifications.logger')
@patch('smoothschedule.commerce.tickets.email_notifications.EmailMultiAlternatives')
def test_send_email_no_recipient(MockEmailMulti, mock_logger, mock_ticket):
    """Test sending email fails when no recipient."""
    service = TicketEmailService(mock_ticket)

    result = service._send_email(
        to_email="",
        subject="Test",
        html_content="",
        text_content="Test"
    )

    assert result is False
    mock_logger.warning.assert_called_once()
    assert "no recipient address" in str(mock_logger.warning.call_args)
    MockEmailMulti.assert_not_called()


@patch('smoothschedule.commerce.tickets.email_notifications.logger')
@patch('smoothschedule.commerce.tickets.email_notifications.EmailMultiAlternatives')
def test_send_email_exception(MockEmailMulti, mock_logger, mock_ticket):
    """Test exception handling during email send."""
    service = TicketEmailService(mock_ticket)

    mock_msg = Mock()
    mock_msg.send.side_effect = Exception("SMTP error")
    MockEmailMulti.return_value = mock_msg

    with patch.object(settings, 'DEFAULT_FROM_EMAIL', 'noreply@test.com'):
        result = service._send_email(
            to_email="recipient@example.com",
            subject="Test",
            html_content="",
            text_content="Test"
        )

    assert result is False
    mock_logger.error.assert_called_once()
    assert "Failed to send ticket email" in str(mock_logger.error.call_args)


@patch('smoothschedule.commerce.tickets.email_notifications.logger')
@patch('smoothschedule.commerce.tickets.email_notifications.EmailMultiAlternatives')
def test_send_email_custom_reply_to(MockEmailMulti, mock_logger, mock_ticket):
    """Test sending email with custom reply-to address."""
    service = TicketEmailService(mock_ticket)

    mock_msg = Mock()
    MockEmailMulti.return_value = mock_msg

    with patch.object(settings, 'DEFAULT_FROM_EMAIL', 'noreply@test.com'):
        result = service._send_email(
            to_email="recipient@example.com",
            subject="Test",
            html_content="",
            text_content="Test",
            reply_to="custom@reply.com"
        )

    assert result is True
    assert mock_msg.reply_to == ["custom@reply.com"]


@patch('smoothschedule.commerce.tickets.email_notifications.logger')
@patch('smoothschedule.commerce.tickets.email_notifications.EmailMultiAlternatives')
def test_send_email_no_html_content(MockEmailMulti, mock_logger, mock_ticket):
    """Test sending email without HTML content."""
    service = TicketEmailService(mock_ticket)

    mock_msg = Mock()
    MockEmailMulti.return_value = mock_msg

    with patch.object(settings, 'DEFAULT_FROM_EMAIL', 'noreply@test.com'):
        result = service._send_email(
            to_email="recipient@example.com",
            subject="Test",
            html_content="",
            text_content="Text only"
        )

    assert result is True
    mock_msg.attach_alternative.assert_not_called()


@patch('smoothschedule.commerce.tickets.email_notifications.logger')
def test_send_email_uses_fallback_domain_when_no_settings(mock_logger, mock_ticket):
    """Test reply-to domain falls back to settings.SUPPORT_EMAIL_DOMAIN."""
    service = TicketEmailService(mock_ticket)

    with patch('smoothschedule.commerce.tickets.email_notifications.EmailMultiAlternatives') as MockEmailMulti:
        mock_msg = Mock()
        MockEmailMulti.return_value = mock_msg

        with patch.object(settings, 'DEFAULT_FROM_EMAIL', 'noreply@test.com', create=True):
            with patch.object(settings, 'SUPPORT_EMAIL_DOMAIN', 'smoothschedule.com', create=True):
                service._send_email(
                    to_email="test@example.com",
                    subject="Test",
                    html_content="",
                    text_content="Test"
                )

            # Should use the fallback domain since TicketEmailSettings doesn't exist
            assert mock_msg.reply_to == ["support+ticket-123@smoothschedule.com"]


# ========== Test _send_email (Platform SMTP) ==========

@patch('smoothschedule.commerce.tickets.email_notifications.get_default_platform_email')
@patch('smoothschedule.commerce.tickets.email_notifications.logger')
def test_send_email_platform_ticket_uses_platform_smtp(mock_logger, mock_get_platform_email, mock_platform_ticket):
    """Test platform ticket uses platform SMTP if available."""
    service = TicketEmailService(mock_platform_ticket)

    mock_platform_email = Mock()
    mock_get_platform_email.return_value = mock_platform_email

    with patch.object(service, '_send_via_platform_smtp', return_value=True) as mock_platform_smtp:
        result = service._send_email(
            to_email="recipient@example.com",
            subject="Platform Test",
            html_content="<p>HTML</p>",
            text_content="Text"
        )

    assert result is True
    mock_platform_smtp.assert_called_once_with(
        platform_email=mock_platform_email,
        to_email="recipient@example.com",
        subject="Platform Test",
        html_content="<p>HTML</p>",
        text_content="Text",
        reply_to="support+ticket-456@smoothschedule.com"
    )


@patch('smoothschedule.commerce.tickets.email_notifications.get_default_platform_email')
@patch('smoothschedule.commerce.tickets.email_notifications.EmailMultiAlternatives')
def test_send_email_platform_ticket_falls_back_to_django(MockEmailMulti, mock_get_platform_email, mock_platform_ticket):
    """Test platform ticket falls back to Django backend if no platform email."""
    service = TicketEmailService(mock_platform_ticket)

    mock_get_platform_email.return_value = None
    mock_msg = Mock()
    MockEmailMulti.return_value = mock_msg

    with patch.object(settings, 'DEFAULT_FROM_EMAIL', 'noreply@test.com'):
        result = service._send_email(
            to_email="recipient@example.com",
            subject="Platform Test",
            html_content="",
            text_content="Text"
        )

    assert result is True
    MockEmailMulti.assert_called_once()


# ========== Test _send_via_platform_smtp ==========

@patch('smoothschedule.commerce.tickets.email_notifications.logger')
@patch('smoothschedule.commerce.tickets.email_notifications.smtplib.SMTP_SSL')
def test_send_via_platform_smtp_ssl_success(MockSMTP_SSL, mock_logger, mock_ticket):
    """Test sending via platform SMTP with SSL."""
    service = TicketEmailService(mock_ticket)

    mock_platform_email = Mock()
    mock_platform_email.email_address = "platform@smoothschedule.com"
    mock_platform_email.effective_sender_name = "SmoothSchedule Support"
    mock_platform_email.get_smtp_settings.return_value = {
        'host': 'smtp.example.com',
        'port': 465,
        'username': 'user@example.com',
        'password': 'secret',
        'use_ssl': True,
        'use_tls': False
    }

    mock_server = Mock()
    MockSMTP_SSL.return_value = mock_server

    result = service._send_via_platform_smtp(
        platform_email=mock_platform_email,
        to_email="recipient@example.com",
        subject="Test Subject",
        html_content="<p>HTML</p>",
        text_content="Plain text",
        reply_to="reply@example.com"
    )

    assert result is True
    MockSMTP_SSL.assert_called_once_with('smtp.example.com', 465)
    mock_server.login.assert_called_once_with('user@example.com', 'secret')
    mock_server.sendmail.assert_called_once()
    mock_server.quit.assert_called_once()
    mock_logger.info.assert_called_once()


@patch('smoothschedule.commerce.tickets.email_notifications.logger')
@patch('smoothschedule.commerce.tickets.email_notifications.smtplib.SMTP')
def test_send_via_platform_smtp_tls_success(MockSMTP, mock_logger, mock_ticket):
    """Test sending via platform SMTP with TLS."""
    service = TicketEmailService(mock_ticket)

    mock_platform_email = Mock()
    mock_platform_email.email_address = "platform@smoothschedule.com"
    mock_platform_email.effective_sender_name = "Support"
    mock_platform_email.get_smtp_settings.return_value = {
        'host': 'smtp.example.com',
        'port': 587,
        'username': 'user@example.com',
        'password': 'secret',
        'use_ssl': False,
        'use_tls': True
    }

    mock_server = Mock()
    MockSMTP.return_value = mock_server

    result = service._send_via_platform_smtp(
        platform_email=mock_platform_email,
        to_email="recipient@example.com",
        subject="Test",
        html_content="",
        text_content="Plain text",
        reply_to="reply@example.com"
    )

    assert result is True
    MockSMTP.assert_called_once_with('smtp.example.com', 587)
    mock_server.starttls.assert_called_once()
    mock_server.login.assert_called_once()


@patch('smoothschedule.commerce.tickets.email_notifications.logger')
@patch('smoothschedule.commerce.tickets.email_notifications.smtplib.SMTP')
def test_send_via_platform_smtp_no_ssl_no_tls(MockSMTP, mock_logger, mock_ticket):
    """Test sending via platform SMTP without SSL/TLS."""
    service = TicketEmailService(mock_ticket)

    mock_platform_email = Mock()
    mock_platform_email.email_address = "platform@smoothschedule.com"
    mock_platform_email.effective_sender_name = "Support"
    mock_platform_email.get_smtp_settings.return_value = {
        'host': 'smtp.example.com',
        'port': 25,
        'username': 'user@example.com',
        'password': 'secret',
        'use_ssl': False,
        'use_tls': False
    }

    mock_server = Mock()
    MockSMTP.return_value = mock_server

    result = service._send_via_platform_smtp(
        platform_email=mock_platform_email,
        to_email="recipient@example.com",
        subject="Test",
        html_content="",
        text_content="Plain",
        reply_to="reply@example.com"
    )

    assert result is True
    mock_server.starttls.assert_not_called()


@patch('smoothschedule.commerce.tickets.email_notifications.logger')
@patch('smoothschedule.commerce.tickets.email_notifications.smtplib.SMTP_SSL')
def test_send_via_platform_smtp_exception(MockSMTP_SSL, mock_logger, mock_ticket):
    """Test exception handling in platform SMTP send."""
    service = TicketEmailService(mock_ticket)

    mock_platform_email = Mock()
    mock_platform_email.email_address = "platform@smoothschedule.com"
    mock_platform_email.effective_sender_name = "Support"
    mock_platform_email.get_smtp_settings.return_value = {
        'host': 'smtp.example.com',
        'port': 465,
        'username': 'user',
        'password': 'pass',
        'use_ssl': True,
        'use_tls': False
    }

    MockSMTP_SSL.side_effect = smtplib.SMTPException("Connection failed")

    result = service._send_via_platform_smtp(
        platform_email=mock_platform_email,
        to_email="recipient@example.com",
        subject="Test",
        html_content="",
        text_content="Plain",
        reply_to="reply@example.com"
    )

    assert result is False
    mock_logger.error.assert_called_once()
    assert "Failed to send platform email" in str(mock_logger.error.call_args)


# ========== Test send_assignment_notification ==========

@patch('smoothschedule.commerce.tickets.email_notifications.logger')
def test_send_assignment_notification_no_assignee(mock_logger, mock_ticket):
    """Test assignment notification when ticket has no assignee."""
    service = TicketEmailService(mock_ticket)

    result = service.send_assignment_notification()

    assert result is False
    mock_logger.warning.assert_called_once()
    assert "has no assignee" in str(mock_logger.warning.call_args)


@patch('smoothschedule.commerce.tickets.email_notifications.logger')
def test_send_assignment_notification_no_email(mock_logger, mock_ticket):
    """Test assignment notification when assignee has no email."""
    mock_ticket.assignee = Mock()
    mock_ticket.assignee.email = ""
    mock_ticket.assignee.id = 99

    service = TicketEmailService(mock_ticket)

    result = service.send_assignment_notification()

    assert result is False
    mock_logger.warning.assert_called_once()
    assert "has no email address" in str(mock_logger.warning.call_args)


def test_send_assignment_notification_with_template(mock_ticket):
    """Test assignment notification using email template."""
    mock_ticket.assignee = Mock()
    mock_ticket.assignee.email = "staff@business.com"
    mock_ticket.assignee.get_full_name.return_value = "Staff Member"

    service = TicketEmailService(mock_ticket)

    mock_template = Mock()
    mock_template.subject = "Assigned: {{TICKET_SUBJECT}}"
    mock_template.html_content = "<p>Hi {{ASSIGNEE_NAME}}</p>"
    mock_template.text_content = "Hi {{ASSIGNEE_NAME}}"

    with patch.object(service, '_get_email_template', return_value=mock_template):
        with patch.object(service, '_send_email', return_value=True) as mock_send:
            result = service.send_assignment_notification()

    assert result is True
    mock_send.assert_called_once()
    call_args = mock_send.call_args
    assert call_args[1]['to_email'] == "staff@business.com"
    assert "Test Ticket Subject" in call_args[1]['subject']
    assert "Staff Member" in call_args[1]['text_content']


def test_send_assignment_notification_fallback(mock_ticket):
    """Test assignment notification using fallback template."""
    mock_ticket.assignee = Mock()
    mock_ticket.assignee.email = "staff@business.com"
    mock_ticket.assignee.get_full_name.return_value = "Staff Member"

    service = TicketEmailService(mock_ticket)

    with patch.object(service, '_get_email_template', return_value=None):
        with patch.object(service, '_send_email', return_value=True) as mock_send:
            result = service.send_assignment_notification()

    assert result is True
    call_args = mock_send.call_args
    assert "[Ticket #123]" in call_args[1]['subject']
    assert "You have been assigned" in call_args[1]['subject']
    assert call_args[1]['html_content'] == ''


# ========== Test send_status_change_notification ==========

def test_send_status_change_notification_no_customer(mock_ticket):
    """Test status change notification when no creator."""
    mock_ticket.creator = None
    service = TicketEmailService(mock_ticket)

    result = service.send_status_change_notification(old_status="OPEN", notify_customer=True)

    assert result is False


def test_send_status_change_notification_no_email(mock_ticket):
    """Test status change notification when creator has no email."""
    mock_ticket.creator.email = ""
    service = TicketEmailService(mock_ticket)

    result = service.send_status_change_notification(old_status="OPEN", notify_customer=True)

    assert result is False


def test_send_status_change_notification_notify_false(mock_ticket):
    """Test status change notification when notify_customer=False."""
    service = TicketEmailService(mock_ticket)

    result = service.send_status_change_notification(old_status="OPEN", notify_customer=False)

    assert result is False


def test_send_status_change_notification_with_template(mock_ticket):
    """Test status change notification with template."""
    service = TicketEmailService(mock_ticket)

    mock_template = Mock()
    mock_template.subject = "Status: {{TICKET_STATUS}}"
    mock_template.html_content = "<p>Changed from {{OLD_STATUS}} to {{TICKET_STATUS}}</p>"
    mock_template.text_content = "Changed from {{OLD_STATUS}} to {{TICKET_STATUS}}"

    with patch.object(service, '_get_email_template', return_value=mock_template):
        with patch.object(service, '_send_email', return_value=True) as mock_send:
            result = service.send_status_change_notification(old_status="OPEN", notify_customer=True)

    assert result is True
    call_args = mock_send.call_args
    assert call_args[1]['to_email'] == "customer@example.com"
    assert "Open" in call_args[1]['subject']


def test_send_status_change_notification_fallback(mock_ticket):
    """Test status change notification with fallback template."""
    service = TicketEmailService(mock_ticket)

    with patch.object(service, '_get_email_template', return_value=None):
        with patch.object(service, '_send_email', return_value=True) as mock_send:
            result = service.send_status_change_notification(old_status="OPEN", notify_customer=True)

    assert result is True
    call_args = mock_send.call_args
    assert "[Ticket #123]" in call_args[1]['subject']
    assert "Status updated" in call_args[1]['subject']


# ========== Test send_reply_notification_to_staff ==========

@patch('smoothschedule.commerce.tickets.email_notifications.logger')
def test_send_reply_notification_to_staff_no_assignee(mock_logger, mock_ticket, mock_comment):
    """Test staff notification when ticket has no assignee."""
    service = TicketEmailService(mock_ticket)

    result = service.send_reply_notification_to_staff(mock_comment)

    assert result is False
    mock_logger.info.assert_called_once()


def test_send_reply_notification_to_staff_assignee_is_author(mock_ticket, mock_comment):
    """Test staff notification when assignee wrote the comment."""
    mock_ticket.assignee = mock_comment.author
    service = TicketEmailService(mock_ticket)

    result = service.send_reply_notification_to_staff(mock_comment)

    assert result is False


def test_send_reply_notification_to_staff_success(mock_ticket, mock_comment):
    """Test successful staff notification."""
    mock_ticket.assignee = Mock()
    mock_ticket.assignee.email = "assignee@business.com"
    mock_ticket.assignee.get_full_name.return_value = "Staff Assignee"

    service = TicketEmailService(mock_ticket)

    mock_template = Mock()
    mock_template.subject = "New reply on {{TICKET_ID}}"
    mock_template.html_content = "<p>{{REPLY_MESSAGE}}</p>"
    mock_template.text_content = "{{REPLY_MESSAGE}}"

    with patch.object(service, '_get_email_template', return_value=mock_template):
        with patch.object(service, '_send_email', return_value=True) as mock_send:
            result = service.send_reply_notification_to_staff(mock_comment)

    assert result is True
    call_args = mock_send.call_args
    assert call_args[1]['to_email'] == "assignee@business.com"
    assert "This is a test reply" in call_args[1]['text_content']


def test_send_reply_notification_to_staff_fallback(mock_ticket, mock_comment):
    """Test staff notification with fallback template."""
    mock_ticket.assignee = Mock()
    mock_ticket.assignee.email = "assignee@business.com"
    mock_ticket.assignee.get_full_name.return_value = "Staff Assignee"

    service = TicketEmailService(mock_ticket)

    with patch.object(service, '_get_email_template', return_value=None):
        with patch.object(service, '_send_email', return_value=True) as mock_send:
            result = service.send_reply_notification_to_staff(mock_comment)

    assert result is True
    call_args = mock_send.call_args
    assert "[Ticket #123]" in call_args[1]['subject']
    assert "New reply from customer" in call_args[1]['subject']


# ========== Test send_reply_notification_to_customer ==========

@patch('smoothschedule.commerce.tickets.email_notifications.logger')
def test_send_reply_notification_to_customer_no_recipient(mock_logger, mock_ticket, mock_comment):
    """Test customer notification when no recipient email."""
    mock_ticket.creator = None
    mock_ticket.external_email = None

    service = TicketEmailService(mock_ticket)

    result = service.send_reply_notification_to_customer(mock_comment)

    assert result is False
    mock_logger.info.assert_called_once()


def test_send_reply_notification_to_customer_creator_is_author(mock_ticket, mock_comment):
    """Test customer notification when creator wrote the comment."""
    mock_comment.author = mock_ticket.creator

    service = TicketEmailService(mock_ticket)

    result = service.send_reply_notification_to_customer(mock_comment)

    assert result is False


def test_send_reply_notification_to_customer_external_is_author(mock_ticket, mock_external_comment):
    """Test customer notification when external sender wrote the comment."""
    mock_ticket.external_email = "external@example.com"
    mock_external_comment.external_author_email = "external@example.com"

    service = TicketEmailService(mock_ticket)

    result = service.send_reply_notification_to_customer(mock_external_comment)

    assert result is False


def test_send_reply_notification_to_customer_internal_comment(mock_ticket, mock_comment):
    """Test customer notification skips internal comments."""
    mock_comment.is_internal = True

    service = TicketEmailService(mock_ticket)

    result = service.send_reply_notification_to_customer(mock_comment)

    assert result is False


def test_send_reply_notification_to_customer_success(mock_ticket, mock_comment):
    """Test successful customer notification."""
    service = TicketEmailService(mock_ticket)

    mock_template = Mock()
    mock_template.subject = "Response to {{TICKET_ID}}"
    mock_template.html_content = "<p>{{REPLY_MESSAGE}}</p>"
    mock_template.text_content = "{{REPLY_MESSAGE}}"

    with patch.object(service, '_get_email_template', return_value=mock_template):
        with patch.object(service, '_send_email', return_value=True) as mock_send:
            result = service.send_reply_notification_to_customer(mock_comment)

    assert result is True
    call_args = mock_send.call_args
    assert call_args[1]['to_email'] == "customer@example.com"


def test_send_reply_notification_to_customer_external_email(mock_ticket, mock_comment):
    """Test customer notification to external email address."""
    mock_ticket.creator = None
    mock_ticket.external_email = "external@example.com"
    mock_ticket.external_name = "External User"

    service = TicketEmailService(mock_ticket)

    with patch.object(service, '_get_email_template', return_value=None):
        with patch.object(service, '_send_email', return_value=True) as mock_send:
            result = service.send_reply_notification_to_customer(mock_comment)

    assert result is True
    call_args = mock_send.call_args
    assert call_args[1]['to_email'] == "external@example.com"


def test_send_reply_notification_to_customer_fallback(mock_ticket, mock_comment):
    """Test customer notification with fallback template."""
    service = TicketEmailService(mock_ticket)

    with patch.object(service, '_get_email_template', return_value=None):
        with patch.object(service, '_send_email', return_value=True) as mock_send:
            result = service.send_reply_notification_to_customer(mock_comment)

    assert result is True
    call_args = mock_send.call_args
    assert "[Ticket #123]" in call_args[1]['subject']
    assert "has responded" in call_args[1]['subject']


# ========== Test send_resolution_notification ==========

def test_send_resolution_notification_no_recipient(mock_ticket):
    """Test resolution notification when no recipient."""
    mock_ticket.creator = None
    mock_ticket.external_email = None

    service = TicketEmailService(mock_ticket)

    result = service.send_resolution_notification()

    assert result is False


def test_send_resolution_notification_creator_success(mock_ticket):
    """Test resolution notification to creator."""
    service = TicketEmailService(mock_ticket)

    mock_template = Mock()
    mock_template.subject = "Resolved: {{TICKET_ID}}"
    mock_template.html_content = "<p>{{RESOLUTION_MESSAGE}}</p>"
    mock_template.text_content = "{{RESOLUTION_MESSAGE}}"

    with patch.object(service, '_get_email_template', return_value=mock_template):
        with patch.object(service, '_send_email', return_value=True) as mock_send:
            result = service.send_resolution_notification(resolution_message="Issue fixed")

    assert result is True
    call_args = mock_send.call_args
    assert call_args[1]['to_email'] == "customer@example.com"
    assert "Issue fixed" in call_args[1]['text_content']


def test_send_resolution_notification_external_email(mock_ticket):
    """Test resolution notification to external email."""
    mock_ticket.creator = None
    mock_ticket.external_email = "external@example.com"

    service = TicketEmailService(mock_ticket)

    with patch.object(service, '_get_email_template', return_value=None):
        with patch.object(service, '_send_email', return_value=True) as mock_send:
            result = service.send_resolution_notification()

    assert result is True
    call_args = mock_send.call_args
    assert call_args[1]['to_email'] == "external@example.com"


def test_send_resolution_notification_default_message(mock_ticket):
    """Test resolution notification with default message."""
    service = TicketEmailService(mock_ticket)

    with patch.object(service, '_get_email_template', return_value=None):
        with patch.object(service, '_send_email', return_value=True) as mock_send:
            result = service.send_resolution_notification(resolution_message="")

    assert result is True
    call_args = mock_send.call_args
    # Default message should be used
    assert "request has been resolved" in call_args[1]['text_content']


def test_send_resolution_notification_fallback(mock_ticket):
    """Test resolution notification with fallback template."""
    service = TicketEmailService(mock_ticket)

    with patch.object(service, '_get_email_template', return_value=None):
        with patch.object(service, '_send_email', return_value=True) as mock_send:
            result = service.send_resolution_notification()

    assert result is True
    call_args = mock_send.call_args
    assert "[Ticket #123]" in call_args[1]['subject']
    assert "request has been resolved" in call_args[1]['subject']


# ========== Test Default Text Templates ==========

def test_get_default_assignment_text(mock_ticket):
    """Test default assignment text template."""
    service = TicketEmailService(mock_ticket)
    context = service._get_base_context()
    context['ASSIGNEE_NAME'] = "Test Assignee"

    result = service._get_default_assignment_text(context)

    assert "New Ticket Assigned to You" in result
    assert "Test Assignee" in result
    assert "#123" in result
    assert "Test Ticket Subject" in result


def test_get_default_status_change_text(mock_ticket):
    """Test default status change text template."""
    service = TicketEmailService(mock_ticket)
    context = service._get_base_context()
    context['RECIPIENT_NAME'] = "Customer Name"

    result = service._get_default_status_change_text(context)

    assert "Ticket Status Updated" in result
    assert "Customer Name" in result
    assert "#123" in result


def test_get_default_reply_staff_text(mock_ticket):
    """Test default staff reply text template."""
    service = TicketEmailService(mock_ticket)
    context = service._get_base_context()
    context['ASSIGNEE_NAME'] = "Staff Name"
    context['REPLY_MESSAGE'] = "Test reply message"

    result = service._get_default_reply_staff_text(context)

    assert "New Reply on Ticket #123" in result
    assert "Staff Name" in result
    assert "Test reply message" in result


def test_get_default_reply_customer_text(mock_ticket):
    """Test default customer reply text template."""
    service = TicketEmailService(mock_ticket)
    context = service._get_base_context()
    context['REPLY_MESSAGE'] = "Our response"

    result = service._get_default_reply_customer_text(context)

    assert "We've Responded to Your Request" in result
    assert "Our response" in result
    assert "John Customer" in result


def test_get_default_resolution_text(mock_ticket):
    """Test default resolution text template."""
    service = TicketEmailService(mock_ticket)
    context = service._get_base_context()
    context['RESOLUTION_MESSAGE'] = "Fixed successfully"

    result = service._get_default_resolution_text(context)

    assert "Your Request Has Been Resolved" in result
    assert "Fixed successfully" in result
    assert "#123 - RESOLVED" in result


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

def test_notify_ticket_assigned(mock_ticket):
    """Test notify_ticket_assigned convenience function."""
    mock_ticket.assignee = Mock()
    mock_ticket.assignee.email = "staff@test.com"
    mock_ticket.assignee.get_full_name.return_value = "Staff"

    with patch.object(TicketEmailService, 'send_assignment_notification', return_value=True) as mock_send:
        result = notify_ticket_assigned(mock_ticket)

    assert result is True
    mock_send.assert_called_once()


def test_notify_ticket_status_changed(mock_ticket):
    """Test notify_ticket_status_changed convenience function."""
    with patch.object(TicketEmailService, 'send_status_change_notification', return_value=True) as mock_send:
        result = notify_ticket_status_changed(mock_ticket, old_status="OPEN")

    assert result is True
    mock_send.assert_called_once_with("OPEN")


def test_notify_ticket_reply_customer_reply(mock_ticket, mock_comment):
    """Test notify_ticket_reply when customer replies (notify staff)."""
    mock_comment.author = mock_ticket.creator
    mock_ticket.assignee = Mock()
    mock_ticket.assignee.email = "staff@test.com"

    with patch.object(TicketEmailService, 'send_reply_notification_to_staff', return_value=True) as mock_staff:
        with patch.object(TicketEmailService, 'send_reply_notification_to_customer', return_value=False) as mock_customer:
            staff_notified, customer_notified = notify_ticket_reply(mock_ticket, mock_comment)

    assert staff_notified is True
    assert customer_notified is False
    mock_staff.assert_called_once_with(mock_comment)
    mock_customer.assert_not_called()


def test_notify_ticket_reply_staff_reply(mock_ticket, mock_comment):
    """Test notify_ticket_reply when staff replies (notify customer)."""
    # Comment author is different from creator
    mock_comment.author = Mock()
    mock_comment.author.email = "staff@test.com"

    with patch.object(TicketEmailService, 'send_reply_notification_to_staff', return_value=False) as mock_staff:
        with patch.object(TicketEmailService, 'send_reply_notification_to_customer', return_value=True) as mock_customer:
            staff_notified, customer_notified = notify_ticket_reply(mock_ticket, mock_comment)

    assert staff_notified is False
    assert customer_notified is True
    mock_staff.assert_not_called()
    mock_customer.assert_called_once_with(mock_comment)


def test_notify_ticket_resolved(mock_ticket):
    """Test notify_ticket_resolved convenience function."""
    with patch.object(TicketEmailService, 'send_resolution_notification', return_value=True) as mock_send:
        result = notify_ticket_resolved(mock_ticket, resolution_message="All fixed")

    assert result is True
    mock_send.assert_called_once_with("All fixed")


def test_notify_ticket_resolved_no_message(mock_ticket):
    """Test notify_ticket_resolved with no message."""
    with patch.object(TicketEmailService, 'send_resolution_notification', return_value=True) as mock_send:
        result = notify_ticket_resolved(mock_ticket)

    assert result is True
    mock_send.assert_called_once_with('')
