"""
Unit tests for Tickets models.

Tests model methods, properties, and validation logic.
"""
from unittest.mock import Mock, patch
from datetime import timedelta
import pytest

from django.utils import timezone


class TestTicketModel:
    """Test Ticket model methods."""

    def test_str_method(self):
        """Should return formatted string with ID, subject, and status."""
        from smoothschedule.commerce.tickets.models import Ticket

        ticket = Ticket()
        ticket.id = 42
        ticket.subject = 'Test Issue'
        ticket.status = Ticket.Status.OPEN

        result = str(ticket)

        assert 'Ticket #42' in result
        assert 'Test Issue' in result
        assert 'Open' in result

    def test_calculate_sla_due_date_urgent(self):
        """Should set 1 hour SLA for urgent priority."""
        from smoothschedule.commerce.tickets.models import Ticket

        ticket = Ticket()
        ticket.priority = Ticket.Priority.URGENT

        now = timezone.now()
        with patch.object(timezone, 'now', return_value=now):
            due_date = ticket._calculate_sla_due_date()

        expected = now + timedelta(hours=1)
        assert abs((due_date - expected).total_seconds()) < 1

    def test_calculate_sla_due_date_high(self):
        """Should set 4 hour SLA for high priority."""
        from smoothschedule.commerce.tickets.models import Ticket

        ticket = Ticket()
        ticket.priority = Ticket.Priority.HIGH

        now = timezone.now()
        with patch.object(timezone, 'now', return_value=now):
            due_date = ticket._calculate_sla_due_date()

        expected = now + timedelta(hours=4)
        assert abs((due_date - expected).total_seconds()) < 1

    def test_calculate_sla_due_date_medium(self):
        """Should set 24 hour SLA for medium priority."""
        from smoothschedule.commerce.tickets.models import Ticket

        ticket = Ticket()
        ticket.priority = Ticket.Priority.MEDIUM

        now = timezone.now()
        with patch.object(timezone, 'now', return_value=now):
            due_date = ticket._calculate_sla_due_date()

        expected = now + timedelta(hours=24)
        assert abs((due_date - expected).total_seconds()) < 1

    def test_calculate_sla_due_date_low(self):
        """Should set 72 hour SLA for low priority."""
        from smoothschedule.commerce.tickets.models import Ticket

        ticket = Ticket()
        ticket.priority = Ticket.Priority.LOW

        now = timezone.now()
        with patch.object(timezone, 'now', return_value=now):
            due_date = ticket._calculate_sla_due_date()

        expected = now + timedelta(hours=72)
        assert abs((due_date - expected).total_seconds()) < 1

    def test_is_overdue_returns_false_when_no_due_date(self):
        """Should return False when no due date set."""
        from smoothschedule.commerce.tickets.models import Ticket

        ticket = Ticket()
        ticket.due_at = None
        ticket.status = Ticket.Status.OPEN

        assert ticket.is_overdue is False

    def test_is_overdue_returns_false_when_resolved(self):
        """Should return False when ticket is resolved."""
        from smoothschedule.commerce.tickets.models import Ticket

        ticket = Ticket()
        ticket.due_at = timezone.now() - timedelta(hours=1)  # Past due
        ticket.status = Ticket.Status.RESOLVED

        assert ticket.is_overdue is False

    def test_is_overdue_returns_false_when_closed(self):
        """Should return False when ticket is closed."""
        from smoothschedule.commerce.tickets.models import Ticket

        ticket = Ticket()
        ticket.due_at = timezone.now() - timedelta(hours=1)  # Past due
        ticket.status = Ticket.Status.CLOSED

        assert ticket.is_overdue is False

    def test_is_overdue_returns_true_when_past_due_and_open(self):
        """Should return True when ticket is past due and still open."""
        from smoothschedule.commerce.tickets.models import Ticket

        ticket = Ticket()
        ticket.due_at = timezone.now() - timedelta(hours=1)
        ticket.status = Ticket.Status.OPEN

        assert ticket.is_overdue is True

    def test_is_overdue_returns_false_when_not_past_due(self):
        """Should return False when ticket is not past due."""
        from smoothschedule.commerce.tickets.models import Ticket

        ticket = Ticket()
        ticket.due_at = timezone.now() + timedelta(hours=1)
        ticket.status = Ticket.Status.OPEN

        assert ticket.is_overdue is False


class TestTicketSaveMethod:
    """Test Ticket save() method."""

    def test_save_sets_sla_due_date_on_new_ticket(self):
        """Should set due_at based on priority for new tickets."""
        from smoothschedule.commerce.tickets.models import Ticket

        ticket = Ticket()
        ticket.pk = None  # New ticket
        ticket.priority = Ticket.Priority.URGENT
        ticket.due_at = None
        ticket.status = Ticket.Status.OPEN
        ticket.resolved_at = None

        now = timezone.now()
        with patch.object(timezone, 'now', return_value=now):
            # Simulate save logic
            if not ticket.due_at and not ticket.pk:
                ticket.due_at = ticket._calculate_sla_due_date()

        expected = now + timedelta(hours=1)
        assert abs((ticket.due_at - expected).total_seconds()) < 1

    def test_save_sets_resolved_at_when_resolved(self):
        """Should set resolved_at when status changes to RESOLVED."""
        from smoothschedule.commerce.tickets.models import Ticket

        ticket = Ticket()
        ticket.pk = 1
        ticket.status = Ticket.Status.RESOLVED
        ticket.resolved_at = None

        now = timezone.now()
        with patch.object(timezone, 'now', return_value=now):
            # Simulate save logic
            if ticket.status in [Ticket.Status.RESOLVED, Ticket.Status.CLOSED] and not ticket.resolved_at:
                ticket.resolved_at = timezone.now()

        assert ticket.resolved_at is not None

    def test_save_sets_resolved_at_when_closed(self):
        """Should set resolved_at when status changes to CLOSED."""
        from smoothschedule.commerce.tickets.models import Ticket

        ticket = Ticket()
        ticket.pk = 1
        ticket.status = Ticket.Status.CLOSED
        ticket.resolved_at = None

        now = timezone.now()
        with patch.object(timezone, 'now', return_value=now):
            # Simulate save logic
            if ticket.status in [Ticket.Status.RESOLVED, Ticket.Status.CLOSED] and not ticket.resolved_at:
                ticket.resolved_at = timezone.now()

        assert ticket.resolved_at is not None

    def test_save_clears_resolved_at_when_reopened(self):
        """Should clear resolved_at when status changes back to non-resolved."""
        from smoothschedule.commerce.tickets.models import Ticket

        ticket = Ticket()
        ticket.pk = 1
        ticket.status = Ticket.Status.OPEN
        ticket.resolved_at = timezone.now()

        # Simulate save logic
        if ticket.status not in [Ticket.Status.RESOLVED, Ticket.Status.CLOSED]:
            ticket.resolved_at = None

        assert ticket.resolved_at is None


class TestTicketTemplateModel:
    """Test TicketTemplate model methods."""

    def test_str_method_with_tenant(self):
        """Should return name with tenant scope."""
        from smoothschedule.commerce.tickets.models import TicketTemplate

        template = TicketTemplate()
        template.name = 'Bug Report'

        mock_tenant = Mock()
        mock_tenant.name = 'Test Business'
        template._state.fields_cache['tenant'] = mock_tenant

        result = str(template)

        assert 'Bug Report' in result
        assert 'Test Business' in result

    def test_str_method_without_tenant(self):
        """Should return name with Platform scope when no tenant."""
        from smoothschedule.commerce.tickets.models import TicketTemplate

        template = TicketTemplate()
        template.name = 'General Inquiry'
        template._state.fields_cache['tenant'] = None

        result = str(template)

        assert 'General Inquiry' in result
        assert 'Platform' in result


class TestIncomingTicketEmailModel:
    """Test IncomingTicketEmail model methods."""

    def test_str_method(self):
        """Should return sender and truncated subject."""
        from smoothschedule.commerce.tickets.models import IncomingTicketEmail

        email = IncomingTicketEmail()
        email.from_address = 'customer@example.com'
        email.subject = 'This is a very long subject that should be truncated'

        result = str(email)

        assert 'customer@example.com' in result
        assert 'This is a very long subject' in result

    def test_mark_processed_updates_status_and_timestamp(self):
        """Should mark email as processed with timestamp."""
        from smoothschedule.commerce.tickets.models import IncomingTicketEmail

        email = IncomingTicketEmail()
        email.processing_status = IncomingTicketEmail.ProcessingStatus.PENDING
        email.processed_at = None

        with patch.object(IncomingTicketEmail, 'save'):
            # Just call with no args to test status change
            email.mark_processed()

        assert email.processing_status == IncomingTicketEmail.ProcessingStatus.PROCESSED
        assert email.processed_at is not None

    def test_mark_processed_without_ticket_or_user(self):
        """Should mark email as processed without ticket or user."""
        from smoothschedule.commerce.tickets.models import IncomingTicketEmail

        email = IncomingTicketEmail()
        email.processing_status = IncomingTicketEmail.ProcessingStatus.PENDING
        email.ticket = None
        email.matched_user = None

        with patch.object(IncomingTicketEmail, 'save'):
            email.mark_processed()

        assert email.processing_status == IncomingTicketEmail.ProcessingStatus.PROCESSED
        assert email.processed_at is not None

    def test_mark_failed_updates_status(self):
        """Should mark email as failed with error message."""
        from smoothschedule.commerce.tickets.models import IncomingTicketEmail

        email = IncomingTicketEmail()
        email.processing_status = IncomingTicketEmail.ProcessingStatus.PENDING

        with patch.object(IncomingTicketEmail, 'save'):
            email.mark_failed('Connection timeout')

        assert email.processing_status == IncomingTicketEmail.ProcessingStatus.FAILED
        assert email.error_message == 'Connection timeout'
        assert email.processed_at is not None

    def test_mark_no_match_updates_status(self):
        """Should mark email as having no matching ticket."""
        from smoothschedule.commerce.tickets.models import IncomingTicketEmail

        email = IncomingTicketEmail()
        email.processing_status = IncomingTicketEmail.ProcessingStatus.PENDING

        with patch.object(IncomingTicketEmail, 'save'):
            email.mark_no_match()

        assert email.processing_status == IncomingTicketEmail.ProcessingStatus.NO_MATCH
        assert email.processed_at is not None


class TestTicketEmailAddressModel:
    """Test TicketEmailAddress model methods."""

    def test_str_method(self):
        """Should return formatted email with display name and tenant."""
        from smoothschedule.commerce.tickets.models import TicketEmailAddress

        email_addr = TicketEmailAddress()
        email_addr.display_name = 'Support'
        email_addr.email_address = 'support@example.com'

        mock_tenant = Mock()
        mock_tenant.name = 'Test Business'
        email_addr._state.fields_cache['tenant'] = mock_tenant

        result = str(email_addr)

        assert 'Support' in result
        assert 'support@example.com' in result
        assert 'Test Business' in result

    def test_is_imap_configured_returns_true(self):
        """Should return True when IMAP is configured."""
        from smoothschedule.commerce.tickets.models import TicketEmailAddress

        email_addr = TicketEmailAddress()
        email_addr.imap_host = 'imap.example.com'
        email_addr.imap_username = 'user@example.com'
        email_addr.imap_password = 'secret'

        assert email_addr.is_imap_configured() is True

    def test_is_imap_configured_returns_false_without_host(self):
        """Should return False when IMAP host is missing."""
        from smoothschedule.commerce.tickets.models import TicketEmailAddress

        email_addr = TicketEmailAddress()
        email_addr.imap_host = ''
        email_addr.imap_username = 'user@example.com'
        email_addr.imap_password = 'secret'

        assert email_addr.is_imap_configured() is False

    def test_is_smtp_configured_returns_true(self):
        """Should return True when SMTP is configured."""
        from smoothschedule.commerce.tickets.models import TicketEmailAddress

        email_addr = TicketEmailAddress()
        email_addr.smtp_host = 'smtp.example.com'
        email_addr.smtp_username = 'user@example.com'
        email_addr.smtp_password = 'secret'

        assert email_addr.is_smtp_configured() is True

    def test_is_smtp_configured_returns_false_without_password(self):
        """Should return False when SMTP password is missing."""
        from smoothschedule.commerce.tickets.models import TicketEmailAddress

        email_addr = TicketEmailAddress()
        email_addr.smtp_host = 'smtp.example.com'
        email_addr.smtp_username = 'user@example.com'
        email_addr.smtp_password = ''

        assert email_addr.is_smtp_configured() is False

    def test_is_fully_configured_returns_true(self):
        """Should return True when both IMAP and SMTP are configured."""
        from smoothschedule.commerce.tickets.models import TicketEmailAddress

        email_addr = TicketEmailAddress()
        email_addr.imap_host = 'imap.example.com'
        email_addr.imap_username = 'user@example.com'
        email_addr.imap_password = 'secret'
        email_addr.smtp_host = 'smtp.example.com'
        email_addr.smtp_username = 'user@example.com'
        email_addr.smtp_password = 'secret'

        assert email_addr.is_fully_configured() is True

    def test_is_fully_configured_returns_false_when_imap_missing(self):
        """Should return False when IMAP is not configured."""
        from smoothschedule.commerce.tickets.models import TicketEmailAddress

        email_addr = TicketEmailAddress()
        email_addr.imap_host = ''
        email_addr.imap_username = ''
        email_addr.imap_password = ''
        email_addr.smtp_host = 'smtp.example.com'
        email_addr.smtp_username = 'user@example.com'
        email_addr.smtp_password = 'secret'

        assert email_addr.is_fully_configured() is False

    def test_save_clears_other_default_addresses(self):
        """Should clear other default addresses when setting new default."""
        from smoothschedule.commerce.tickets.models import TicketEmailAddress

        email_addr = TicketEmailAddress()
        email_addr.pk = 1
        email_addr.is_default = True

        mock_tenant = Mock()
        email_addr._state.fields_cache['tenant'] = mock_tenant

        mock_queryset = Mock()
        with patch.object(TicketEmailAddress.objects, 'filter', return_value=mock_queryset):
            # Simulate save logic
            if email_addr.is_default:
                TicketEmailAddress.objects.filter(
                    tenant=mock_tenant,
                    is_default=True
                ).exclude(pk=email_addr.pk).update(is_default=False)

        mock_queryset.exclude.assert_called_once_with(pk=1)
        mock_queryset.exclude.return_value.update.assert_called_once_with(is_default=False)


class TestTicketCommentModel:
    """Test TicketComment model methods."""

    def test_author_display_name_returns_full_name(self):
        """Should return author's full name when available."""
        from smoothschedule.commerce.tickets.models import TicketComment

        comment = TicketComment()
        mock_author = Mock()
        mock_author.get_full_name.return_value = 'John Doe'
        mock_author.email = 'john@example.com'
        comment._state.fields_cache['author'] = mock_author
        comment.external_author_name = None
        comment.external_author_email = None

        assert comment.author_display_name == 'John Doe'

    def test_author_display_name_returns_email_when_no_full_name(self):
        """Should return author's email when no full name."""
        from smoothschedule.commerce.tickets.models import TicketComment

        comment = TicketComment()
        mock_author = Mock()
        mock_author.get_full_name.return_value = ''
        mock_author.email = 'john@example.com'
        comment._state.fields_cache['author'] = mock_author
        comment.external_author_name = None
        comment.external_author_email = None

        assert comment.author_display_name == 'john@example.com'

    def test_author_display_name_returns_external_name(self):
        """Should return external author name when no author."""
        from smoothschedule.commerce.tickets.models import TicketComment

        comment = TicketComment()
        comment._state.fields_cache['author'] = None
        comment.external_author_name = 'External User'
        comment.external_author_email = 'external@example.com'

        assert comment.author_display_name == 'External User'

    def test_author_display_name_returns_external_email_when_no_name(self):
        """Should return external email when no name."""
        from smoothschedule.commerce.tickets.models import TicketComment

        comment = TicketComment()
        comment._state.fields_cache['author'] = None
        comment.external_author_name = None
        comment.external_author_email = 'external@example.com'

        assert comment.author_display_name == 'external@example.com'

    def test_author_display_name_returns_unknown_when_nothing(self):
        """Should return 'Unknown' when no author info."""
        from smoothschedule.commerce.tickets.models import TicketComment

        comment = TicketComment()
        comment._state.fields_cache['author'] = None
        comment.external_author_name = None
        comment.external_author_email = None

        assert comment.author_display_name == 'Unknown'

    def test_author_email_returns_author_email(self):
        """Should return author's email when author exists."""
        from smoothschedule.commerce.tickets.models import TicketComment

        comment = TicketComment()
        mock_author = Mock()
        mock_author.email = 'john@example.com'
        comment._state.fields_cache['author'] = mock_author
        comment.external_author_email = 'external@example.com'

        assert comment.author_email == 'john@example.com'

    def test_author_email_returns_external_email_when_no_author(self):
        """Should return external email when no author."""
        from smoothschedule.commerce.tickets.models import TicketComment

        comment = TicketComment()
        comment._state.fields_cache['author'] = None
        comment.external_author_email = 'external@example.com'

        assert comment.author_email == 'external@example.com'

    def test_str_method_with_author(self):
        """Should return formatted string with author email."""
        from smoothschedule.commerce.tickets.models import TicketComment

        comment = TicketComment()
        mock_author = Mock()
        mock_author.email = 'john@example.com'
        comment._state.fields_cache['author'] = mock_author

        mock_ticket = Mock()
        mock_ticket.id = 42
        comment._state.fields_cache['ticket'] = mock_ticket

        comment.created_at = timezone.now()

        result = str(comment)

        assert 'Ticket #42' in result
        assert 'john@example.com' in result

    def test_str_method_with_external_email(self):
        """Should return formatted string with external email."""
        from smoothschedule.commerce.tickets.models import TicketComment

        comment = TicketComment()
        comment._state.fields_cache['author'] = None
        comment.external_author_email = 'external@example.com'

        mock_ticket = Mock()
        mock_ticket.id = 42
        comment._state.fields_cache['ticket'] = mock_ticket

        comment.created_at = timezone.now()

        result = str(comment)

        assert 'Ticket #42' in result
        assert 'external@example.com' in result

    def test_str_method_with_unknown_author(self):
        """Should return 'Unknown' when no author info."""
        from smoothschedule.commerce.tickets.models import TicketComment

        comment = TicketComment()
        comment._state.fields_cache['author'] = None
        comment.external_author_email = None

        mock_ticket = Mock()
        mock_ticket.id = 42
        comment._state.fields_cache['ticket'] = mock_ticket

        comment.created_at = timezone.now()

        result = str(comment)

        assert 'Ticket #42' in result
        assert 'Unknown' in result
