"""
Unit tests for Messaging models.

Tests model methods, properties, and validation logic without database access.
"""
from unittest.mock import Mock, patch, MagicMock
import pytest


class TestCommunicationSessionModel:
    """Test CommunicationSession model methods."""

    def test_str_method_active(self):
        """Should return session info with Active status."""
        from smoothschedule.communication.messaging.models import CommunicationSession

        session = CommunicationSession()
        session.is_active = True

        mock_event = Mock()
        mock_event.title = 'Haircut Appointment'
        session._state.fields_cache['event'] = mock_event

        result = str(session)

        assert 'Session for Haircut Appointment' in result
        assert 'Active' in result

    def test_str_method_closed(self):
        """Should return session info with Closed status."""
        from smoothschedule.communication.messaging.models import CommunicationSession

        session = CommunicationSession()
        session.is_active = False

        mock_event = Mock()
        mock_event.title = 'Consultation'
        session._state.fields_cache['event'] = mock_event

        result = str(session)

        assert 'Session for Consultation' in result
        assert 'Closed' in result

    def test_message_count_returns_none(self):
        """Should return None (stub for Twilio API)."""
        from smoothschedule.communication.messaging.models import CommunicationSession

        session = CommunicationSession()

        result = session.message_count

        assert result is None


class TestBroadcastMessageModel:
    """Test BroadcastMessage model methods."""

    def test_str_method(self):
        """Should return subject with status."""
        from smoothschedule.communication.messaging.models import BroadcastMessage

        message = BroadcastMessage()
        message.subject = 'Important Update'
        message.status = BroadcastMessage.Status.SENT

        result = str(message)

        assert 'Important Update' in result
        assert 'Sent' in result

    def test_str_method_draft_status(self):
        """Should show Draft status."""
        from smoothschedule.communication.messaging.models import BroadcastMessage

        message = BroadcastMessage()
        message.subject = 'News'
        message.status = BroadcastMessage.Status.DRAFT

        result = str(message)

        assert 'Draft' in result

    def test_str_method_scheduled_status(self):
        """Should show Scheduled status."""
        from smoothschedule.communication.messaging.models import BroadcastMessage

        message = BroadcastMessage()
        message.subject = 'Weekly Update'
        message.status = BroadcastMessage.Status.SCHEDULED

        result = str(message)

        assert 'Scheduled' in result

    def test_get_target_description_owners_only(self):
        """Should return 'Owners' when only owners targeted."""
        from smoothschedule.communication.messaging.models import BroadcastMessage

        message = BroadcastMessage()
        message.target_owners = True
        message.target_managers = False
        message.target_staff = False
        message.target_customers = False

        # Mock the ManyToMany field via patch
        with patch.object(type(message), 'individual_recipients', create=True) as mock_m2m:
            mock_m2m.__get__ = Mock(return_value=Mock(exists=Mock(return_value=False)))

            result = message.get_target_description()

        assert result == 'Owners'

    def test_get_target_description_multiple_targets(self):
        """Should list multiple targets."""
        from smoothschedule.communication.messaging.models import BroadcastMessage

        message = BroadcastMessage()
        message.target_owners = True
        message.target_managers = True
        message.target_staff = False
        message.target_customers = True

        with patch.object(type(message), 'individual_recipients', create=True) as mock_m2m:
            mock_m2m.__get__ = Mock(return_value=Mock(exists=Mock(return_value=False)))

            result = message.get_target_description()

        assert 'Owners' in result
        assert 'Managers' in result
        assert 'Customers' in result
        assert 'Staff' not in result

    def test_get_target_description_all_targets(self):
        """Should list all targets when all selected."""
        from smoothschedule.communication.messaging.models import BroadcastMessage

        message = BroadcastMessage()
        message.target_owners = True
        message.target_managers = True
        message.target_staff = True
        message.target_customers = True

        with patch.object(type(message), 'individual_recipients', create=True) as mock_m2m:
            mock_m2m.__get__ = Mock(return_value=Mock(exists=Mock(return_value=False)))

            result = message.get_target_description()

        assert 'Owners' in result
        assert 'Managers' in result
        assert 'Staff' in result
        assert 'Customers' in result

    def test_get_target_description_with_individuals(self):
        """Should include individual count."""
        from smoothschedule.communication.messaging.models import BroadcastMessage

        message = BroadcastMessage()
        message.target_owners = False
        message.target_managers = False
        message.target_staff = False
        message.target_customers = False

        mock_manager = Mock()
        mock_manager.exists.return_value = True
        mock_manager.count.return_value = 5

        with patch.object(type(message), 'individual_recipients', create=True) as mock_m2m:
            mock_m2m.__get__ = Mock(return_value=mock_manager)

            result = message.get_target_description()

        assert '5 Individual(s)' in result

    def test_get_target_description_no_recipients(self):
        """Should return 'No recipients' when none selected."""
        from smoothschedule.communication.messaging.models import BroadcastMessage

        message = BroadcastMessage()
        message.target_owners = False
        message.target_managers = False
        message.target_staff = False
        message.target_customers = False

        with patch.object(type(message), 'individual_recipients', create=True) as mock_m2m:
            mock_m2m.__get__ = Mock(return_value=Mock(exists=Mock(return_value=False)))

            result = message.get_target_description()

        assert result == 'No recipients'

    def test_get_target_description_mixed(self):
        """Should combine role targets with individual count."""
        from smoothschedule.communication.messaging.models import BroadcastMessage

        message = BroadcastMessage()
        message.target_owners = False
        message.target_managers = True
        message.target_staff = True
        message.target_customers = False

        mock_manager = Mock()
        mock_manager.exists.return_value = True
        mock_manager.count.return_value = 3

        with patch.object(type(message), 'individual_recipients', create=True) as mock_m2m:
            mock_m2m.__get__ = Mock(return_value=mock_manager)

            result = message.get_target_description()

        assert 'Managers' in result
        assert 'Staff' in result
        assert '3 Individual(s)' in result


class TestBroadcastMessageChoices:
    """Test BroadcastMessage choice enums."""

    def test_target_type_choices_exist(self):
        """Should have TargetType choices."""
        from smoothschedule.communication.messaging.models import BroadcastMessage

        assert hasattr(BroadcastMessage, 'TargetType')
        assert BroadcastMessage.TargetType.ALL_OWNERS == 'all_owners'
        assert BroadcastMessage.TargetType.ALL_MANAGERS == 'all_managers'
        assert BroadcastMessage.TargetType.ALL_STAFF == 'all_staff'
        assert BroadcastMessage.TargetType.ALL_CUSTOMERS == 'all_customers'
        assert BroadcastMessage.TargetType.SELECTED_USERS == 'selected_users'

    def test_delivery_method_choices_exist(self):
        """Should have DeliveryMethod choices."""
        from smoothschedule.communication.messaging.models import BroadcastMessage

        assert hasattr(BroadcastMessage, 'DeliveryMethod')
        assert BroadcastMessage.DeliveryMethod.IN_APP == 'in_app'
        assert BroadcastMessage.DeliveryMethod.EMAIL == 'email'
        assert BroadcastMessage.DeliveryMethod.SMS == 'sms'
        assert BroadcastMessage.DeliveryMethod.ALL == 'all'

    def test_status_choices_exist(self):
        """Should have Status choices."""
        from smoothschedule.communication.messaging.models import BroadcastMessage

        assert hasattr(BroadcastMessage, 'Status')
        assert BroadcastMessage.Status.DRAFT == 'draft'
        assert BroadcastMessage.Status.SCHEDULED == 'scheduled'
        assert BroadcastMessage.Status.SENDING == 'sending'
        assert BroadcastMessage.Status.SENT == 'sent'
        assert BroadcastMessage.Status.FAILED == 'failed'


class TestMessageRecipientModel:
    """Test MessageRecipient model methods."""

    def test_str_method(self):
        """Should return message subject and user email."""
        from smoothschedule.communication.messaging.models import MessageRecipient

        recipient = MessageRecipient()

        mock_message = Mock()
        mock_message.subject = 'Weekly Newsletter'
        recipient._state.fields_cache['message'] = mock_message

        mock_user = Mock()
        mock_user.email = 'user@example.com'
        recipient._state.fields_cache['user'] = mock_user

        result = str(recipient)

        assert 'Weekly Newsletter' in result
        assert 'user@example.com' in result
        assert '->' in result

    def test_delivery_status_choices_exist(self):
        """Should have DeliveryStatus choices."""
        from smoothschedule.communication.messaging.models import MessageRecipient

        assert hasattr(MessageRecipient, 'DeliveryStatus')
        assert MessageRecipient.DeliveryStatus.PENDING == 'pending'
        assert MessageRecipient.DeliveryStatus.DELIVERED == 'delivered'
        assert MessageRecipient.DeliveryStatus.FAILED == 'failed'
        assert MessageRecipient.DeliveryStatus.BOUNCED == 'bounced'


class TestCommunicationSessionMeta:
    """Test CommunicationSession Meta options."""

    def test_app_label(self):
        """Should have correct app label."""
        from smoothschedule.communication.messaging.models import CommunicationSession

        assert CommunicationSession._meta.app_label == 'messaging'

    def test_ordering(self):
        """Should order by -created_at."""
        from smoothschedule.communication.messaging.models import CommunicationSession

        assert CommunicationSession._meta.ordering == ['-created_at']


class TestBroadcastMessageMeta:
    """Test BroadcastMessage Meta options."""

    def test_app_label(self):
        """Should have correct app label."""
        from smoothschedule.communication.messaging.models import BroadcastMessage

        assert BroadcastMessage._meta.app_label == 'messaging'

    def test_ordering(self):
        """Should order by -created_at."""
        from smoothschedule.communication.messaging.models import BroadcastMessage

        assert BroadcastMessage._meta.ordering == ['-created_at']


class TestMessageRecipientMeta:
    """Test MessageRecipient Meta options."""

    def test_app_label(self):
        """Should have correct app label."""
        from smoothschedule.communication.messaging.models import MessageRecipient

        assert MessageRecipient._meta.app_label == 'messaging'

    def test_unique_together(self):
        """Should enforce unique message-user pairs."""
        from smoothschedule.communication.messaging.models import MessageRecipient

        assert MessageRecipient._meta.unique_together == (('message', 'user'),)
