"""
Unit tests for Ticket Email Notification Service

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

Updated to use the new PuckEmailTemplate and send_system_email system.
"""

import smtplib
from unittest.mock import Mock, patch, MagicMock
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.first_name = "John"
    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.first_name = "Business"
    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


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

def test_get_default_platform_email_success():
    """Test successfully getting default platform email."""
    mock_email = Mock()
    mock_email.email_address = "support@smoothschedule.com"

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

        result = get_default_platform_email()

        assert result == mock_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_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',
        '%I:%M %p': '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['tenant_name'] == "Test Business"
    assert context['tenant_email'] == "support@testbusiness.com"
    assert context['tenant_phone'] == "555-1234"
    assert context['customer_name'] == "John Customer"
    assert context['customer_first_name'] == "John"
    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_link'] == "http://test.lvh.me:5173/tickets/123"
    assert context['current_date'] == 'January 15, 2024'
    assert context['current_time'] == '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',
        '%I:%M %p': '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['tenant_name'] == "SmoothSchedule Platform"
    assert context['tenant_email'] == "noreply@smoothschedule.com"
    assert context['tenant_phone'] == ""
    assert context['ticket_link'] == "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.side_effect = lambda fmt: 'January 15, 2024' if 'Y' in fmt else '10:30 AM'
    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_first_name'] == "Customer"
    assert context['customer_email'] == ""


# ========== Test _send_email (kept for platform SMTP) ==========

@patch('smoothschedule.commerce.tickets.email_notifications.EmailMultiAlternatives')
def test_send_email_success_with_tenant(MockEmailMultiAlternatives, mock_ticket):
    """Test successful email sending via Django backend."""
    mock_msg = Mock()
    MockEmailMultiAlternatives.return_value = mock_msg

    service = TicketEmailService(mock_ticket)

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

    assert result is True
    MockEmailMultiAlternatives.assert_called_once()
    mock_msg.attach_alternative.assert_called_once_with("<p>HTML</p>", 'text/html')
    mock_msg.send.assert_called_once_with(fail_silently=False)


def test_send_email_no_recipient(mock_ticket):
    """Test email sending with no recipient."""
    service = TicketEmailService(mock_ticket)

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

    assert result is False


@patch('smoothschedule.commerce.tickets.email_notifications.logger')
@patch('smoothschedule.commerce.tickets.email_notifications.EmailMultiAlternatives')
def test_send_email_exception(MockEmailMultiAlternatives, mock_logger, mock_ticket):
    """Test exception handling during email send."""
    mock_msg = Mock()
    mock_msg.send.side_effect = Exception("SMTP error")
    MockEmailMultiAlternatives.return_value = mock_msg

    service = TicketEmailService(mock_ticket)

    result = service._send_email(
        to_email="recipient@example.com",
        subject="Test",
        html_content="",
        text_content="Text"
    )

    assert result is False
    mock_logger.error.assert_called_once()


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

def test_send_assignment_notification_no_assignee(mock_ticket):
    """Test assignment notification with no assignee."""
    mock_ticket.assignee = None

    service = TicketEmailService(mock_ticket)
    result = service.send_assignment_notification()

    assert result is False


def test_send_assignment_notification_no_email(mock_ticket):
    """Test assignment notification when assignee has no email."""
    mock_ticket.assignee = Mock()
    mock_ticket.assignee.email = ""

    service = TicketEmailService(mock_ticket)
    result = service.send_assignment_notification()

    assert result is False


@patch('smoothschedule.commerce.tickets.email_notifications.send_system_email')
@patch('smoothschedule.commerce.tickets.email_notifications.timezone')
def test_send_assignment_notification_success(mock_timezone, mock_send_email, mock_ticket):
    """Test successful assignment notification."""
    mock_now = Mock()
    mock_now.strftime.side_effect = lambda fmt: 'January 15, 2024' if 'Y' in fmt else '10:30 AM'
    mock_timezone.now.return_value = mock_now

    mock_ticket.assignee = Mock()
    mock_ticket.assignee.email = "assignee@example.com"
    mock_ticket.assignee.get_full_name.return_value = "Assignee Name"

    mock_send_email.return_value = True

    service = TicketEmailService(mock_ticket)
    result = service.send_assignment_notification()

    assert result is True
    mock_send_email.assert_called_once()
    call_kwargs = mock_send_email.call_args[1]
    assert call_kwargs['to_email'] == "assignee@example.com"
    assert 'assignee_name' in call_kwargs['context']


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

def test_send_status_change_notification_no_customer(mock_ticket):
    """Test status change notification with no customer."""
    mock_ticket.creator = None

    service = TicketEmailService(mock_ticket)
    result = service.send_status_change_notification("OLD_STATUS")

    assert result is False


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

    service = TicketEmailService(mock_ticket)
    result = service.send_status_change_notification("OLD_STATUS")

    assert result is False


def test_send_status_change_notification_notify_false(mock_ticket):
    """Test status change notification disabled."""
    service = TicketEmailService(mock_ticket)
    result = service.send_status_change_notification("OLD_STATUS", notify_customer=False)

    assert result is False


@patch('smoothschedule.commerce.tickets.email_notifications.send_system_email')
@patch('smoothschedule.commerce.tickets.email_notifications.timezone')
def test_send_status_change_notification_success(mock_timezone, mock_send_email, mock_ticket):
    """Test successful status change notification."""
    mock_now = Mock()
    mock_now.strftime.side_effect = lambda fmt: 'January 15, 2024' if 'Y' in fmt else '10:30 AM'
    mock_timezone.now.return_value = mock_now

    mock_send_email.return_value = True

    service = TicketEmailService(mock_ticket)
    result = service.send_status_change_notification("PENDING")

    assert result is True
    mock_send_email.assert_called_once()


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

def test_send_reply_notification_to_staff_no_assignee(mock_ticket, mock_comment):
    """Test reply notification to staff with no assignee."""
    mock_ticket.assignee = None

    service = TicketEmailService(mock_ticket)
    result = service.send_reply_notification_to_staff(mock_comment)

    assert result is False


def test_send_reply_notification_to_staff_assignee_is_author(mock_ticket, mock_comment):
    """Test reply notification skipped when assignee is author."""
    mock_ticket.assignee = Mock()
    mock_ticket.assignee.email = "assignee@example.com"
    mock_comment.author = mock_ticket.assignee

    service = TicketEmailService(mock_ticket)
    result = service.send_reply_notification_to_staff(mock_comment)

    assert result is False


@patch('smoothschedule.commerce.tickets.email_notifications.send_system_email')
@patch('smoothschedule.commerce.tickets.email_notifications.timezone')
def test_send_reply_notification_to_staff_success(mock_timezone, mock_send_email, mock_ticket, mock_comment):
    """Test successful reply notification to staff."""
    mock_now = Mock()
    mock_now.strftime.side_effect = lambda fmt: 'January 15, 2024' if 'Y' in fmt else '10:30 AM'
    mock_timezone.now.return_value = mock_now

    mock_ticket.assignee = Mock()
    mock_ticket.assignee.email = "assignee@example.com"
    mock_ticket.assignee.get_full_name.return_value = "Assignee Name"

    mock_send_email.return_value = True

    service = TicketEmailService(mock_ticket)
    result = service.send_reply_notification_to_staff(mock_comment)

    assert result is True
    mock_send_email.assert_called_once()
    call_kwargs = mock_send_email.call_args[1]
    assert call_kwargs['to_email'] == "assignee@example.com"
    assert 'reply_message' in call_kwargs['context']


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

def test_send_reply_notification_to_customer_no_recipient(mock_ticket, mock_comment):
    """Test reply notification when customer has no 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


def test_send_reply_notification_to_customer_creator_is_author(mock_ticket, mock_comment):
    """Test reply notification skipped when creator is author."""
    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_comment):
    """Test reply notification skipped when external sender is author."""
    mock_ticket.creator = None
    mock_ticket.external_email = "external@example.com"
    mock_comment.external_author_email = "external@example.com"

    service = TicketEmailService(mock_ticket)
    result = service.send_reply_notification_to_customer(mock_comment)

    assert result is False


def test_send_reply_notification_to_customer_internal_comment(mock_ticket, mock_comment):
    """Test internal comments are not sent to customers."""
    mock_comment.is_internal = True

    service = TicketEmailService(mock_ticket)
    result = service.send_reply_notification_to_customer(mock_comment)

    assert result is False


@patch('smoothschedule.commerce.tickets.email_notifications.send_system_email')
@patch('smoothschedule.commerce.tickets.email_notifications.timezone')
def test_send_reply_notification_to_customer_success(mock_timezone, mock_send_email, mock_ticket, mock_comment):
    """Test successful reply notification to customer."""
    mock_now = Mock()
    mock_now.strftime.side_effect = lambda fmt: 'January 15, 2024' if 'Y' in fmt else '10:30 AM'
    mock_timezone.now.return_value = mock_now

    mock_send_email.return_value = True

    service = TicketEmailService(mock_ticket)
    result = service.send_reply_notification_to_customer(mock_comment)

    assert result is True
    mock_send_email.assert_called_once()


@patch('smoothschedule.commerce.tickets.email_notifications.send_system_email')
@patch('smoothschedule.commerce.tickets.email_notifications.timezone')
def test_send_reply_notification_to_customer_external_email(mock_timezone, mock_send_email, mock_ticket, mock_comment):
    """Test reply notification to external email."""
    mock_now = Mock()
    mock_now.strftime.side_effect = lambda fmt: 'January 15, 2024' if 'Y' in fmt else '10:30 AM'
    mock_timezone.now.return_value = mock_now

    mock_ticket.creator = None
    mock_ticket.external_email = "external@example.com"
    mock_ticket.external_name = "External Person"

    mock_send_email.return_value = True

    service = TicketEmailService(mock_ticket)
    result = service.send_reply_notification_to_customer(mock_comment)

    assert result is True
    mock_send_email.assert_called_once()
    call_kwargs = mock_send_email.call_args[1]
    assert call_kwargs['to_email'] == "external@example.com"


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

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

    service = TicketEmailService(mock_ticket)
    result = service.send_resolution_notification()

    assert result is False


@patch('smoothschedule.commerce.tickets.email_notifications.send_system_email')
@patch('smoothschedule.commerce.tickets.email_notifications.timezone')
def test_send_resolution_notification_success(mock_timezone, mock_send_email, mock_ticket):
    """Test successful resolution notification to creator."""
    mock_now = Mock()
    mock_now.strftime.side_effect = lambda fmt: 'January 15, 2024' if 'Y' in fmt else '10:30 AM'
    mock_timezone.now.return_value = mock_now

    mock_send_email.return_value = True

    service = TicketEmailService(mock_ticket)
    result = service.send_resolution_notification("Issue has been fixed")

    assert result is True
    mock_send_email.assert_called_once()
    call_kwargs = mock_send_email.call_args[1]
    assert 'resolution_message' in call_kwargs['context']
    assert call_kwargs['context']['resolution_message'] == "Issue has been fixed"


@patch('smoothschedule.commerce.tickets.email_notifications.send_system_email')
@patch('smoothschedule.commerce.tickets.email_notifications.timezone')
def test_send_resolution_notification_external_email(mock_timezone, mock_send_email, mock_ticket):
    """Test resolution notification to external email."""
    mock_now = Mock()
    mock_now.strftime.side_effect = lambda fmt: 'January 15, 2024' if 'Y' in fmt else '10:30 AM'
    mock_timezone.now.return_value = mock_now

    mock_ticket.creator = None
    mock_ticket.external_email = "external@example.com"
    mock_ticket.external_name = "External User"

    mock_send_email.return_value = True

    service = TicketEmailService(mock_ticket)
    result = service.send_resolution_notification()

    assert result is True
    mock_send_email.assert_called_once()
    call_kwargs = mock_send_email.call_args[1]
    assert call_kwargs['to_email'] == "external@example.com"


@patch('smoothschedule.commerce.tickets.email_notifications.send_system_email')
@patch('smoothschedule.commerce.tickets.email_notifications.timezone')
def test_send_resolution_notification_default_message(mock_timezone, mock_send_email, mock_ticket):
    """Test resolution notification with default message."""
    mock_now = Mock()
    mock_now.strftime.side_effect = lambda fmt: 'January 15, 2024' if 'Y' in fmt else '10:30 AM'
    mock_timezone.now.return_value = mock_now

    mock_send_email.return_value = True

    service = TicketEmailService(mock_ticket)
    result = service.send_resolution_notification()

    assert result is True
    call_kwargs = mock_send_email.call_args[1]
    assert call_kwargs['context']['resolution_message'] == "Your request has been resolved."


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

@patch.object(TicketEmailService, 'send_assignment_notification')
def test_notify_ticket_assigned(mock_method, mock_ticket):
    """Test notify_ticket_assigned convenience function."""
    mock_method.return_value = True

    result = notify_ticket_assigned(mock_ticket)

    assert result is True
    mock_method.assert_called_once()


@patch.object(TicketEmailService, 'send_status_change_notification')
def test_notify_ticket_status_changed(mock_method, mock_ticket):
    """Test notify_ticket_status_changed convenience function."""
    mock_method.return_value = True

    result = notify_ticket_status_changed(mock_ticket, "OLD_STATUS")

    assert result is True
    mock_method.assert_called_once_with("OLD_STATUS")


@patch.object(TicketEmailService, 'send_reply_notification_to_staff')
@patch.object(TicketEmailService, 'send_reply_notification_to_customer')
def test_notify_ticket_reply_customer_reply(mock_customer, mock_staff, mock_ticket, mock_comment):
    """Test notify_ticket_reply when customer replies."""
    # Customer is the author
    mock_comment.author = mock_ticket.creator
    mock_staff.return_value = True

    result = notify_ticket_reply(mock_ticket, mock_comment)

    assert result == (True, False)
    mock_staff.assert_called_once()
    mock_customer.assert_not_called()


@patch.object(TicketEmailService, 'send_reply_notification_to_staff')
@patch.object(TicketEmailService, 'send_reply_notification_to_customer')
def test_notify_ticket_reply_staff_reply(mock_customer, mock_staff, mock_ticket, mock_comment):
    """Test notify_ticket_reply when staff replies."""
    # Staff is the author (not customer)
    mock_comment.author = Mock()
    mock_customer.return_value = True

    result = notify_ticket_reply(mock_ticket, mock_comment)

    assert result == (False, True)
    mock_customer.assert_called_once()
    mock_staff.assert_not_called()


@patch.object(TicketEmailService, 'send_resolution_notification')
def test_notify_ticket_resolved(mock_method, mock_ticket):
    """Test notify_ticket_resolved convenience function."""
    mock_method.return_value = True

    result = notify_ticket_resolved(mock_ticket, "All done!")

    assert result is True
    mock_method.assert_called_once_with("All done!")
