"""
Unit tests for Schedule serializers.

Tests serializer validation and transformation logic.
"""
from unittest.mock import Mock, patch
from rest_framework.test import APIRequestFactory
from decimal import Decimal
from datetime import datetime, timedelta, date, time
import pytest

from smoothschedule.scheduling.schedule.serializers import (
    ResourceTypeSerializer,
    CustomerSerializer,
    StaffSerializer,
    ServiceSerializer,
    ResourceSerializer,
    EventSerializer,
    ParticipantSerializer,
    TimeBlockSerializer,
    HolidaySerializer,
    PluginInstallationSerializer,
)


class TestResourceTypeSerializer:
    """Test ResourceTypeSerializer validation."""

    def test_read_only_fields(self):
        """Test that correct fields are read-only."""
        serializer = ResourceTypeSerializer()

        assert serializer.fields['id'].read_only
        assert serializer.fields['created_at'].read_only
        assert serializer.fields['updated_at'].read_only
        assert serializer.fields['is_default'].read_only

    def test_writable_fields(self):
        """Test that correct fields are writable."""
        serializer = ResourceTypeSerializer()
        writable = [f for f in serializer.fields if not serializer.fields[f].read_only]

        assert 'name' in writable
        assert 'description' in writable
        assert 'category' in writable
        assert 'icon_name' in writable

    def test_validate_allows_renaming_default_type(self):
        """Test that default types can be renamed."""
        # Arrange
        mock_instance = Mock()
        mock_instance.is_default = True
        mock_instance.name = 'Staff'

        serializer = ResourceTypeSerializer(instance=mock_instance)

        # Act - renaming is allowed
        attrs = {'name': 'Team Members'}
        result = serializer.validate(attrs)

        # Assert
        assert result == attrs

    def test_delete_validation_blocks_default_types(self):
        """Test that default types cannot be deleted."""
        # Arrange
        mock_instance = Mock()
        mock_instance.is_default = True

        serializer = ResourceTypeSerializer()

        # Act & Assert
        with pytest.raises(Exception):  # ValidationError in real usage
            serializer.delete(mock_instance)

    def test_delete_validation_blocks_types_in_use(self):
        """Test that types with resources cannot be deleted."""
        # Arrange
        mock_instance = Mock()
        mock_instance.is_default = False
        mock_instance.name = 'Custom Type'
        mock_instance.resources.exists.return_value = True
        mock_instance.resources.count.return_value = 3

        serializer = ResourceTypeSerializer()

        # Act & Assert
        with pytest.raises(Exception):  # ValidationError in real usage
            serializer.delete(mock_instance)


class TestCustomerSerializer:
    """Test CustomerSerializer."""

    def test_read_only_fields(self):
        """Test that correct fields are read-only."""
        serializer = CustomerSerializer()

        assert serializer.fields['id'].read_only
        assert serializer.fields['name'].read_only
        assert serializer.fields['total_spend'].read_only
        assert serializer.fields['last_visit'].read_only
        assert serializer.fields['status'].read_only

    def test_get_name_returns_full_name(self):
        """Test that get_name returns user's full name."""
        # Arrange
        mock_user = Mock()
        mock_user.full_name = 'John Doe'

        serializer = CustomerSerializer()

        # Act
        name = serializer.get_name(mock_user)

        # Assert
        assert name == 'John Doe'

    def test_get_status_returns_active_for_active_user(self):
        """Test that get_status returns Active for active users."""
        # Arrange
        mock_user = Mock()
        mock_user.is_active = True

        serializer = CustomerSerializer()

        # Act
        status = serializer.get_status(mock_user)

        # Assert
        assert status == 'Active'

    def test_get_status_returns_inactive_for_inactive_user(self):
        """Test that get_status returns Inactive for inactive users."""
        # Arrange
        mock_user = Mock()
        mock_user.is_active = False

        serializer = CustomerSerializer()

        # Act
        status = serializer.get_status(mock_user)

        # Assert
        assert status == 'Inactive'

    def test_get_user_data_returns_masquerade_info(self):
        """Test that get_user_data returns info needed for masquerading."""
        # Arrange
        mock_user = Mock()
        mock_user.id = 42
        mock_user.username = 'johndoe'
        mock_user.full_name = 'John Doe'
        mock_user.email = 'john@example.com'

        serializer = CustomerSerializer()

        # Act
        user_data = serializer.get_user_data(mock_user)

        # Assert
        assert user_data['id'] == 42
        assert user_data['username'] == 'johndoe'
        assert user_data['name'] == 'John Doe'
        assert user_data['email'] == 'john@example.com'
        assert user_data['role'] == 'customer'


class TestStaffSerializer:
    """Test StaffSerializer."""

    def test_read_only_fields(self):
        """Test that correct fields are read-only."""
        serializer = StaffSerializer()

        assert serializer.fields['id'].read_only
        assert serializer.fields['username'].read_only
        assert serializer.fields['email'].read_only
        assert serializer.fields['role'].read_only
        assert serializer.fields['can_invite_staff'].read_only

    def test_get_role_maps_tenant_owner(self):
        """Test that TENANT_OWNER maps to owner."""
        # Arrange
        mock_user = Mock()
        mock_user.role = 'TENANT_OWNER'

        serializer = StaffSerializer()

        # Act
        role = serializer.get_role(mock_user)

        # Assert
        assert role == 'owner'

    def test_get_role_maps_tenant_manager(self):
        """Test that TENANT_MANAGER maps to manager."""
        # Arrange
        mock_user = Mock()
        mock_user.role = 'TENANT_MANAGER'

        serializer = StaffSerializer()

        # Act
        role = serializer.get_role(mock_user)

        # Assert
        assert role == 'manager'

    def test_get_role_maps_tenant_staff(self):
        """Test that TENANT_STAFF maps to staff."""
        # Arrange
        mock_user = Mock()
        mock_user.role = 'TENANT_STAFF'

        serializer = StaffSerializer()

        # Act
        role = serializer.get_role(mock_user)

        # Assert
        assert role == 'staff'

    def test_get_can_invite_staff_calls_model_method(self):
        """Test that can_invite_staff calls the model method."""
        # Arrange
        mock_user = Mock()
        mock_user.can_invite_staff.return_value = True

        serializer = StaffSerializer()

        # Act
        can_invite = serializer.get_can_invite_staff(mock_user)

        # Assert
        assert can_invite is True
        mock_user.can_invite_staff.assert_called_once()


class TestServiceSerializer:
    """Test ServiceSerializer."""

    def test_read_only_fields(self):
        """Test that correct fields are read-only."""
        serializer = ServiceSerializer()

        assert serializer.fields['duration_minutes'].read_only
        assert serializer.fields['deposit_display'].read_only
        assert serializer.fields['requires_deposit'].read_only
        assert serializer.fields['requires_saved_payment_method'].read_only

    def test_duration_minutes_source(self):
        """Test that duration_minutes comes from duration field."""
        serializer = ServiceSerializer()
        assert serializer.fields['duration_minutes'].source == 'duration'

    def test_get_deposit_display_fixed_amount(self):
        """Test deposit_display for fixed amount deposits."""
        mock_service = Mock()
        mock_service.deposit_amount = Decimal('25.00')
        mock_service.deposit_percent = None

        serializer = ServiceSerializer()
        display = serializer.get_deposit_display(mock_service)

        assert display == "$25.00 deposit"

    def test_get_deposit_display_percentage(self):
        """Test deposit_display for percentage deposits."""
        mock_service = Mock()
        mock_service.deposit_amount = None
        mock_service.deposit_percent = Decimal('20.00')

        serializer = ServiceSerializer()
        display = serializer.get_deposit_display(mock_service)

        assert display == "20.00% deposit"

    def test_get_deposit_display_none(self):
        """Test deposit_display when no deposit is required."""
        mock_service = Mock()
        mock_service.deposit_amount = None
        mock_service.deposit_percent = None

        serializer = ServiceSerializer()
        display = serializer.get_deposit_display(mock_service)

        assert display is None

    def test_validate_rejects_variable_pricing_with_percentage_deposit(self):
        """Test that variable pricing cannot use percentage deposits."""
        serializer = ServiceSerializer()
        attrs = {
            'variable_pricing': True,
            'deposit_percent': Decimal('20.00')
        }

        with pytest.raises(Exception) as exc_info:
            serializer.validate(attrs)

        assert 'deposit_percent' in str(exc_info.value)

    def test_validate_rejects_deposit_percent_over_100(self):
        """Test that deposit percentage cannot exceed 100%."""
        serializer = ServiceSerializer()
        attrs = {
            'deposit_percent': Decimal('150.00')
        }

        with pytest.raises(Exception) as exc_info:
            serializer.validate(attrs)

        assert 'deposit_percent' in str(exc_info.value)

    def test_validate_allows_valid_percentage_deposit(self):
        """Test that valid percentage deposits are allowed."""
        serializer = ServiceSerializer()
        attrs = {
            'variable_pricing': False,
            'deposit_percent': Decimal('50.00')
        }

        result = serializer.validate(attrs)
        assert result == attrs

    def test_validate_allows_variable_pricing_with_fixed_deposit(self):
        """Test that variable pricing can use fixed amount deposits."""
        serializer = ServiceSerializer()
        attrs = {
            'variable_pricing': True,
            'deposit_amount': Decimal('50.00')
        }

        result = serializer.validate(attrs)
        assert result == attrs


class TestResourceSerializer:
    """Test ResourceSerializer."""

    def test_read_only_fields(self):
        """Test that correct fields are read-only."""
        serializer = ResourceSerializer()

        assert serializer.fields['created_at'].read_only
        assert serializer.fields['updated_at'].read_only
        assert serializer.fields['is_archived_by_quota'].read_only
        assert serializer.fields['user_name'].read_only
        assert serializer.fields['capacity_description'].read_only

    def test_writable_fields(self):
        """Test that correct fields are writable."""
        serializer = ResourceSerializer()
        writable = [f for f in serializer.fields if not serializer.fields[f].read_only]

        assert 'name' in writable
        assert 'type' in writable
        assert 'description' in writable
        assert 'max_concurrent_events' in writable
        assert 'buffer_duration' in writable
        assert 'is_active' in writable
        assert 'user_id' in writable

    def test_get_capacity_description_unlimited(self):
        """Test capacity description for unlimited capacity."""
        mock_resource = Mock()
        mock_resource.max_concurrent_events = 0

        serializer = ResourceSerializer()
        description = serializer.get_capacity_description(mock_resource)

        assert description == "Unlimited capacity"

    def test_get_capacity_description_exclusive(self):
        """Test capacity description for exclusive use."""
        mock_resource = Mock()
        mock_resource.max_concurrent_events = 1

        serializer = ResourceSerializer()
        description = serializer.get_capacity_description(mock_resource)

        assert description == "Exclusive use (1 at a time)"

    def test_get_capacity_description_concurrent(self):
        """Test capacity description for concurrent events."""
        mock_resource = Mock()
        mock_resource.max_concurrent_events = 5

        serializer = ResourceSerializer()
        description = serializer.get_capacity_description(mock_resource)

        assert description == "Up to 5 concurrent events"

    def test_to_representation_includes_user_id(self):
        """Test that to_representation includes user_id."""
        mock_resource = Mock()
        mock_resource.id = 1
        mock_resource.name = "Test Resource"
        mock_resource.type = 1
        mock_resource.description = "Test"
        mock_resource.user_id = 42
        mock_resource.max_concurrent_events = 1
        mock_resource.buffer_duration = timedelta(minutes=15)
        mock_resource.saved_lane_count = 1
        mock_resource.is_active = True
        mock_resource.is_archived_by_quota = False
        mock_resource.user_can_edit_schedule = False
        mock_resource.created_at = datetime.now()
        mock_resource.updated_at = datetime.now()
        mock_resource.user = None

        serializer = ResourceSerializer()
        with patch.object(serializer, 'get_capacity_description', return_value="Test"):
            result = serializer.to_representation(mock_resource)

        assert result['user_id'] == 42


class TestEventSerializer:
    """Test EventSerializer."""

    def test_read_only_fields(self):
        """Test that correct fields are read-only in Meta class."""
        # Test Meta.read_only_fields directly to avoid field instantiation issues
        # with TimezoneSerializerMixin (which adds business_timezone SerializerMethodField)
        assert 'created_at' in EventSerializer.Meta.read_only_fields
        assert 'updated_at' in EventSerializer.Meta.read_only_fields
        assert 'created_by' in EventSerializer.Meta.read_only_fields

        # These are SerializerMethodFields which are always read-only by nature:
        # participants, duration_minutes, resource_id, customer_id, service_id,
        # customer_name, service_name, is_paid

    def test_write_only_fields(self):
        """Test that correct fields are write-only."""
        # Verify write-only fields are declared on the serializer class
        # These are defined as class-level field declarations with write_only=True
        # Check that the fields exist in the Meta.fields list and have write_only=True
        assert 'resource_ids' in EventSerializer.Meta.fields
        assert 'staff_ids' in EventSerializer.Meta.fields
        assert 'customer' in EventSerializer.Meta.fields
        assert 'service' in EventSerializer.Meta.fields

        # Verify the field objects themselves have write_only=True
        # Access via __dict__ to avoid triggering field instantiation via .fields property
        assert EventSerializer._declared_fields['resource_ids'].write_only is True
        assert EventSerializer._declared_fields['staff_ids'].write_only is True
        assert EventSerializer._declared_fields['customer'].write_only is True
        assert EventSerializer._declared_fields['service'].write_only is True

    def test_get_duration_minutes(self):
        """Test duration_minutes calculation."""
        mock_event = Mock()
        mock_event.duration = timedelta(minutes=90)

        serializer = EventSerializer()
        duration = serializer.get_duration_minutes(mock_event)

        assert duration == 90

    def test_get_resource_id_returns_first_resource(self):
        """Test that get_resource_id returns first resource participant."""
        mock_participant = Mock()
        mock_participant.object_id = 123

        mock_event = Mock()
        mock_event.participants.filter.return_value.first.return_value = mock_participant

        serializer = EventSerializer()
        resource_id = serializer.get_resource_id(mock_event)

        assert resource_id == 123
        mock_event.participants.filter.assert_called_with(role='RESOURCE')

    def test_get_resource_id_returns_none_when_no_resource(self):
        """Test that get_resource_id returns None when no resource participant."""
        mock_event = Mock()
        mock_event.participants.filter.return_value.first.return_value = None

        serializer = EventSerializer()
        resource_id = serializer.get_resource_id(mock_event)

        assert resource_id is None

    def test_get_customer_id_returns_customer(self):
        """Test that get_customer_id returns customer participant."""
        mock_participant = Mock()
        mock_participant.object_id = 456

        mock_event = Mock()
        mock_event.participants.filter.return_value.first.return_value = mock_participant

        serializer = EventSerializer()
        customer_id = serializer.get_customer_id(mock_event)

        assert customer_id == 456
        mock_event.participants.filter.assert_called_with(role='CUSTOMER')

    def test_get_customer_name_from_participant(self):
        """Test customer name extraction from participant."""
        mock_user = Mock()
        mock_user.full_name = "John Doe"

        mock_participant = Mock()
        mock_participant.content_object = mock_user

        mock_event = Mock()
        mock_event.participants.filter.return_value.first.return_value = mock_participant
        mock_event.title = "Test Event"

        serializer = EventSerializer()
        name = serializer.get_customer_name(mock_event)

        assert name == "John Doe"

    def test_get_customer_name_fallback_to_title(self):
        """Test customer name fallback when no participant."""
        mock_event = Mock()
        mock_event.participants.filter.return_value.first.return_value = None
        mock_event.title = "Jane Smith - Haircut"

        serializer = EventSerializer()
        name = serializer.get_customer_name(mock_event)

        assert name == "Jane Smith"

    def test_get_service_name_from_service_fk(self):
        """Test service name from service foreign key."""
        mock_service = Mock()
        mock_service.name = "Haircut"

        mock_event = Mock()
        mock_event.service = mock_service
        mock_event.title = "Test"

        serializer = EventSerializer()
        name = serializer.get_service_name(mock_event)

        assert name == "Haircut"

    def test_get_service_name_fallback_to_title(self):
        """Test service name fallback when no service FK."""
        mock_event = Mock()
        mock_event.service = None
        mock_event.title = "Customer - Massage"

        serializer = EventSerializer()
        name = serializer.get_service_name(mock_event)

        assert name == "Massage"

    def test_get_is_paid_returns_true_for_paid_status(self):
        """Test is_paid returns True for PAID status."""
        mock_event = Mock()
        mock_event.status = 'PAID'

        serializer = EventSerializer()
        is_paid = serializer.get_is_paid(mock_event)

        assert is_paid is True

    def test_get_is_paid_returns_false_for_other_status(self):
        """Test is_paid returns False for non-PAID status."""
        mock_event = Mock()
        mock_event.status = 'SCHEDULED'

        serializer = EventSerializer()
        is_paid = serializer.get_is_paid(mock_event)

        assert is_paid is False

    def test_validate_status_maps_pending_to_scheduled(self):
        """Test status mapping from PENDING to SCHEDULED."""
        serializer = EventSerializer()
        result = serializer.validate_status('PENDING')

        assert result == 'SCHEDULED'

    def test_validate_status_maps_confirmed_to_scheduled(self):
        """Test status mapping from CONFIRMED to SCHEDULED."""
        serializer = EventSerializer()
        result = serializer.validate_status('CONFIRMED')

        assert result == 'SCHEDULED'

    def test_validate_status_maps_cancelled_to_canceled(self):
        """Test status mapping from CANCELLED to CANCELED."""
        serializer = EventSerializer()
        result = serializer.validate_status('CANCELLED')

        assert result == 'CANCELED'

    def test_validate_status_maps_no_show_to_noshow(self):
        """Test status mapping from NO_SHOW to NOSHOW."""
        serializer = EventSerializer()
        result = serializer.validate_status('NO_SHOW')

        assert result == 'NOSHOW'

    def test_to_representation_maps_scheduled_to_confirmed(self):
        """Test reverse status mapping in serialization."""
        # Test the status mapping logic directly without invoking full serializer
        # (Avoids issues with TimezoneSerializerMixin and full inheritance chain)
        serializer = EventSerializer()

        # Verify the reverse mapping exists
        assert EventSerializer.STATUS_REVERSE_MAPPING['SCHEDULED'] == 'CONFIRMED'
        assert EventSerializer.STATUS_REVERSE_MAPPING['CANCELED'] == 'CANCELLED'
        assert EventSerializer.STATUS_REVERSE_MAPPING['NOSHOW'] == 'NO_SHOW'

        # Test the actual mapping logic would work by simulating to_representation
        # The serializer's to_representation applies the reverse mapping
        test_data = {'status': 'SCHEDULED'}
        mapped_status = EventSerializer.STATUS_REVERSE_MAPPING.get(test_data['status'], test_data['status'])
        assert mapped_status == 'CONFIRMED'

    def test_validate_rejects_end_before_start(self):
        """Test validation rejects end_time before start_time."""
        serializer = EventSerializer()
        attrs = {
            'start_time': datetime(2024, 1, 1, 10, 0),
            'end_time': datetime(2024, 1, 1, 9, 0),
            'resource_ids': []
        }

        with pytest.raises(Exception) as exc_info:
            serializer.validate(attrs)

        assert 'end_time' in str(exc_info.value)

    def test_validate_rejects_past_events_for_new_events(self):
        """Test validation rejects past start times for new events."""
        from django.utils import timezone
        serializer = EventSerializer()
        past_time = timezone.now() - timedelta(days=1)
        attrs = {
            'start_time': past_time,
            'end_time': past_time + timedelta(hours=1),
            'resource_ids': []
        }

        with pytest.raises(Exception) as exc_info:
            serializer.validate(attrs)

        assert 'start_time' in str(exc_info.value) or 'past' in str(exc_info.value).lower()


class TestParticipantSerializer:
    """Test ParticipantSerializer."""

    def test_read_only_fields(self):
        """Test that correct fields are read-only."""
        serializer = ParticipantSerializer()

        assert serializer.fields['created_at'].read_only
        assert serializer.fields['content_type_str'].read_only
        assert serializer.fields['participant_display'].read_only

    def test_get_content_type_str(self):
        """Test content_type_str returns string representation."""
        mock_content_type = Mock()
        mock_content_type.__str__ = Mock(return_value="resource")

        mock_participant = Mock()
        mock_participant.content_type = mock_content_type

        serializer = ParticipantSerializer()
        result = serializer.get_content_type_str(mock_participant)

        assert result == "resource"

    def test_get_participant_display_with_object(self):
        """Test participant_display with valid content_object."""
        mock_content = Mock()
        mock_content.__str__ = Mock(return_value="John Doe")

        mock_participant = Mock()
        mock_participant.content_object = mock_content

        serializer = ParticipantSerializer()
        result = serializer.get_participant_display(mock_participant)

        assert result == "John Doe"

    def test_get_participant_display_without_object(self):
        """Test participant_display when content_object is None."""
        mock_participant = Mock()
        mock_participant.content_object = None

        serializer = ParticipantSerializer()
        result = serializer.get_participant_display(mock_participant)

        assert result is None


class TestTimeBlockSerializer:
    """Test TimeBlockSerializer."""

    def test_read_only_fields(self):
        """Test that correct fields are read-only in Meta class."""
        # Test Meta.read_only_fields directly to avoid field instantiation issues
        # with TimezoneSerializerMixin (which adds business_timezone SerializerMethodField)
        assert 'created_by' in TimeBlockSerializer.Meta.read_only_fields
        assert 'created_at' in TimeBlockSerializer.Meta.read_only_fields
        assert 'updated_at' in TimeBlockSerializer.Meta.read_only_fields
        assert 'reviewed_by' in TimeBlockSerializer.Meta.read_only_fields
        assert 'reviewed_at' in TimeBlockSerializer.Meta.read_only_fields

        # These are SerializerMethodFields which are always read-only by nature:
        # resource_name, created_by_name, reviewed_by_name, level, pattern_display,
        # holiday_name, conflict_count

    def test_get_created_by_name_with_user(self):
        """Test created_by_name with valid user."""
        mock_user = Mock()
        mock_user.get_full_name.return_value = "John Doe"

        mock_block = Mock()
        mock_block.created_by = mock_user

        serializer = TimeBlockSerializer()
        name = serializer.get_created_by_name(mock_block)

        assert name == "John Doe"

    def test_get_created_by_name_without_user(self):
        """Test created_by_name when no user."""
        mock_block = Mock()
        mock_block.created_by = None

        serializer = TimeBlockSerializer()
        name = serializer.get_created_by_name(mock_block)

        assert name is None

    def test_get_reviewed_by_name_with_user(self):
        """Test reviewed_by_name with valid user."""
        mock_user = Mock()
        mock_user.get_full_name.return_value = "Jane Smith"

        mock_block = Mock()
        mock_block.reviewed_by = mock_user

        serializer = TimeBlockSerializer()
        name = serializer.get_reviewed_by_name(mock_block)

        assert name == "Jane Smith"

    def test_get_level_returns_business_when_no_resource(self):
        """Test level returns 'business' when no resource."""
        mock_block = Mock()
        mock_block.resource = None

        serializer = TimeBlockSerializer()
        level = serializer.get_level(mock_block)

        assert level == 'business'

    def test_get_level_returns_resource_when_has_resource(self):
        """Test level returns 'resource' when resource exists."""
        mock_block = Mock()
        mock_block.resource = Mock(id=1)

        serializer = TimeBlockSerializer()
        level = serializer.get_level(mock_block)

        assert level == 'resource'

    def test_get_pattern_display_one_time_single_day(self):
        """Test pattern display for single-day one-time block."""
        mock_block = Mock()
        mock_block.recurrence_type = 'NONE'
        mock_block.start_date = date(2024, 12, 25)
        mock_block.end_date = date(2024, 12, 25)

        serializer = TimeBlockSerializer()
        pattern = serializer.get_pattern_display(mock_block)

        assert "December 25, 2024" in pattern

    def test_get_pattern_display_weekly(self):
        """Test pattern display for weekly recurrence."""
        mock_block = Mock()
        mock_block.recurrence_type = 'WEEKLY'
        mock_block.recurrence_pattern = {'days_of_week': [0, 2, 4]}  # Mon, Wed, Fri

        serializer = TimeBlockSerializer()
        pattern = serializer.get_pattern_display(mock_block)

        assert "Weekly" in pattern
        assert "Mon" in pattern
        assert "Wed" in pattern
        assert "Fri" in pattern

    def test_get_pattern_display_monthly(self):
        """Test pattern display for monthly recurrence."""
        mock_block = Mock()
        mock_block.recurrence_type = 'MONTHLY'
        mock_block.recurrence_pattern = {'days_of_month': [1, 15]}

        serializer = TimeBlockSerializer()
        pattern = serializer.get_pattern_display(mock_block)

        assert "Monthly" in pattern
        assert "1st" in pattern
        assert "15th" in pattern

    def test_validate_none_type_requires_start_date(self):
        """Test NONE recurrence type requires start_date."""
        serializer = TimeBlockSerializer()
        attrs = {
            'recurrence_type': 'NONE',
            'recurrence_pattern': {}
        }

        with pytest.raises(Exception) as exc_info:
            serializer.validate(attrs)

        assert 'start_date' in str(exc_info.value)

    def test_validate_weekly_requires_days_of_week(self):
        """Test WEEKLY recurrence requires days_of_week."""
        serializer = TimeBlockSerializer()
        attrs = {
            'recurrence_type': 'WEEKLY',
            'recurrence_pattern': {}
        }

        with pytest.raises(Exception) as exc_info:
            serializer.validate(attrs)

        assert 'days_of_week' in str(exc_info.value)

    def test_validate_weekly_rejects_invalid_days(self):
        """Test WEEKLY validation rejects invalid day numbers."""
        serializer = TimeBlockSerializer()
        attrs = {
            'recurrence_type': 'WEEKLY',
            'recurrence_pattern': {'days_of_week': [0, 7]}  # 7 is invalid
        }

        with pytest.raises(Exception) as exc_info:
            serializer.validate(attrs)

        assert 'days_of_week' in str(exc_info.value)

    def test_validate_monthly_requires_days_of_month(self):
        """Test MONTHLY recurrence requires days_of_month."""
        serializer = TimeBlockSerializer()
        attrs = {
            'recurrence_type': 'MONTHLY',
            'recurrence_pattern': {}
        }

        with pytest.raises(Exception) as exc_info:
            serializer.validate(attrs)

        assert 'days_of_month' in str(exc_info.value)

    def test_validate_monthly_rejects_invalid_days(self):
        """Test MONTHLY validation rejects invalid day numbers."""
        serializer = TimeBlockSerializer()
        attrs = {
            'recurrence_type': 'MONTHLY',
            'recurrence_pattern': {'days_of_month': [1, 32]}  # 32 is invalid
        }

        with pytest.raises(Exception) as exc_info:
            serializer.validate(attrs)

        assert 'days_of_month' in str(exc_info.value)

    def test_validate_yearly_requires_month_and_day(self):
        """Test YEARLY recurrence requires month and day."""
        serializer = TimeBlockSerializer()
        attrs = {
            'recurrence_type': 'YEARLY',
            'recurrence_pattern': {'month': 12}  # Missing day
        }

        with pytest.raises(Exception) as exc_info:
            serializer.validate(attrs)

        assert 'month and day' in str(exc_info.value)

    def test_validate_yearly_rejects_invalid_month(self):
        """Test YEARLY validation rejects invalid month."""
        serializer = TimeBlockSerializer()
        attrs = {
            'recurrence_type': 'YEARLY',
            'recurrence_pattern': {'month': 13, 'day': 1}
        }

        with pytest.raises(Exception) as exc_info:
            serializer.validate(attrs)

        assert 'month' in str(exc_info.value)

    def test_validate_not_all_day_requires_times(self):
        """Test non-all-day blocks require start and end times."""
        serializer = TimeBlockSerializer()
        attrs = {
            'recurrence_type': 'NONE',
            'start_date': date(2024, 1, 1),
            'all_day': False,
            'recurrence_pattern': {}
        }

        with pytest.raises(Exception) as exc_info:
            serializer.validate(attrs)

        assert 'start_time' in str(exc_info.value)

    def test_validate_end_time_after_start_time(self):
        """Test end_time must be after start_time."""
        serializer = TimeBlockSerializer()
        attrs = {
            'recurrence_type': 'NONE',
            'start_date': date(2024, 1, 1),
            'all_day': False,
            'start_time': time(10, 0),
            'end_time': time(9, 0),  # Before start
            'recurrence_pattern': {}
        }

        with pytest.raises(Exception) as exc_info:
            serializer.validate(attrs)

        assert 'end_time' in str(exc_info.value)


class TestHolidaySerializer:
    """Test HolidaySerializer."""

    def test_all_fields_read_only(self):
        """Test that all fields are read-only (reference data)."""
        serializer = HolidaySerializer()

        # Holidays are reference data, all fields should be read-only
        for field_name, field in serializer.fields.items():
            assert field.read_only, f"Field {field_name} should be read-only"

    def test_get_next_occurrence_current_year(self):
        """Test next_occurrence for holiday later this year."""
        mock_holiday = Mock()
        mock_holiday.get_date_for_year = Mock()

        # Set up mock to return future date for current year
        today = date.today()
        future_date = date(today.year, 12, 25)
        mock_holiday.get_date_for_year.return_value = future_date

        serializer = HolidaySerializer()
        result = serializer.get_next_occurrence(mock_holiday)

        assert result == future_date.isoformat()

    def test_get_next_occurrence_next_year(self):
        """Test next_occurrence falls back to next year if past."""
        mock_holiday = Mock()

        today = date.today()
        past_date = date(today.year, 1, 1)
        next_year_date = date(today.year + 1, 1, 1)

        # Return past date for current year, future date for next year
        mock_holiday.get_date_for_year = Mock(side_effect=[past_date, next_year_date])

        serializer = HolidaySerializer()
        result = serializer.get_next_occurrence(mock_holiday)

        assert result == next_year_date.isoformat()


class TestPluginInstallationSerializer:
    """Test PluginInstallationSerializer."""

    def test_read_only_fields(self):
        """Test that correct fields are read-only."""
        serializer = PluginInstallationSerializer()

        assert serializer.fields['id'].read_only
        assert serializer.fields['installed_by'].read_only
        assert serializer.fields['installed_by_name'].read_only
        assert serializer.fields['installed_at'].read_only
        assert serializer.fields['template_version_hash'].read_only
        assert serializer.fields['reviewed_at'].read_only
        assert serializer.fields['template_name'].read_only
        assert serializer.fields['template_slug'].read_only
        assert serializer.fields['has_update'].read_only

    def test_get_installed_by_name_with_user(self):
        """Test installed_by_name with valid user."""
        mock_user = Mock()
        mock_user.get_full_name.return_value = "Alice Jones"
        mock_user.username = "alice"

        mock_installation = Mock()
        mock_installation.installed_by = mock_user

        serializer = PluginInstallationSerializer()
        name = serializer.get_installed_by_name(mock_installation)

        assert name == "Alice Jones"

    def test_get_installed_by_name_falls_back_to_username(self):
        """Test installed_by_name falls back to username."""
        mock_user = Mock()
        mock_user.get_full_name.return_value = ""
        mock_user.username = "bob"

        mock_installation = Mock()
        mock_installation.installed_by = mock_user

        serializer = PluginInstallationSerializer()
        name = serializer.get_installed_by_name(mock_installation)

        assert name == "bob"

    def test_get_installed_by_name_without_user(self):
        """Test installed_by_name when no user."""
        mock_installation = Mock()
        mock_installation.installed_by = None

        serializer = PluginInstallationSerializer()
        name = serializer.get_installed_by_name(mock_installation)

        assert name is None

    def test_get_has_update_calls_model_method(self):
        """Test has_update calls the model's method."""
        mock_installation = Mock()
        mock_installation.has_update_available.return_value = True

        serializer = PluginInstallationSerializer()
        has_update = serializer.get_has_update(mock_installation)

        assert has_update is True
        mock_installation.has_update_available.assert_called_once()


class TestScheduledTaskSerializer:
    """Test ScheduledTaskSerializer."""

    def test_get_created_by_name_with_full_name(self):
        """Test created_by_name returns full name."""
        from smoothschedule.scheduling.schedule.serializers import ScheduledTaskSerializer

        mock_user = Mock()
        mock_user.get_full_name.return_value = "John Doe"

        mock_task = Mock()
        mock_task.created_by = mock_user

        serializer = ScheduledTaskSerializer()
        name = serializer.get_created_by_name(mock_task)

        assert name == "John Doe"

    def test_get_created_by_name_falls_back_to_username(self):
        """Test created_by_name falls back to username if no full name."""
        from smoothschedule.scheduling.schedule.serializers import ScheduledTaskSerializer

        mock_user = Mock()
        mock_user.get_full_name.return_value = ""
        mock_user.username = "jdoe"

        mock_task = Mock()
        mock_task.created_by = mock_user

        serializer = ScheduledTaskSerializer()
        name = serializer.get_created_by_name(mock_task)

        assert name == "jdoe"

    def test_get_created_by_name_returns_none_without_user(self):
        """Test created_by_name returns None when no user."""
        from smoothschedule.scheduling.schedule.serializers import ScheduledTaskSerializer

        mock_task = Mock()
        mock_task.created_by = None

        serializer = ScheduledTaskSerializer()
        name = serializer.get_created_by_name(mock_task)

        assert name is None

    @patch('smoothschedule.scheduling.automations.registry.registry')
    def test_get_plugin_display_name_from_registry(self, mock_registry):
        """Test plugin_display_name returns display name from registry."""
        from smoothschedule.scheduling.schedule.serializers import ScheduledTaskSerializer

        mock_plugin = Mock()
        mock_plugin.display_name = "Backup Plugin"
        mock_registry.get.return_value = mock_plugin

        mock_task = Mock()
        mock_task.plugin_name = "backup"

        serializer = ScheduledTaskSerializer()
        name = serializer.get_plugin_display_name(mock_task)

        assert name == "Backup Plugin"
        mock_registry.get.assert_called_with("backup")

    @patch('smoothschedule.scheduling.automations.registry.registry')
    def test_get_plugin_display_name_falls_back_to_plugin_name(self, mock_registry):
        """Test plugin_display_name falls back when not in registry."""
        from smoothschedule.scheduling.schedule.serializers import ScheduledTaskSerializer

        mock_registry.get.return_value = None

        mock_task = Mock()
        mock_task.plugin_name = "unknown_plugin"

        serializer = ScheduledTaskSerializer()
        name = serializer.get_plugin_display_name(mock_task)

        assert name == "unknown_plugin"

    def test_validate_cron_requires_expression(self):
        """Test CRON schedule type requires cron_expression."""
        from smoothschedule.scheduling.schedule.serializers import ScheduledTaskSerializer
        from smoothschedule.scheduling.schedule.models import ScheduledTask

        serializer = ScheduledTaskSerializer()
        attrs = {
            'schedule_type': ScheduledTask.ScheduleType.CRON,
            'cron_expression': ''
        }

        with pytest.raises(Exception) as exc_info:
            serializer.validate(attrs)

        assert 'cron_expression' in str(exc_info.value)

    def test_validate_interval_requires_minutes(self):
        """Test INTERVAL schedule type requires interval_minutes."""
        from smoothschedule.scheduling.schedule.serializers import ScheduledTaskSerializer
        from smoothschedule.scheduling.schedule.models import ScheduledTask

        serializer = ScheduledTaskSerializer()
        attrs = {
            'schedule_type': ScheduledTask.ScheduleType.INTERVAL,
            'interval_minutes': None
        }

        with pytest.raises(Exception) as exc_info:
            serializer.validate(attrs)

        assert 'interval_minutes' in str(exc_info.value)

    def test_validate_one_time_requires_run_at(self):
        """Test ONE_TIME schedule type requires run_at."""
        from smoothschedule.scheduling.schedule.serializers import ScheduledTaskSerializer
        from smoothschedule.scheduling.schedule.models import ScheduledTask

        serializer = ScheduledTaskSerializer()
        attrs = {
            'schedule_type': ScheduledTask.ScheduleType.ONE_TIME,
            'run_at': None
        }

        with pytest.raises(Exception) as exc_info:
            serializer.validate(attrs)

        assert 'run_at' in str(exc_info.value)

    @patch('smoothschedule.scheduling.automations.registry.registry')
    def test_validate_plugin_name_exists(self, mock_registry):
        """Test plugin_name validation passes for existing plugin."""
        from smoothschedule.scheduling.schedule.serializers import ScheduledTaskSerializer

        mock_registry.get.return_value = Mock()

        serializer = ScheduledTaskSerializer()
        result = serializer.validate_plugin_name("backup")

        assert result == "backup"

    @patch('smoothschedule.scheduling.automations.registry.registry')
    def test_validate_plugin_name_rejects_unknown(self, mock_registry):
        """Test plugin_name validation fails for unknown plugin."""
        from smoothschedule.scheduling.schedule.serializers import ScheduledTaskSerializer

        mock_registry.get.return_value = None

        serializer = ScheduledTaskSerializer()

        with pytest.raises(Exception) as exc_info:
            serializer.validate_plugin_name("unknown_plugin")

        assert 'not found' in str(exc_info.value)

    def test_validate_plugin_config_requires_dict(self):
        """Test plugin_config must be a dictionary."""
        from smoothschedule.scheduling.schedule.serializers import ScheduledTaskSerializer

        serializer = ScheduledTaskSerializer()

        with pytest.raises(Exception) as exc_info:
            serializer.validate_plugin_config("not a dict")

        assert 'dictionary' in str(exc_info.value)

    def test_validate_plugin_config_accepts_dict(self):
        """Test plugin_config accepts valid dictionary."""
        from smoothschedule.scheduling.schedule.serializers import ScheduledTaskSerializer

        serializer = ScheduledTaskSerializer()
        config = {'key': 'value'}

        result = serializer.validate_plugin_config(config)
        assert result == config


class TestServiceSerializerInternal:
    """Test ServiceSerializer internal methods."""

    def test_to_internal_value_converts_price_to_cents(self):
        """Test price is converted from dollars to cents."""
        # Arrange data with price in dollars
        data = {
            'name': 'Test Service',
            'duration': 60,
            'price': '25.50',  # $25.50
        }

        # Act - using the serializer's to_internal_value logic
        from decimal import Decimal
        price = Decimal(str(data['price']))
        price_cents = int(price * 100)

        # Assert
        assert price_cents == 2550

    def test_to_internal_value_converts_deposit_to_cents(self):
        """Test deposit_amount is converted from dollars to cents."""
        # Arrange data with deposit in dollars
        data = {
            'name': 'Test Service',
            'duration': 60,
            'deposit_amount': '10.00',  # $10.00
        }

        # Act - using the serializer's to_internal_value logic
        from decimal import Decimal
        deposit = Decimal(str(data['deposit_amount']))
        deposit_cents = int(deposit * 100)

        # Assert
        assert deposit_cents == 1000

    def test_get_resource_names_empty_when_all_resources(self):
        """Test resource_names returns empty when all_resources is True."""
        mock_service = Mock()
        mock_service.all_resources = True
        mock_service.resource_ids = [1, 2, 3]

        serializer = ServiceSerializer()
        names = serializer.get_resource_names(mock_service)

        assert names == []

    def test_get_resource_names_empty_when_no_ids(self):
        """Test resource_names returns empty when no resource_ids."""
        mock_service = Mock()
        mock_service.all_resources = False
        mock_service.resource_ids = []

        serializer = ServiceSerializer()
        names = serializer.get_resource_names(mock_service)

        assert names == []


class TestEventSerializerVariablePricing:
    """Test EventSerializer variable pricing methods."""

    def test_get_is_variable_pricing(self):
        """Test is_variable_pricing from model property."""
        mock_event = Mock()
        mock_event.is_variable_pricing = True

        serializer = EventSerializer()
        result = serializer.get_is_variable_pricing(mock_event)

        assert result is True

    def test_get_remaining_balance_with_value(self):
        """Test remaining_balance formatting."""
        from decimal import Decimal
        mock_event = Mock()
        mock_event.remaining_balance = Decimal('50.00')

        serializer = EventSerializer()
        result = serializer.get_remaining_balance(mock_event)

        assert result == '50.00'

    def test_get_remaining_balance_none(self):
        """Test remaining_balance when None."""
        mock_event = Mock()
        mock_event.remaining_balance = None

        serializer = EventSerializer()
        result = serializer.get_remaining_balance(mock_event)

        assert result is None

    def test_get_overpaid_amount_with_value(self):
        """Test overpaid_amount formatting."""
        from decimal import Decimal
        mock_event = Mock()
        mock_event.overpaid_amount = Decimal('10.00')

        serializer = EventSerializer()
        result = serializer.get_overpaid_amount(mock_event)

        assert result == '10.00'

    def test_get_overpaid_amount_none(self):
        """Test overpaid_amount when None."""
        mock_event = Mock()
        mock_event.overpaid_amount = None

        serializer = EventSerializer()
        result = serializer.get_overpaid_amount(mock_event)

        assert result is None

    def test_get_service_id_from_event(self):
        """Test service_id extraction."""
        mock_event = Mock()
        mock_event.service_id = 42

        serializer = EventSerializer()
        result = serializer.get_service_id(mock_event)

        assert result == 42

    def test_get_service_id_none(self):
        """Test service_id when no service."""
        mock_event = Mock()
        mock_event.service_id = None

        serializer = EventSerializer()
        result = serializer.get_service_id(mock_event)

        assert result is None

    def test_validate_status_accepts_backend_value(self):
        """Test validate_status accepts valid backend values."""
        serializer = EventSerializer()

        # Patch Event.Status to provide valid statuses
        with patch('smoothschedule.scheduling.schedule.serializers.Event') as MockEvent:
            MockEvent.Status.__iter__ = Mock(return_value=iter([Mock(value='SCHEDULED'), Mock(value='CANCELED')]))

            # Backend values should pass through unchanged
            result = serializer.validate_status('SCHEDULED')
            assert result == 'SCHEDULED'

    def test_validate_status_rejects_invalid(self):
        """Test validate_status rejects invalid values."""
        serializer = EventSerializer()

        with patch('smoothschedule.scheduling.schedule.serializers.Event') as MockEvent:
            MockEvent.Status.__iter__ = Mock(return_value=iter([Mock(value='SCHEDULED')]))

            with pytest.raises(Exception) as exc_info:
                serializer.validate_status('INVALID_STATUS')

            assert 'Invalid status' in str(exc_info.value)


class TestResourceSerializerMethods:
    """Test ResourceSerializer internal methods."""

    def test_is_staff_type_with_staff_category(self):
        """Test _is_staff_type returns True for staff category."""
        from smoothschedule.scheduling.schedule.models import ResourceType

        mock_type = Mock()
        mock_type.category = ResourceType.Category.STAFF

        serializer = ResourceSerializer()
        result = serializer._is_staff_type(resource_type=mock_type)

        assert result is True

    def test_is_staff_type_with_legacy_staff_type(self):
        """Test _is_staff_type returns True for legacy STAFF type."""
        from smoothschedule.scheduling.schedule.models import Resource

        serializer = ResourceSerializer()
        result = serializer._is_staff_type(legacy_type=Resource.Type.STAFF)

        assert result is True

    def test_is_staff_type_returns_false_for_non_staff(self):
        """Test _is_staff_type returns False for non-staff types."""
        from smoothschedule.scheduling.schedule.models import ResourceType

        mock_type = Mock()
        mock_type.category = ResourceType.Category.OTHER  # OTHER = non-staff

        serializer = ResourceSerializer()
        result = serializer._is_staff_type(resource_type=mock_type)

        assert result is False

    def test_is_staff_type_returns_false_when_none(self):
        """Test _is_staff_type returns False when no type provided."""
        serializer = ResourceSerializer()
        result = serializer._is_staff_type()

        assert result is False

    def test_validate_requires_user_for_staff_type(self):
        """Test validation requires user_id for staff type resources."""
        from smoothschedule.scheduling.schedule.models import Resource

        serializer = ResourceSerializer()
        attrs = {
            'type': Resource.Type.STAFF,
            'user_id': None
        }

        with pytest.raises(Exception) as exc_info:
            serializer.validate(attrs)

        assert 'user_id' in str(exc_info.value)


class TestCustomerSerializerCreate:
    """Test CustomerSerializer create method."""

    def test_create_uses_email_as_username(self):
        """Test that create uses email as username."""
        serializer = CustomerSerializer()

        validated_data = {
            'email': 'Test@Example.com',
        }

        # Simulate the create logic
        import uuid
        email = validated_data.get('email', '')
        if email:
            validated_data['username'] = email.lower()
        else:
            validated_data['username'] = f"customer_{uuid.uuid4().hex[:8]}"

        assert validated_data['username'] == 'test@example.com'

    def test_create_generates_uuid_username_when_no_email(self):
        """Test that create generates UUID username when no email."""
        serializer = CustomerSerializer()

        validated_data = {
            'email': '',
        }

        # Simulate the create logic
        import uuid
        email = validated_data.get('email', '')
        if email:
            validated_data['username'] = email.lower()
        else:
            validated_data['username'] = f"customer_{uuid.uuid4().hex[:8]}"

        assert validated_data['username'].startswith('customer_')
        assert len(validated_data['username']) == 17  # "customer_" (9) + hex (8)


class TestCustomerSerializerMethodFields:
    """Test CustomerSerializer method fields."""

    def test_get_name_returns_full_name(self):
        """Test get_name returns user's full name."""
        serializer = CustomerSerializer()
        mock_obj = Mock()
        mock_obj.full_name = 'John Doe'

        result = serializer.get_name(mock_obj)
        assert result == 'John Doe'

    def test_get_total_spend_returns_zero(self):
        """Test get_total_spend returns zero (TODO)."""
        serializer = CustomerSerializer()
        mock_obj = Mock()

        result = serializer.get_total_spend(mock_obj)
        assert result == 0

    def test_get_last_visit_returns_none(self):
        """Test get_last_visit returns None (TODO)."""
        serializer = CustomerSerializer()
        mock_obj = Mock()

        result = serializer.get_last_visit(mock_obj)
        assert result is None

    def test_get_status_returns_active_when_active(self):
        """Test get_status returns Active for active users."""
        serializer = CustomerSerializer()
        mock_obj = Mock()
        mock_obj.is_active = True

        result = serializer.get_status(mock_obj)
        assert result == 'Active'

    def test_get_status_returns_inactive_when_inactive(self):
        """Test get_status returns Inactive for inactive users."""
        serializer = CustomerSerializer()
        mock_obj = Mock()
        mock_obj.is_active = False

        result = serializer.get_status(mock_obj)
        assert result == 'Inactive'

    def test_get_avatar_url_returns_none(self):
        """Test get_avatar_url returns None (TODO)."""
        serializer = CustomerSerializer()
        mock_obj = Mock()

        result = serializer.get_avatar_url(mock_obj)
        assert result is None

    def test_get_tags_returns_empty_list(self):
        """Test get_tags returns empty list (TODO)."""
        serializer = CustomerSerializer()
        mock_obj = Mock()

        result = serializer.get_tags(mock_obj)
        assert result == []

    def test_get_city_returns_empty_string(self):
        """Test get_city returns empty string (TODO)."""
        serializer = CustomerSerializer()
        mock_obj = Mock()

        result = serializer.get_city(mock_obj)
        assert result == ''

    def test_get_state_returns_empty_string(self):
        """Test get_state returns empty string (TODO)."""
        serializer = CustomerSerializer()
        mock_obj = Mock()

        result = serializer.get_state(mock_obj)
        assert result == ''

    def test_get_zip_returns_empty_string(self):
        """Test get_zip returns empty string (TODO)."""
        serializer = CustomerSerializer()
        mock_obj = Mock()

        result = serializer.get_zip(mock_obj)
        assert result == ''

    def test_get_user_data_returns_dict(self):
        """Test get_user_data returns user data dict."""
        serializer = CustomerSerializer()
        mock_obj = Mock()
        mock_obj.id = 123
        mock_obj.username = 'testuser'
        mock_obj.full_name = 'Test User'
        mock_obj.email = 'test@example.com'

        result = serializer.get_user_data(mock_obj)

        assert result['id'] == 123
        assert result['username'] == 'testuser'
        assert result['name'] == 'Test User'
        assert result['email'] == 'test@example.com'
        assert result['role'] == 'customer'


class TestServiceSerializerToInternalValue:
    """Test ServiceSerializer to_internal_value method."""

    def test_converts_price_to_cents(self):
        """Test that price in dollars is converted to price_cents."""
        serializer = ServiceSerializer()

        data = {
            'name': 'Test Service',
            'price': '25.50',
        }

        # Mock the parent to_internal_value to return the data
        with patch.object(ServiceSerializer, 'to_internal_value') as mock_parent:
            # Can't easily test this without a full Django context
            # So just verify the serializer has the method
            assert hasattr(serializer, 'to_internal_value')

    def test_serializer_has_to_internal_value(self):
        """Test ServiceSerializer has to_internal_value method."""
        serializer = ServiceSerializer()
        assert hasattr(serializer, 'to_internal_value')
        assert callable(serializer.to_internal_value)


class TestServiceSerializerGetResourceNames:
    """Test ServiceSerializer get_resource_names method."""

    def test_returns_empty_when_all_resources_true(self):
        """Test returns empty list when all_resources is True."""
        serializer = ServiceSerializer()
        mock_obj = Mock()
        mock_obj.all_resources = True

        result = serializer.get_resource_names(mock_obj)
        assert result == []

    def test_returns_empty_when_no_resource_ids(self):
        """Test returns empty list when resource_ids is empty."""
        serializer = ServiceSerializer()
        mock_obj = Mock()
        mock_obj.all_resources = False
        mock_obj.resource_ids = []

        result = serializer.get_resource_names(mock_obj)
        assert result == []


class TestServiceSerializerDepositDisplay:
    """Test ServiceSerializer deposit display."""

    def test_get_deposit_display_with_amount(self):
        """Test deposit display with fixed amount."""
        serializer = ServiceSerializer()
        mock_obj = Mock()
        mock_obj.deposit_amount = Decimal('25.00')
        mock_obj.deposit_percent = None

        result = serializer.get_deposit_display(mock_obj)
        assert result == '$25.00 deposit'

    def test_get_deposit_display_with_percent(self):
        """Test deposit display with percentage."""
        serializer = ServiceSerializer()
        mock_obj = Mock()
        mock_obj.deposit_amount = None
        mock_obj.deposit_percent = 20

        result = serializer.get_deposit_display(mock_obj)
        assert result == '20% deposit'

    def test_get_deposit_display_returns_none_when_no_deposit(self):
        """Test deposit display returns None when no deposit."""
        serializer = ServiceSerializer()
        mock_obj = Mock()
        mock_obj.deposit_amount = None
        mock_obj.deposit_percent = None

        result = serializer.get_deposit_display(mock_obj)
        assert result is None

    def test_get_deposit_display_returns_none_when_zero_amount(self):
        """Test deposit display returns None when deposit is zero."""
        serializer = ServiceSerializer()
        mock_obj = Mock()
        mock_obj.deposit_amount = Decimal('0.00')
        mock_obj.deposit_percent = 0

        result = serializer.get_deposit_display(mock_obj)
        assert result is None


class TestStaffSerializerMethodFields:
    """Test StaffSerializer method fields."""

    def test_get_name_returns_full_name(self):
        """Test get_name returns full name."""
        serializer = StaffSerializer()
        mock_obj = Mock()
        mock_obj.full_name = 'Jane Smith'

        result = serializer.get_name(mock_obj)
        assert result == 'Jane Smith'

    def test_get_role_maps_tenant_manager_to_manager(self):
        """Test get_role maps TENANT_MANAGER to manager."""
        serializer = StaffSerializer()
        mock_obj = Mock()
        mock_obj.role = 'TENANT_MANAGER'

        result = serializer.get_role(mock_obj)
        assert result == 'manager'


class TestResourceSerializerFields:
    """Test ResourceSerializer fields."""

    def test_serializer_has_expected_fields(self):
        """Test serializer has expected fields."""
        serializer = ResourceSerializer()
        assert 'id' in serializer.fields
        assert 'name' in serializer.fields
        assert 'type' in serializer.fields

    def test_id_is_read_only(self):
        """Test id field is read-only."""
        serializer = ResourceSerializer()
        assert serializer.fields['id'].read_only


class TestTimeBlockSerializerValidation:
    """Test TimeBlockSerializer validation."""

    def test_serializer_has_validate_method(self):
        """Test that serializer has validate method."""
        serializer = TimeBlockSerializer()
        assert hasattr(serializer, 'validate')
        assert callable(serializer.validate)


class TestEventSerializerFields:
    """Test EventSerializer fields."""

    def test_serializer_exists(self):
        """Test EventSerializer can be imported."""
        assert EventSerializer is not None

    def test_serializer_has_meta(self):
        """Test serializer has Meta class."""
        assert hasattr(EventSerializer, 'Meta')


class TestPluginInstallationSerializerFields:
    """Test PluginInstallationSerializer fields."""

    def test_has_expected_fields(self):
        """Test serializer has expected fields."""
        serializer = PluginInstallationSerializer()

        # Check key fields exist
        assert 'id' in serializer.fields
        assert 'template' in serializer.fields
        assert 'config_values' in serializer.fields

    def test_id_is_read_only(self):
        """Test id field is read-only."""
        serializer = PluginInstallationSerializer()
        assert serializer.fields['id'].read_only


class TestCustomerSerializerCreate:
    """Test CustomerSerializer create method."""

    def test_has_create_method(self):
        """Test serializer has create method."""
        serializer = CustomerSerializer()
        assert hasattr(serializer, 'create')
        assert callable(serializer.create)


class TestCustomerSerializerGetMethods:
    """Test CustomerSerializer get methods."""

    def test_get_total_spend_returns_zero(self):
        """Test get_total_spend returns 0 (TODO implementation)."""
        serializer = CustomerSerializer()
        mock_obj = Mock()

        result = serializer.get_total_spend(mock_obj)
        assert result == 0

    def test_get_last_visit_returns_none(self):
        """Test get_last_visit returns None (TODO implementation)."""
        serializer = CustomerSerializer()
        mock_obj = Mock()

        result = serializer.get_last_visit(mock_obj)
        assert result is None

    def test_get_avatar_url_returns_none(self):
        """Test get_avatar_url returns None (TODO implementation)."""
        serializer = CustomerSerializer()
        mock_obj = Mock()

        result = serializer.get_avatar_url(mock_obj)
        assert result is None

    def test_get_tags_returns_empty_list(self):
        """Test get_tags returns empty list (TODO implementation)."""
        serializer = CustomerSerializer()
        mock_obj = Mock()

        result = serializer.get_tags(mock_obj)
        assert result == []

    def test_get_city_returns_empty_string(self):
        """Test get_city returns empty string (TODO implementation)."""
        serializer = CustomerSerializer()
        mock_obj = Mock()

        result = serializer.get_city(mock_obj)
        assert result == ''

    def test_get_state_returns_empty_string(self):
        """Test get_state returns empty string (TODO implementation)."""
        serializer = CustomerSerializer()
        mock_obj = Mock()

        result = serializer.get_state(mock_obj)
        assert result == ''


class TestResourceSerializerValidation:
    """Test ResourceSerializer validation."""

    def test_has_validate_method(self):
        """Test serializer has validate method."""
        serializer = ResourceSerializer()
        assert hasattr(serializer, 'validate')

    def test_has_update_method(self):
        """Test serializer has update method."""
        serializer = ResourceSerializer()
        assert hasattr(serializer, 'update')

    def test_has_create_method(self):
        """Test serializer has create method."""
        serializer = ResourceSerializer()
        assert hasattr(serializer, 'create')


class TestResourceSerializerGetMethods:
    """Test ResourceSerializer get methods."""

    def test_fields_exist(self):
        """Test serializer has expected fields."""
        serializer = ResourceSerializer()
        assert 'id' in serializer.fields
        assert 'name' in serializer.fields


class TestEventSerializerMethods:
    """Test EventSerializer methods."""

    def test_serializer_exists(self):
        """Test EventSerializer can be instantiated."""
        serializer = EventSerializer()
        assert serializer is not None

    def test_has_meta_class_with_fields(self):
        """Test serializer has Meta with fields defined."""
        assert hasattr(EventSerializer, 'Meta')
        assert hasattr(EventSerializer.Meta, 'model')


class TestParticipantSerializerMethods:
    """Test ParticipantSerializer methods."""

    def test_serializer_exists(self):
        """Test ParticipantSerializer exists."""
        assert ParticipantSerializer is not None

    def test_has_meta_class(self):
        """Test serializer has Meta class."""
        assert hasattr(ParticipantSerializer, 'Meta')


class TestHolidaySerializerMethods:
    """Test HolidaySerializer methods."""

    def test_serializer_exists(self):
        """Test HolidaySerializer exists."""
        assert HolidaySerializer is not None

    def test_has_meta_class(self):
        """Test serializer has Meta class."""
        assert hasattr(HolidaySerializer, 'Meta')


class TestStaffSerializerUpdatePermissions:
    """Test StaffSerializer update and permissions."""

    def test_has_update_method(self):
        """Test serializer has update method."""
        serializer = StaffSerializer()
        assert hasattr(serializer, 'update')
        assert callable(serializer.update)

    def test_has_validate_method(self):
        """Test serializer has validate method."""
        serializer = StaffSerializer()
        assert hasattr(serializer, 'validate')
        assert callable(serializer.validate)


class TestServiceSerializerValidationExtended:
    """Test ServiceSerializer validation and fields."""

    def test_has_validate_method(self):
        """Test serializer has validate method."""
        serializer = ServiceSerializer()
        assert hasattr(serializer, 'validate')
        assert callable(serializer.validate)

    def test_has_expected_fields(self):
        """Test serializer has expected fields."""
        serializer = ServiceSerializer()
        assert 'id' in serializer.fields
        assert 'name' in serializer.fields
        assert 'duration_minutes' in serializer.fields
        assert 'deposit_display' in serializer.fields


class TestCustomerSerializerCreateMethod:
    """Test CustomerSerializer.create method."""

    def test_create_sets_email_as_username(self):
        """Test create uses email as username when provided."""
        serializer = CustomerSerializer()

        validated_data = {
            'email': 'customer@example.com',
            'first_name': 'John',
            'last_name': 'Doe',
        }

        with patch.object(CustomerSerializer, 'create', wraps=serializer.create) as mock_create:
            with patch('smoothschedule.scheduling.schedule.serializers.serializers.ModelSerializer.create') as mock_super_create:
                mock_super_create.return_value = Mock(id=1)

                serializer.create(validated_data)

        # Email should be lowercased and set as username
        assert validated_data['username'] == 'customer@example.com'

    def test_create_generates_uuid_username_when_no_email(self):
        """Test create generates UUID username when email is empty."""
        serializer = CustomerSerializer()

        validated_data = {
            'email': '',
            'first_name': 'John',
        }

        with patch('smoothschedule.scheduling.schedule.serializers.serializers.ModelSerializer.create') as mock_super_create:
            mock_super_create.return_value = Mock(id=1)

            serializer.create(validated_data)

        # Should generate a customer_ prefixed username
        assert validated_data['username'].startswith('customer_')
        assert len(validated_data['username']) == len('customer_') + 8  # 8 hex chars

    def test_create_generates_uuid_username_when_email_not_provided(self):
        """Test create generates UUID username when email key is missing."""
        serializer = CustomerSerializer()

        validated_data = {
            'first_name': 'John',
        }

        with patch('smoothschedule.scheduling.schedule.serializers.serializers.ModelSerializer.create') as mock_super_create:
            mock_super_create.return_value = Mock(id=1)

            serializer.create(validated_data)

        # Should generate a customer_ prefixed username
        assert validated_data['username'].startswith('customer_')


class TestCustomerSerializerGetMethods:
    """Test CustomerSerializer get_* methods."""

    def test_get_total_spend_returns_zero(self):
        """Test get_total_spend returns 0 (placeholder)."""
        serializer = CustomerSerializer()
        mock_obj = Mock()

        result = serializer.get_total_spend(mock_obj)

        assert result == 0

    def test_get_last_visit_returns_none(self):
        """Test get_last_visit returns None (placeholder)."""
        serializer = CustomerSerializer()
        mock_obj = Mock()

        result = serializer.get_last_visit(mock_obj)

        assert result is None

    def test_get_avatar_url_returns_none(self):
        """Test get_avatar_url returns None (placeholder)."""
        serializer = CustomerSerializer()
        mock_obj = Mock()

        result = serializer.get_avatar_url(mock_obj)

        assert result is None

    def test_get_tags_returns_empty_list(self):
        """Test get_tags returns empty list (placeholder)."""
        serializer = CustomerSerializer()
        mock_obj = Mock()

        result = serializer.get_tags(mock_obj)

        assert result == []

    def test_get_city_returns_empty_string(self):
        """Test get_city returns empty string (placeholder)."""
        serializer = CustomerSerializer()
        mock_obj = Mock()

        result = serializer.get_city(mock_obj)

        assert result == ''

    def test_get_state_returns_empty_string(self):
        """Test get_state returns empty string (placeholder)."""
        serializer = CustomerSerializer()
        mock_obj = Mock()

        result = serializer.get_state(mock_obj)

        assert result == ''

    def test_get_zip_returns_empty_string(self):
        """Test get_zip returns empty string (placeholder)."""
        serializer = CustomerSerializer()
        mock_obj = Mock()

        result = serializer.get_zip(mock_obj)

        assert result == ''


class TestServiceSerializerInternalValue:
    """Test ServiceSerializer.to_internal_value price/deposit conversion."""

    def test_converts_price_to_cents(self):
        """Should convert price dollars to price_cents."""
        from smoothschedule.scheduling.schedule.serializers import ServiceSerializer
        data = {
            'name': 'Test Service',
            'duration': 60,
            'price': '25.00',
        }

        serializer = ServiceSerializer(data=data)
        with patch.object(ServiceSerializer, 'is_valid', return_value=True):
            internal = serializer.to_internal_value(data)

        assert internal.get('price_cents') == 2500
        assert 'price' not in internal

    def test_converts_deposit_amount_to_cents(self):
        """Should convert deposit_amount to deposit_amount_cents."""
        from smoothschedule.scheduling.schedule.serializers import ServiceSerializer
        data = {
            'name': 'Test Service',
            'duration': 60,
            'deposit_amount': '10.50',
        }

        serializer = ServiceSerializer(data=data)
        with patch.object(ServiceSerializer, 'is_valid', return_value=True):
            internal = serializer.to_internal_value(data)

        assert internal.get('deposit_amount_cents') == 1050
        assert 'deposit_amount' not in internal

    def test_handles_null_deposit_amount(self):
        """Should handle None deposit_amount."""
        from smoothschedule.scheduling.schedule.serializers import ServiceSerializer
        data = {
            'name': 'Test Service',
            'duration': 60,
            'deposit_amount': None,
        }

        serializer = ServiceSerializer(data=data)
        with patch.object(ServiceSerializer, 'is_valid', return_value=True):
            internal = serializer.to_internal_value(data)

        assert internal.get('deposit_amount_cents') is None


class TestEventPluginSerializerMethods:
    """Test EventPluginSerializer methods."""

    def test_get_execution_time_with_time(self):
        """Should return ISO format execution time."""
        from smoothschedule.scheduling.schedule.serializers import EventPluginSerializer
        mock_obj = Mock()
        exec_time = datetime(2024, 6, 15, 10, 30, 0)
        mock_obj.get_execution_time.return_value = exec_time

        serializer = EventPluginSerializer()
        result = serializer.get_execution_time(mock_obj)

        assert result == exec_time.isoformat()

    def test_get_execution_time_returns_none(self):
        """Should return None when no execution time."""
        from smoothschedule.scheduling.schedule.serializers import EventPluginSerializer
        mock_obj = Mock()
        mock_obj.get_execution_time.return_value = None

        serializer = EventPluginSerializer()
        result = serializer.get_execution_time(mock_obj)

        assert result is None

    def test_get_timing_description_before_start_no_offset(self):
        """Should return 'At start' for before_start with 0 offset."""
        from smoothschedule.scheduling.schedule.serializers import EventPluginSerializer
        from smoothschedule.scheduling.schedule.models import EventPlugin
        mock_obj = Mock()
        mock_obj.trigger = EventPlugin.Trigger.BEFORE_START
        mock_obj.offset_minutes = 0

        serializer = EventPluginSerializer()
        result = serializer.get_timing_description(mock_obj)

        assert result == "At start"

    def test_get_timing_description_before_start_with_offset(self):
        """Should return 'X min before start' for before_start with offset."""
        from smoothschedule.scheduling.schedule.serializers import EventPluginSerializer
        from smoothschedule.scheduling.schedule.models import EventPlugin
        mock_obj = Mock()
        mock_obj.trigger = EventPlugin.Trigger.BEFORE_START
        mock_obj.offset_minutes = 10

        serializer = EventPluginSerializer()
        result = serializer.get_timing_description(mock_obj)

        assert result == "10 min before start"

    def test_get_timing_description_at_start_no_offset(self):
        """Should return 'At start' for at_start with 0 offset."""
        from smoothschedule.scheduling.schedule.serializers import EventPluginSerializer
        from smoothschedule.scheduling.schedule.models import EventPlugin
        mock_obj = Mock()
        mock_obj.trigger = EventPlugin.Trigger.AT_START
        mock_obj.offset_minutes = 0

        serializer = EventPluginSerializer()
        result = serializer.get_timing_description(mock_obj)

        assert result == "At start"

    def test_get_timing_description_at_start_with_offset(self):
        """Should return 'X min after start' for at_start with offset."""
        from smoothschedule.scheduling.schedule.serializers import EventPluginSerializer
        from smoothschedule.scheduling.schedule.models import EventPlugin
        mock_obj = Mock()
        mock_obj.trigger = EventPlugin.Trigger.AT_START
        mock_obj.offset_minutes = 15

        serializer = EventPluginSerializer()
        result = serializer.get_timing_description(mock_obj)

        assert result == "15 min after start"

    def test_get_timing_description_after_start_no_offset(self):
        """Should return 'At start' for after_start with 0 offset."""
        from smoothschedule.scheduling.schedule.serializers import EventPluginSerializer
        from smoothschedule.scheduling.schedule.models import EventPlugin
        mock_obj = Mock()
        mock_obj.trigger = EventPlugin.Trigger.AFTER_START
        mock_obj.offset_minutes = 0

        serializer = EventPluginSerializer()
        result = serializer.get_timing_description(mock_obj)

        assert result == "At start"

    def test_get_timing_description_after_start_with_offset(self):
        """Should return 'X min after start' for after_start with offset."""
        from smoothschedule.scheduling.schedule.serializers import EventPluginSerializer
        from smoothschedule.scheduling.schedule.models import EventPlugin
        mock_obj = Mock()
        mock_obj.trigger = EventPlugin.Trigger.AFTER_START
        mock_obj.offset_minutes = 30

        serializer = EventPluginSerializer()
        result = serializer.get_timing_description(mock_obj)

        assert result == "30 min after start"

    def test_get_timing_description_after_end_no_offset(self):
        """Should return 'At end' for after_end with 0 offset."""
        from smoothschedule.scheduling.schedule.serializers import EventPluginSerializer
        from smoothschedule.scheduling.schedule.models import EventPlugin
        mock_obj = Mock()
        mock_obj.trigger = EventPlugin.Trigger.AFTER_END
        mock_obj.offset_minutes = 0

        serializer = EventPluginSerializer()
        result = serializer.get_timing_description(mock_obj)

        assert result == "At end"

    def test_get_timing_description_after_end_with_offset(self):
        """Should return 'X min after end' for after_end with offset."""
        from smoothschedule.scheduling.schedule.serializers import EventPluginSerializer
        from smoothschedule.scheduling.schedule.models import EventPlugin
        mock_obj = Mock()
        mock_obj.trigger = EventPlugin.Trigger.AFTER_END
        mock_obj.offset_minutes = 5

        serializer = EventPluginSerializer()
        result = serializer.get_timing_description(mock_obj)

        assert result == "5 min after end"

    def test_get_timing_description_on_complete(self):
        """Should return 'When completed' for on_complete trigger."""
        from smoothschedule.scheduling.schedule.serializers import EventPluginSerializer
        from smoothschedule.scheduling.schedule.models import EventPlugin
        mock_obj = Mock()
        mock_obj.trigger = EventPlugin.Trigger.ON_COMPLETE
        mock_obj.offset_minutes = 0

        serializer = EventPluginSerializer()
        result = serializer.get_timing_description(mock_obj)

        assert result == "When completed"

    def test_get_timing_description_on_cancel(self):
        """Should return 'When canceled' for on_cancel trigger."""
        from smoothschedule.scheduling.schedule.serializers import EventPluginSerializer
        from smoothschedule.scheduling.schedule.models import EventPlugin
        mock_obj = Mock()
        mock_obj.trigger = EventPlugin.Trigger.ON_CANCEL
        mock_obj.offset_minutes = 0

        serializer = EventPluginSerializer()
        result = serializer.get_timing_description(mock_obj)

        assert result == "When canceled"

    def test_get_timing_description_unknown_trigger(self):
        """Should return 'Unknown' for unrecognized trigger."""
        from smoothschedule.scheduling.schedule.serializers import EventPluginSerializer
        mock_obj = Mock()
        mock_obj.trigger = 'INVALID_TRIGGER'
        mock_obj.offset_minutes = 0

        serializer = EventPluginSerializer()
        result = serializer.get_timing_description(mock_obj)

        assert result == "Unknown"

    def test_validate_resets_offset_for_on_complete(self):
        """Should reset offset to 0 for on_complete trigger."""
        from smoothschedule.scheduling.schedule.serializers import EventPluginSerializer
        from smoothschedule.scheduling.schedule.models import EventPlugin

        serializer = EventPluginSerializer()
        attrs = {
            'trigger': EventPlugin.Trigger.ON_COMPLETE,
            'offset_minutes': 15
        }

        result = serializer.validate(attrs)

        assert result['offset_minutes'] == 0

    def test_validate_resets_offset_for_on_cancel(self):
        """Should reset offset to 0 for on_cancel trigger."""
        from smoothschedule.scheduling.schedule.serializers import EventPluginSerializer
        from smoothschedule.scheduling.schedule.models import EventPlugin

        serializer = EventPluginSerializer()
        attrs = {
            'trigger': EventPlugin.Trigger.ON_CANCEL,
            'offset_minutes': 30
        }

        result = serializer.validate(attrs)

        assert result['offset_minutes'] == 0


class TestTimeBlockSerializerOrdinal:
    """Test TimeBlockSerializer._ordinal method."""

    def test_ordinal_first(self):
        """Should return '1st' for 1."""
        serializer = TimeBlockSerializer()
        assert serializer._ordinal(1) == '1st'

    def test_ordinal_second(self):
        """Should return '2nd' for 2."""
        serializer = TimeBlockSerializer()
        assert serializer._ordinal(2) == '2nd'

    def test_ordinal_third(self):
        """Should return '3rd' for 3."""
        serializer = TimeBlockSerializer()
        assert serializer._ordinal(3) == '3rd'

    def test_ordinal_fourth(self):
        """Should return '4th' for 4."""
        serializer = TimeBlockSerializer()
        assert serializer._ordinal(4) == '4th'

    def test_ordinal_eleventh(self):
        """Should return '11th' for 11 (special case)."""
        serializer = TimeBlockSerializer()
        assert serializer._ordinal(11) == '11th'

    def test_ordinal_twelfth(self):
        """Should return '12th' for 12 (special case)."""
        serializer = TimeBlockSerializer()
        assert serializer._ordinal(12) == '12th'

    def test_ordinal_thirteenth(self):
        """Should return '13th' for 13 (special case)."""
        serializer = TimeBlockSerializer()
        assert serializer._ordinal(13) == '13th'

    def test_ordinal_twenty_first(self):
        """Should return '21st' for 21."""
        serializer = TimeBlockSerializer()
        assert serializer._ordinal(21) == '21st'


class TestTimeBlockPatternDisplay:
    """Test TimeBlockSerializer.get_pattern_display for various types."""

    def test_monthly_with_days(self):
        """Should return 'Monthly on the Xth, Yth' for monthly with days."""
        from smoothschedule.scheduling.schedule.models import TimeBlock
        mock_obj = Mock()
        mock_obj.recurrence_type = TimeBlock.RecurrenceType.MONTHLY
        mock_obj.recurrence_pattern = {'days_of_month': [1, 15]}

        serializer = TimeBlockSerializer()
        result = serializer.get_pattern_display(mock_obj)

        assert result == 'Monthly on the 1st, 15th'

    def test_monthly_without_days(self):
        """Should return 'Monthly' for monthly without specific days."""
        from smoothschedule.scheduling.schedule.models import TimeBlock
        mock_obj = Mock()
        mock_obj.recurrence_type = TimeBlock.RecurrenceType.MONTHLY
        mock_obj.recurrence_pattern = {}

        serializer = TimeBlockSerializer()
        result = serializer.get_pattern_display(mock_obj)

        assert result == 'Monthly'

    def test_yearly_with_month_and_day(self):
        """Should return 'Yearly on Month Day' for yearly with date."""
        from smoothschedule.scheduling.schedule.models import TimeBlock
        mock_obj = Mock()
        mock_obj.recurrence_type = TimeBlock.RecurrenceType.YEARLY
        mock_obj.recurrence_pattern = {'month': 12, 'day': 25}

        serializer = TimeBlockSerializer()
        result = serializer.get_pattern_display(mock_obj)

        assert result == 'Yearly on December 25'

    def test_yearly_without_date(self):
        """Should return 'Yearly' for yearly without specific date."""
        from smoothschedule.scheduling.schedule.models import TimeBlock
        mock_obj = Mock()
        mock_obj.recurrence_type = TimeBlock.RecurrenceType.YEARLY
        mock_obj.recurrence_pattern = {}

        serializer = TimeBlockSerializer()
        result = serializer.get_pattern_display(mock_obj)

        assert result == 'Yearly'

    def test_holiday_with_code(self):
        """Should return holiday name for holiday type with code."""
        from smoothschedule.scheduling.schedule.models import TimeBlock, Holiday
        mock_obj = Mock()
        mock_obj.recurrence_type = TimeBlock.RecurrenceType.HOLIDAY
        mock_obj.recurrence_pattern = {'holiday_code': 'christmas'}

        serializer = TimeBlockSerializer()
        with patch.object(Holiday.objects, 'get') as mock_get:
            mock_holiday = Mock()
            mock_holiday.name = 'Christmas Day'
            mock_get.return_value = mock_holiday

            result = serializer.get_pattern_display(mock_obj)

        assert result == 'Holiday: Christmas Day'

    def test_holiday_code_not_found(self):
        """Should return code if holiday not found."""
        from smoothschedule.scheduling.schedule.models import TimeBlock, Holiday
        mock_obj = Mock()
        mock_obj.recurrence_type = TimeBlock.RecurrenceType.HOLIDAY
        mock_obj.recurrence_pattern = {'holiday_code': 'unknown_holiday'}

        serializer = TimeBlockSerializer()
        with patch.object(Holiday.objects, 'get', side_effect=Holiday.DoesNotExist):
            result = serializer.get_pattern_display(mock_obj)

        assert result == 'Holiday: unknown_holiday'

    def test_holiday_without_code(self):
        """Should return 'Holiday' for holiday without code."""
        from smoothschedule.scheduling.schedule.models import TimeBlock
        mock_obj = Mock()
        mock_obj.recurrence_type = TimeBlock.RecurrenceType.HOLIDAY
        mock_obj.recurrence_pattern = {}

        serializer = TimeBlockSerializer()
        result = serializer.get_pattern_display(mock_obj)

        assert result == 'Holiday'


class TestTimeBlockGetHolidayName:
    """Test TimeBlockSerializer.get_holiday_name method."""

    def test_returns_none_for_non_holiday_type(self):
        """Should return None for non-holiday recurrence types."""
        from smoothschedule.scheduling.schedule.models import TimeBlock
        mock_obj = Mock()
        mock_obj.recurrence_type = TimeBlock.RecurrenceType.WEEKLY
        mock_obj.recurrence_pattern = {}

        serializer = TimeBlockSerializer()
        result = serializer.get_holiday_name(mock_obj)

        assert result is None

    def test_returns_holiday_name_when_found(self):
        """Should return holiday name when code exists and found."""
        from smoothschedule.scheduling.schedule.models import TimeBlock, Holiday
        mock_obj = Mock()
        mock_obj.recurrence_type = TimeBlock.RecurrenceType.HOLIDAY
        mock_obj.recurrence_pattern = {'holiday_code': 'new_years'}

        serializer = TimeBlockSerializer()
        with patch.object(Holiday.objects, 'get') as mock_get:
            mock_holiday = Mock()
            mock_holiday.name = "New Year's Day"
            mock_get.return_value = mock_holiday

            result = serializer.get_holiday_name(mock_obj)

        assert result == "New Year's Day"

    def test_returns_none_when_holiday_not_found(self):
        """Should return None when holiday code not found."""
        from smoothschedule.scheduling.schedule.models import TimeBlock, Holiday
        mock_obj = Mock()
        mock_obj.recurrence_type = TimeBlock.RecurrenceType.HOLIDAY
        mock_obj.recurrence_pattern = {'holiday_code': 'nonexistent'}

        serializer = TimeBlockSerializer()
        with patch.object(Holiday.objects, 'get', side_effect=Holiday.DoesNotExist):
            result = serializer.get_holiday_name(mock_obj)

        assert result is None

    def test_returns_none_when_no_holiday_code(self):
        """Should return None when no holiday code in pattern."""
        from smoothschedule.scheduling.schedule.models import TimeBlock
        mock_obj = Mock()
        mock_obj.recurrence_type = TimeBlock.RecurrenceType.HOLIDAY
        mock_obj.recurrence_pattern = {}

        serializer = TimeBlockSerializer()
        result = serializer.get_holiday_name(mock_obj)

        assert result is None
