"""
Comprehensive unit tests for mobile/field app serializers.

Tests serializer field configurations, validation logic, and custom methods
using mocks to avoid database hits.
"""
from datetime import datetime, timedelta
from decimal import Decimal
from unittest.mock import Mock, MagicMock, patch
import pytest

from smoothschedule.communication.mobile.serializers import (
    ServiceSummarySerializer,
    CustomerInfoSerializer,
    JobListSerializer,
    JobDetailSerializer,
    SetStatusSerializer,
    RescheduleJobSerializer,
    StartEnRouteSerializer,
    LocationUpdateSerializer,
    LocationUpdateResponseSerializer,
    InitiateCallSerializer,
    InitiateCallResponseSerializer,
    SendSMSSerializer,
    SendSMSResponseSerializer,
    CallHistorySerializer,
    EmployeeProfileSerializer,
)
from smoothschedule.scheduling.schedule.models import Event


class TestServiceSummarySerializer:
    """Test ServiceSummarySerializer field configuration."""

    def test_serializer_fields(self):
        """Test that serializer includes correct fields."""
        serializer = ServiceSummarySerializer()
        assert set(serializer.fields.keys()) == {'id', 'name', 'duration', 'price'}

    def test_serializer_with_mock_service(self):
        """Test serialization of a service object."""
        mock_service = Mock()
        mock_service.id = 1
        mock_service.name = "Haircut"
        mock_service.duration = 60
        mock_service.price = Decimal("25.00")

        serializer = ServiceSummarySerializer(mock_service)
        data = serializer.data

        assert data['id'] == 1
        assert data['name'] == "Haircut"
        assert data['duration'] == 60
        assert data['price'] == Decimal("25.00")


class TestCustomerInfoSerializer:
    """Test CustomerInfoSerializer with various customer data scenarios."""

    def test_serializer_fields(self):
        """Test that serializer includes correct fields."""
        serializer = CustomerInfoSerializer()
        field_names = set(serializer.fields.keys())
        assert field_names == {'id', 'name', 'phone_masked', 'email'}

    def test_get_name_with_full_name_attribute(self):
        """Test get_name returns full_name when available."""
        mock_customer = MagicMock()
        mock_customer.id = 1
        mock_customer.full_name = "John Doe"
        mock_customer.email = "john@example.com"

        serializer = CustomerInfoSerializer(mock_customer)
        result = serializer.get_name(mock_customer)
        assert result == "John Doe"

    def test_get_name_with_get_full_name_method(self):
        """Test get_name calls get_full_name() when full_name attribute missing."""
        # Create a mock without full_name attribute
        mock_customer = Mock(spec=['id', 'email', 'get_full_name', 'username'])
        mock_customer.id = 1
        mock_customer.email = "john@example.com"
        mock_customer.get_full_name = Mock(return_value="Jane Smith")
        mock_customer.username = "janesmith"

        serializer = CustomerInfoSerializer(mock_customer)
        result = serializer.get_name(mock_customer)
        assert result == "Jane Smith"

    def test_get_name_fallback_to_username(self):
        """Test get_name falls back to username when no full name."""
        # Create a mock without full_name, get_full_name returns None
        mock_customer = Mock(spec=['id', 'email', 'get_full_name', 'username'])
        mock_customer.id = 1
        mock_customer.email = "user@example.com"
        mock_customer.get_full_name = Mock(return_value=None)
        mock_customer.username = "customeruser"

        serializer = CustomerInfoSerializer(mock_customer)
        result = serializer.get_name(mock_customer)
        assert result == "customeruser"

    def test_get_name_default_customer(self):
        """Test get_name returns 'Customer' when no identifiers."""
        mock_customer = Mock(spec=['id', 'email'])
        mock_customer.id = 1
        mock_customer.email = "test@example.com"

        serializer = CustomerInfoSerializer(mock_customer)
        assert serializer.data['name'] == "Customer"

    def test_get_phone_masked_with_valid_phone(self):
        """Test phone masking with a valid phone number."""
        mock_customer = Mock()
        mock_customer.id = 1
        mock_customer.full_name = "Test User"
        mock_customer.phone = "1234567890"
        mock_customer.email = "test@example.com"

        serializer = CustomerInfoSerializer(mock_customer)
        assert serializer.data['phone_masked'] == "***-***-7890"

    def test_get_phone_masked_with_short_phone(self):
        """Test phone masking returns None for phone < 4 digits."""
        mock_customer = Mock()
        mock_customer.id = 1
        mock_customer.full_name = "Test User"
        mock_customer.phone = "123"
        mock_customer.email = "test@example.com"

        serializer = CustomerInfoSerializer(mock_customer)
        assert serializer.data['phone_masked'] is None

    def test_get_phone_masked_with_no_phone(self):
        """Test phone masking returns None when phone is missing."""
        mock_customer = Mock(spec=['id', 'full_name', 'email'])
        mock_customer.id = 1
        mock_customer.full_name = "Test User"
        mock_customer.email = "test@example.com"

        serializer = CustomerInfoSerializer(mock_customer)
        assert serializer.data['phone_masked'] is None

    def test_email_can_be_null(self):
        """Test that email field allows null values."""
        mock_customer = Mock()
        mock_customer.id = 1
        mock_customer.full_name = "Test User"
        mock_customer.phone = "1234567890"
        mock_customer.email = None

        serializer = CustomerInfoSerializer(mock_customer)
        assert serializer.data['email'] is None


class TestJobListSerializer:
    """Test JobListSerializer for job list views."""

    def test_serializer_fields(self):
        """Test that serializer includes all required fields."""
        # Test Meta.fields directly to avoid field instantiation issues
        # with TimezoneSerializerMixin (which adds business_timezone SerializerMethodField)
        expected_fields = {
            'id', 'title', 'start_time', 'end_time', 'status',
            'status_display', 'service_name', 'customer_name',
            'address', 'duration_minutes', 'allowed_transitions',
            'business_timezone',
        }
        assert set(JobListSerializer.Meta.fields) == expected_fields

    def test_status_display_is_read_only(self):
        """Test that status_display field is read-only."""
        # Verify status_display is a CharField sourced from get_status_display (read-only by nature)
        # Access _declared_fields to avoid TimezoneSerializerMixin instantiation issues
        assert JobListSerializer._declared_fields['status_display'].read_only is True

    def test_get_service_name_with_service(self):
        """Test get_service_name returns service name."""
        mock_event = Mock()
        mock_service = Mock()
        mock_service.name = "Pool Cleaning"
        mock_event.service = mock_service

        serializer = JobListSerializer(mock_event)
        assert serializer.get_service_name(mock_event) == "Pool Cleaning"

    def test_get_service_name_without_service(self):
        """Test get_service_name returns None when no service."""
        mock_event = Mock()
        mock_event.service = None

        serializer = JobListSerializer(mock_event)
        assert serializer.get_service_name(mock_event) is None

    def test_get_customer_name_with_customer(self):
        """Test get_customer_name retrieves customer from participants."""
        mock_event = Mock()
        mock_event.id = 1
        mock_customer = Mock()
        mock_customer.full_name = "Alice Johnson"

        serializer = JobListSerializer(mock_event)
        serializer._customer_cache = {1: mock_customer}

        result = serializer.get_customer_name(mock_event)
        assert result == "Alice Johnson"

    def test_get_customer_name_fallback_to_username(self):
        """Test get_customer_name falls back to username."""
        mock_event = Mock()
        mock_event.id = 1
        mock_customer = Mock(spec=['username'])
        mock_customer.username = "alice"

        serializer = JobListSerializer(mock_event)
        serializer._customer_cache = {1: mock_customer}

        result = serializer.get_customer_name(mock_event)
        assert result == "alice"

    def test_get_customer_name_no_customer(self):
        """Test get_customer_name returns None when no customer."""
        mock_event = Mock()
        mock_event.id = 1

        serializer = JobListSerializer(mock_event)
        serializer._customer_cache = {1: None}

        result = serializer.get_customer_name(mock_event)
        assert result is None

    def test_get_address_from_notes(self):
        """Test get_address extracts address from event notes."""
        mock_event = Mock()
        mock_event.notes = "Address: 123 Main St, City"

        serializer = JobListSerializer(mock_event)
        result = serializer.get_address(mock_event)
        assert result == "Address: 123 Main St, City"

    def test_get_address_from_customer(self):
        """Test get_address gets address from customer."""
        mock_event = Mock()
        mock_event.id = 1
        mock_event.notes = ""
        mock_customer = Mock()
        mock_customer.address = "456 Oak Ave"

        serializer = JobListSerializer(mock_event)
        serializer._customer_cache = {1: mock_customer}

        result = serializer.get_address(mock_event)
        assert result == "456 Oak Ave"

    def test_get_address_returns_none(self):
        """Test get_address returns None when no address available."""
        mock_event = Mock()
        mock_event.id = 1
        mock_event.notes = None  # No notes

        # Customer exists but has no address attribute
        serializer = JobListSerializer(mock_event)
        serializer._customer_cache = {1: None}

        result = serializer.get_address(mock_event)
        assert result is None

    def test_get_duration_minutes_calculates_correctly(self):
        """Test get_duration_minutes calculates duration."""
        mock_event = Mock()
        mock_event.start_time = datetime(2024, 1, 1, 10, 0)
        mock_event.end_time = datetime(2024, 1, 1, 11, 30)

        serializer = JobListSerializer(mock_event)
        result = serializer.get_duration_minutes(mock_event)
        assert result == 90

    def test_get_duration_minutes_returns_none(self):
        """Test get_duration_minutes returns None when times missing."""
        mock_event = Mock()
        mock_event.start_time = None
        mock_event.end_time = None

        serializer = JobListSerializer(mock_event)
        result = serializer.get_duration_minutes(mock_event)
        assert result is None

    def test_get_allowed_transitions(self):
        """Test get_allowed_transitions fetches from StatusMachine."""
        with patch('smoothschedule.communication.mobile.services.StatusMachine') as mock_status_machine_class:
            mock_status_machine_class.VALID_TRANSITIONS = {
                Event.Status.SCHEDULED: [Event.Status.EN_ROUTE, Event.Status.CANCELED],
            }
            mock_event = Mock()
            mock_event.status = Event.Status.SCHEDULED

            serializer = JobListSerializer(mock_event)
            result = serializer.get_allowed_transitions(mock_event)

            assert result == [Event.Status.EN_ROUTE, Event.Status.CANCELED]

    def test_customer_cache_initialization(self):
        """Test _customer_cache is initialized on first access."""
        mock_event = Mock()
        mock_event.id = 1

        serializer = JobListSerializer(mock_event)
        assert not hasattr(serializer, '_customer_cache')

        # Should initialize cache when _get_customer_participant is called
        with patch('django.contrib.contenttypes.models.ContentType'):
            mock_event.participants.filter.return_value.first.return_value = None
            serializer._get_customer_participant(mock_event)
            assert hasattr(serializer, '_customer_cache')


class TestJobDetailSerializer:
    """Test JobDetailSerializer for job detail views."""

    def test_serializer_fields(self):
        """Test that serializer includes all required fields."""
        # Test Meta.fields directly to avoid field instantiation issues
        # with TimezoneSerializerMixin (which adds business_timezone SerializerMethodField)
        expected_fields = {
            'id', 'title', 'start_time', 'end_time', 'status',
            'status_display', 'notes', 'service', 'customer',
            'assigned_staff', 'duration_minutes', 'allowed_transitions',
            'can_track_location', 'has_active_call_session',
            'status_history', 'latest_location', 'deposit_amount',
            'final_price', 'created_at', 'updated_at', 'can_edit_schedule',
            'business_timezone',
        }
        assert set(JobDetailSerializer.Meta.fields) == expected_fields

    def test_service_field_is_read_only(self):
        """Test that service field is read-only."""
        # Verify service is a nested ServiceSummarySerializer with read_only=True
        # Access _declared_fields to avoid TimezoneSerializerMixin instantiation issues
        assert JobDetailSerializer._declared_fields['service'].read_only is True

    def test_get_customer_serializes_with_customer_info(self):
        """Test get_customer uses CustomerInfoSerializer."""
        mock_event = Mock()
        mock_customer = Mock()
        mock_customer.id = 1
        mock_customer.full_name = "Bob Smith"
        mock_customer.phone = "5551234567"
        mock_customer.email = "bob@example.com"

        with patch.object(JobDetailSerializer, '_get_customer_participant', return_value=mock_customer):
            serializer = JobDetailSerializer(mock_event)
            result = serializer.get_customer(mock_event)

        assert result is not None
        assert result['id'] == 1
        assert result['name'] == "Bob Smith"
        assert result['phone_masked'] == "***-***-4567"

    def test_get_customer_returns_none_when_no_customer(self):
        """Test get_customer returns None when no customer found."""
        mock_event = Mock()

        with patch.object(JobDetailSerializer, '_get_customer_participant', return_value=None):
            serializer = JobDetailSerializer(mock_event)
            result = serializer.get_customer(mock_event)

        assert result is None

    def test_get_assigned_staff_with_users(self):
        """Test get_assigned_staff retrieves staff from User participants."""
        with patch('django.contrib.contenttypes.models.ContentType'):
            mock_event = Mock()
            mock_user = Mock()
            mock_user.id = 1
            mock_user.full_name = "Staff Member"
            mock_user.username = "staffuser"

            # Mock participant
            mock_participant = Mock()
            mock_participant.content_object = mock_user

            # Setup event participants filter - first for users, second for resources
            mock_event.participants.filter.side_effect = [
                [mock_participant],  # User participants
                []  # Resource participants
            ]

            serializer = JobDetailSerializer(mock_event)
            result = serializer.get_assigned_staff(mock_event)

            assert len(result) == 1
            assert result[0]['id'] == 1
            assert result[0]['name'] == "Staff Member"
            assert result[0]['type'] == 'user'

    def test_get_assigned_staff_with_resources(self):
        """Test get_assigned_staff retrieves staff from Resource participants."""
        with patch('django.contrib.contenttypes.models.ContentType'):
            mock_event = Mock()
            mock_resource = Mock()
            mock_resource.id = 2
            mock_resource.name = "Service Van 1"
            mock_resource.user_id = 5

            # Mock participant
            mock_participant = Mock()
            mock_participant.content_object = mock_resource

            # Setup to return empty for user participants, then resource participants
            mock_event.participants.filter.side_effect = [
                [],  # User participants
                [mock_participant]  # Resource participants
            ]

            serializer = JobDetailSerializer(mock_event)
            result = serializer.get_assigned_staff(mock_event)

            assert len(result) == 1
            assert result[0]['id'] == 2
            assert result[0]['name'] == "Service Van 1"
            assert result[0]['type'] == 'resource'
            assert result[0]['user_id'] == 5

    def test_get_can_track_location(self):
        """Test get_can_track_location checks if status allows tracking."""
        with patch('smoothschedule.communication.mobile.services.StatusMachine') as mock_status_machine_class:
            mock_status_machine_class.TRACKING_STATUSES = {Event.Status.EN_ROUTE, Event.Status.IN_PROGRESS}
            mock_event = Mock()
            mock_event.status = Event.Status.EN_ROUTE

            serializer = JobDetailSerializer(mock_event)
            result = serializer.get_can_track_location(mock_event)

            assert result is True

    def test_get_can_track_location_false_for_completed(self):
        """Test get_can_track_location returns False for completed jobs."""
        with patch('smoothschedule.communication.mobile.services.StatusMachine') as mock_status_machine_class:
            mock_status_machine_class.TRACKING_STATUSES = {Event.Status.EN_ROUTE, Event.Status.IN_PROGRESS}
            mock_event = Mock()
            mock_event.status = Event.Status.COMPLETED

            serializer = JobDetailSerializer(mock_event)
            result = serializer.get_can_track_location(mock_event)

            assert result is False

    def test_get_has_active_call_session(self):
        """Test get_has_active_call_session checks for active sessions."""
        with patch('smoothschedule.communication.credits.models.MaskedSession') as mock_masked_session:
            with patch('django.utils.timezone') as mock_timezone:
                mock_now = datetime(2024, 1, 1, 12, 0)
                mock_timezone.now.return_value = mock_now
                mock_masked_session.objects.filter.return_value.exists.return_value = True

                mock_event = Mock()
                mock_event.id = 1
                mock_tenant = Mock()

                serializer = JobDetailSerializer(mock_event, context={'tenant': mock_tenant})
                result = serializer.get_has_active_call_session(mock_event)

                assert result is True

    def test_get_has_active_call_session_no_tenant(self):
        """Test get_has_active_call_session returns False when no tenant."""
        mock_event = Mock()

        serializer = JobDetailSerializer(mock_event, context={})
        result = serializer.get_has_active_call_session(mock_event)

        assert result is False

    @patch('smoothschedule.communication.mobile.serializers.EventStatusHistory')
    def test_get_status_history(self, mock_event_status_history):
        """Test get_status_history retrieves and formats history."""
        mock_history_entry = Mock()
        mock_history_entry.old_status = Event.Status.SCHEDULED
        mock_history_entry.new_status = Event.Status.EN_ROUTE
        mock_history_entry.changed_by = Mock(full_name="John Doe")
        mock_history_entry.changed_at = datetime(2024, 1, 1, 10, 0)
        mock_history_entry.notes = "Started driving"

        mock_event_status_history.objects.filter.return_value.select_related.return_value.__getitem__ = (
            lambda self, key: [mock_history_entry]
        )

        mock_event = Mock()
        mock_event.id = 1
        mock_tenant = Mock()

        serializer = JobDetailSerializer(mock_event, context={'tenant': mock_tenant})
        result = serializer.get_status_history(mock_event)

        assert isinstance(result, list)

    def test_get_status_history_no_tenant(self):
        """Test get_status_history returns empty list when no tenant."""
        mock_event = Mock()

        serializer = JobDetailSerializer(mock_event, context={})
        result = serializer.get_status_history(mock_event)

        assert result == []

    @patch('smoothschedule.communication.mobile.serializers.EmployeeLocationUpdate')
    def test_get_latest_location(self, mock_location_update):
        """Test get_latest_location retrieves and formats location."""
        mock_location = Mock()
        mock_location.latitude = Decimal("40.7128")
        mock_location.longitude = Decimal("-74.0060")
        mock_location.timestamp = datetime(2024, 1, 1, 10, 30)
        mock_location.accuracy = 10.5

        mock_location_update.get_latest_for_event.return_value = mock_location

        mock_event = Mock()
        mock_event.id = 1
        mock_tenant = Mock()
        mock_tenant.id = 1

        serializer = JobDetailSerializer(mock_event, context={'tenant': mock_tenant})
        result = serializer.get_latest_location(mock_event)

        assert result is not None
        assert result['latitude'] == 40.7128
        assert result['longitude'] == -74.0060
        assert result['accuracy'] == 10.5

    @patch('smoothschedule.communication.mobile.serializers.EmployeeLocationUpdate')
    def test_get_latest_location_returns_none(self, mock_location_update):
        """Test get_latest_location returns None when no location."""
        mock_location_update.get_latest_for_event.return_value = None

        mock_event = Mock()
        mock_event.id = 1
        mock_tenant = Mock()
        mock_tenant.id = 1

        serializer = JobDetailSerializer(mock_event, context={'tenant': mock_tenant})
        result = serializer.get_latest_location(mock_event)

        assert result is None

    def test_get_can_edit_schedule_with_permission(self):
        """Test get_can_edit_schedule returns True when user has permission."""
        with patch('smoothschedule.scheduling.schedule.models.Resource') as mock_resource:
            mock_user = Mock()
            mock_user.is_authenticated = True

            mock_request = Mock()
            mock_request.user = mock_user

            mock_resource_obj = Mock()
            mock_resource_obj.user_can_edit_schedule = True
            mock_resource.objects.filter.return_value = [mock_resource_obj]

            mock_event = Mock()

            serializer = JobDetailSerializer(mock_event, context={'request': mock_request})
            result = serializer.get_can_edit_schedule(mock_event)

            assert result is True

    def test_get_can_edit_schedule_without_permission(self):
        """Test get_can_edit_schedule returns False when no permission."""
        with patch('smoothschedule.scheduling.schedule.models.Resource') as mock_resource:
            mock_user = Mock()
            mock_user.is_authenticated = True

            mock_request = Mock()
            mock_request.user = mock_user

            mock_resource_obj = Mock()
            mock_resource_obj.user_can_edit_schedule = False
            mock_resource.objects.filter.return_value = [mock_resource_obj]

            mock_event = Mock()

            serializer = JobDetailSerializer(mock_event, context={'request': mock_request})
            result = serializer.get_can_edit_schedule(mock_event)

            assert result is False

    def test_get_can_edit_schedule_no_request(self):
        """Test get_can_edit_schedule returns False when no request context."""
        mock_event = Mock()

        serializer = JobDetailSerializer(mock_event, context={})
        result = serializer.get_can_edit_schedule(mock_event)

        assert result is False


class TestSetStatusSerializer:
    """Test SetStatusSerializer validation."""

    def test_serializer_fields(self):
        """Test that serializer includes required fields."""
        serializer = SetStatusSerializer()
        assert 'status' in serializer.fields
        assert 'notes' in serializer.fields
        assert 'latitude' in serializer.fields
        assert 'longitude' in serializer.fields

    def test_status_field_choices(self):
        """Test status field has Event.Status choices."""
        serializer = SetStatusSerializer()
        status_field = serializer.fields['status']
        assert hasattr(status_field, 'choices')

    def test_notes_field_optional(self):
        """Test notes field is optional and allows blank."""
        serializer = SetStatusSerializer()
        notes_field = serializer.fields['notes']
        assert notes_field.required is False
        assert notes_field.allow_blank is True

    def test_notes_defaults_to_empty_string(self):
        """Test notes field defaults to empty string."""
        data = {'status': Event.Status.EN_ROUTE}
        serializer = SetStatusSerializer(data=data)
        assert serializer.is_valid()
        assert serializer.validated_data['notes'] == ''

    def test_latitude_longitude_optional(self):
        """Test latitude and longitude are optional."""
        data = {'status': Event.Status.COMPLETED, 'notes': 'Done'}
        serializer = SetStatusSerializer(data=data)
        assert serializer.is_valid()

    def test_valid_with_location(self):
        """Test validation passes with latitude and longitude."""
        data = {
            'status': Event.Status.EN_ROUTE,
            'latitude': '40.7128',
            'longitude': '-74.0060',
        }
        serializer = SetStatusSerializer(data=data)
        assert serializer.is_valid()
        assert serializer.validated_data['latitude'] == Decimal('40.7128')
        assert serializer.validated_data['longitude'] == Decimal('-74.0060')


class TestRescheduleJobSerializer:
    """Test RescheduleJobSerializer validation logic."""

    def test_serializer_fields(self):
        """Test that serializer includes required fields."""
        serializer = RescheduleJobSerializer()
        assert 'start_time' in serializer.fields
        assert 'end_time' in serializer.fields
        assert 'duration_minutes' in serializer.fields

    def test_all_fields_optional(self):
        """Test all fields are individually optional."""
        serializer = RescheduleJobSerializer()
        assert serializer.fields['start_time'].required is False
        assert serializer.fields['end_time'].required is False
        assert serializer.fields['duration_minutes'].required is False

    def test_duration_min_value(self):
        """Test duration_minutes has minimum value of 5."""
        serializer = RescheduleJobSerializer()
        assert serializer.fields['duration_minutes'].min_value == 5

    def test_duration_max_value(self):
        """Test duration_minutes has maximum value of 1440."""
        serializer = RescheduleJobSerializer()
        assert serializer.fields['duration_minutes'].max_value == 1440

    def test_validation_requires_at_least_one_field(self):
        """Test validation fails when no fields provided."""
        data = {}
        serializer = RescheduleJobSerializer(data=data)
        assert not serializer.is_valid()
        assert 'non_field_errors' in serializer.errors

    def test_validation_with_start_time_only(self):
        """Test validation passes with only start_time."""
        data = {'start_time': '2024-01-01T10:00:00Z'}
        serializer = RescheduleJobSerializer(data=data)
        assert serializer.is_valid()

    def test_validation_with_end_time_only(self):
        """Test validation passes with only end_time."""
        data = {'end_time': '2024-01-01T11:00:00Z'}
        serializer = RescheduleJobSerializer(data=data)
        assert serializer.is_valid()

    def test_validation_with_duration_only(self):
        """Test validation passes with only duration_minutes."""
        data = {'duration_minutes': 60}
        serializer = RescheduleJobSerializer(data=data)
        assert serializer.is_valid()

    def test_validation_end_time_must_be_after_start_time(self):
        """Test validation fails when end_time before start_time."""
        data = {
            'start_time': '2024-01-01T11:00:00Z',
            'end_time': '2024-01-01T10:00:00Z',
        }
        serializer = RescheduleJobSerializer(data=data)
        assert not serializer.is_valid()
        assert 'non_field_errors' in serializer.errors

    def test_validation_end_time_cannot_equal_start_time(self):
        """Test validation fails when end_time equals start_time."""
        data = {
            'start_time': '2024-01-01T10:00:00Z',
            'end_time': '2024-01-01T10:00:00Z',
        }
        serializer = RescheduleJobSerializer(data=data)
        assert not serializer.is_valid()

    def test_validation_with_valid_start_and_end_times(self):
        """Test validation passes with valid start and end times."""
        data = {
            'start_time': '2024-01-01T10:00:00Z',
            'end_time': '2024-01-01T11:00:00Z',
        }
        serializer = RescheduleJobSerializer(data=data)
        assert serializer.is_valid()

    def test_validation_with_all_fields(self):
        """Test validation passes with all fields provided."""
        data = {
            'start_time': '2024-01-01T10:00:00Z',
            'end_time': '2024-01-01T11:30:00Z',
            'duration_minutes': 90,
        }
        serializer = RescheduleJobSerializer(data=data)
        assert serializer.is_valid()


class TestStartEnRouteSerializer:
    """Test StartEnRouteSerializer."""

    def test_serializer_fields(self):
        """Test that serializer includes required fields."""
        serializer = StartEnRouteSerializer()
        assert 'latitude' in serializer.fields
        assert 'longitude' in serializer.fields
        assert 'send_customer_notification' in serializer.fields

    def test_location_fields_optional(self):
        """Test latitude and longitude are optional."""
        serializer = StartEnRouteSerializer()
        assert serializer.fields['latitude'].required is False
        assert serializer.fields['longitude'].required is False

    def test_send_customer_notification_defaults_true(self):
        """Test send_customer_notification defaults to True."""
        data = {}
        serializer = StartEnRouteSerializer(data=data)
        assert serializer.is_valid()
        assert serializer.validated_data['send_customer_notification'] is True

    def test_valid_with_location(self):
        """Test validation with location data."""
        data = {
            'latitude': '40.7128',
            'longitude': '-74.0060',
            'send_customer_notification': False,
        }
        serializer = StartEnRouteSerializer(data=data)
        assert serializer.is_valid()


class TestLocationUpdateSerializer:
    """Test LocationUpdateSerializer field requirements."""

    def test_serializer_fields(self):
        """Test that serializer includes all fields."""
        serializer = LocationUpdateSerializer()
        expected_fields = {
            'latitude', 'longitude', 'accuracy', 'altitude',
            'heading', 'speed', 'timestamp', 'battery_level',
        }
        assert set(serializer.fields.keys()) == expected_fields

    def test_latitude_longitude_required(self):
        """Test latitude and longitude are required."""
        serializer = LocationUpdateSerializer()
        assert serializer.fields['latitude'].required is True
        assert serializer.fields['longitude'].required is True

    def test_timestamp_required(self):
        """Test timestamp is required."""
        serializer = LocationUpdateSerializer()
        assert serializer.fields['timestamp'].required is True

    def test_optional_fields(self):
        """Test accuracy, altitude, heading, speed, battery_level are optional."""
        serializer = LocationUpdateSerializer()
        assert serializer.fields['accuracy'].required is False
        assert serializer.fields['altitude'].required is False
        assert serializer.fields['heading'].required is False
        assert serializer.fields['speed'].required is False
        assert serializer.fields['battery_level'].required is False

    def test_valid_minimal_data(self):
        """Test validation with only required fields."""
        data = {
            'latitude': '40.7128',
            'longitude': '-74.0060',
            'timestamp': '2024-01-01T10:00:00Z',
        }
        serializer = LocationUpdateSerializer(data=data)
        assert serializer.is_valid()

    def test_valid_complete_data(self):
        """Test validation with all fields."""
        data = {
            'latitude': '40.7128',
            'longitude': '-74.0060',
            'accuracy': 10.5,
            'altitude': 50.0,
            'heading': 180.0,
            'speed': 5.5,
            'timestamp': '2024-01-01T10:00:00Z',
            'battery_level': 0.85,
        }
        serializer = LocationUpdateSerializer(data=data)
        assert serializer.is_valid()


class TestLocationUpdateResponseSerializer:
    """Test LocationUpdateResponseSerializer."""

    def test_serializer_fields(self):
        """Test that serializer includes required fields."""
        serializer = LocationUpdateResponseSerializer()
        assert 'success' in serializer.fields
        assert 'should_continue_tracking' in serializer.fields
        assert 'message' in serializer.fields

    def test_message_field_optional(self):
        """Test message field is optional."""
        serializer = LocationUpdateResponseSerializer()
        assert serializer.fields['message'].required is False

    def test_serialization(self):
        """Test serialization of response data."""
        data = {
            'success': True,
            'should_continue_tracking': True,
            'message': 'Location updated',
        }
        serializer = LocationUpdateResponseSerializer(data=data)
        assert serializer.is_valid()
        assert serializer.validated_data['success'] is True


class TestInitiateCallSerializer:
    """Test InitiateCallSerializer."""

    def test_serializer_has_no_required_fields(self):
        """Test serializer accepts empty data (phone comes from job)."""
        data = {}
        serializer = InitiateCallSerializer(data=data)
        assert serializer.is_valid()


class TestInitiateCallResponseSerializer:
    """Test InitiateCallResponseSerializer."""

    def test_serializer_fields(self):
        """Test that serializer includes all response fields."""
        serializer = InitiateCallResponseSerializer()
        expected_fields = {
            'call_sid', 'call_log_id', 'proxy_number', 'status', 'message',
        }
        assert set(serializer.fields.keys()) == expected_fields

    def test_serialization(self):
        """Test serialization of call response."""
        data = {
            'call_sid': 'CA123456789',
            'call_log_id': 1,
            'proxy_number': '+15551234567',
            'status': 'initiated',
            'message': 'Call initiated successfully',
        }
        serializer = InitiateCallResponseSerializer(data=data)
        assert serializer.is_valid()


class TestSendSMSSerializer:
    """Test SendSMSSerializer validation."""

    def test_serializer_fields(self):
        """Test that serializer includes message field."""
        serializer = SendSMSSerializer()
        assert 'message' in serializer.fields

    def test_message_required(self):
        """Test message field is required."""
        data = {}
        serializer = SendSMSSerializer(data=data)
        assert not serializer.is_valid()
        assert 'message' in serializer.errors

    def test_message_max_length(self):
        """Test message field has max_length of 1600."""
        serializer = SendSMSSerializer()
        assert serializer.fields['message'].max_length == 1600

    def test_valid_message(self):
        """Test validation with valid message."""
        data = {'message': 'On my way!'}
        serializer = SendSMSSerializer(data=data)
        assert serializer.is_valid()

    def test_message_too_long(self):
        """Test validation fails when message exceeds max_length."""
        data = {'message': 'A' * 1601}
        serializer = SendSMSSerializer(data=data)
        assert not serializer.is_valid()
        assert 'message' in serializer.errors


class TestSendSMSResponseSerializer:
    """Test SendSMSResponseSerializer."""

    def test_serializer_fields(self):
        """Test that serializer includes all response fields."""
        serializer = SendSMSResponseSerializer()
        expected_fields = {'message_sid', 'call_log_id', 'status'}
        assert set(serializer.fields.keys()) == expected_fields

    def test_serialization(self):
        """Test serialization of SMS response."""
        data = {
            'message_sid': 'SM123456789',
            'call_log_id': 2,
            'status': 'sent',
        }
        serializer = SendSMSResponseSerializer(data=data)
        assert serializer.is_valid()


class TestCallHistorySerializer:
    """Test CallHistorySerializer."""

    def test_serializer_fields(self):
        """Test that serializer includes all fields."""
        serializer = CallHistorySerializer()
        expected_fields = {
            'id', 'call_type', 'type_display', 'direction',
            'direction_display', 'status', 'status_display',
            'duration_seconds', 'initiated_at', 'answered_at',
            'ended_at', 'employee_name',
        }
        assert set(serializer.fields.keys()) == expected_fields

    def test_display_fields_use_get_display_methods(self):
        """Test display fields are mapped to get_*_display methods."""
        serializer = CallHistorySerializer()
        assert serializer.fields['type_display'].source == 'get_call_type_display'
        assert serializer.fields['direction_display'].source == 'get_direction_display'
        assert serializer.fields['status_display'].source == 'get_status_display'

    def test_get_employee_name_with_employee(self):
        """Test get_employee_name returns employee full_name."""
        mock_call_log = Mock()
        mock_call_log.employee = Mock(full_name="Jane Doe")

        serializer = CallHistorySerializer()
        result = serializer.get_employee_name(mock_call_log)
        assert result == "Jane Doe"

    def test_get_employee_name_without_employee(self):
        """Test get_employee_name returns None when no employee."""
        mock_call_log = Mock()
        mock_call_log.employee = None

        serializer = CallHistorySerializer()
        result = serializer.get_employee_name(mock_call_log)
        assert result is None


class TestEmployeeProfileSerializer:
    """Test EmployeeProfileSerializer."""

    def test_serializer_fields(self):
        """Test that serializer includes all fields."""
        serializer = EmployeeProfileSerializer()
        expected_fields = {
            'id', 'email', 'name', 'phone', 'role',
            'business_id', 'business_name', 'business_subdomain',
            'can_use_masked_calls', 'can_track_location',
        }
        assert set(serializer.fields.keys()) == expected_fields

    def test_business_id_source(self):
        """Test business_id field sources from tenant_id."""
        serializer = EmployeeProfileSerializer()
        assert serializer.fields['business_id'].source == 'tenant_id'

    def test_get_business_name_with_tenant(self):
        """Test get_business_name returns tenant name."""
        mock_employee = Mock()
        mock_tenant = Mock()
        mock_tenant.name = "ABC Company"
        mock_employee.tenant = mock_tenant

        serializer = EmployeeProfileSerializer()
        result = serializer.get_business_name(mock_employee)
        assert result == "ABC Company"

    def test_get_business_name_without_tenant(self):
        """Test get_business_name returns None when no tenant."""
        mock_employee = Mock()
        mock_employee.tenant = None

        serializer = EmployeeProfileSerializer()
        result = serializer.get_business_name(mock_employee)
        assert result is None

    def test_get_business_subdomain_with_primary_domain(self):
        """Test get_business_subdomain extracts subdomain from primary domain."""
        mock_domain = Mock()
        mock_domain.domain = "demo.smoothschedule.com"

        mock_employee = Mock()
        mock_employee.tenant = Mock()
        mock_employee.tenant.domains.filter.return_value.first.return_value = mock_domain

        serializer = EmployeeProfileSerializer()
        result = serializer.get_business_subdomain(mock_employee)
        assert result == "demo"

    def test_get_business_subdomain_without_tenant(self):
        """Test get_business_subdomain returns None when no tenant."""
        mock_employee = Mock()
        mock_employee.tenant = None

        serializer = EmployeeProfileSerializer()
        result = serializer.get_business_subdomain(mock_employee)
        assert result is None

    def test_get_business_subdomain_without_primary_domain(self):
        """Test get_business_subdomain returns None when no primary domain."""
        mock_employee = Mock()
        mock_employee.tenant = Mock()
        mock_employee.tenant.domains.filter.return_value.first.return_value = None

        serializer = EmployeeProfileSerializer()
        result = serializer.get_business_subdomain(mock_employee)
        assert result is None

    def test_get_can_use_masked_calls_with_feature(self):
        """Test get_can_use_masked_calls checks tenant feature."""
        mock_employee = Mock()
        mock_employee.tenant = Mock()
        mock_employee.tenant.has_feature.return_value = True

        serializer = EmployeeProfileSerializer()
        result = serializer.get_can_use_masked_calls(mock_employee)
        assert result is True

        mock_employee.tenant.has_feature.assert_called_once_with('can_use_masked_phone_numbers')

    def test_get_can_use_masked_calls_without_feature(self):
        """Test get_can_use_masked_calls returns False when feature disabled."""
        mock_employee = Mock()
        mock_employee.tenant = Mock()
        mock_employee.tenant.has_feature.return_value = False

        serializer = EmployeeProfileSerializer()
        result = serializer.get_can_use_masked_calls(mock_employee)
        assert result is False

    def test_get_can_use_masked_calls_without_tenant(self):
        """Test get_can_use_masked_calls returns False when no tenant."""
        mock_employee = Mock()
        mock_employee.tenant = None

        serializer = EmployeeProfileSerializer()
        result = serializer.get_can_use_masked_calls(mock_employee)
        assert result is False

    def test_get_can_track_location_with_feature(self):
        """Test get_can_track_location checks tenant feature."""
        mock_employee = Mock()
        mock_employee.tenant = Mock()
        mock_employee.tenant.has_feature.return_value = True

        serializer = EmployeeProfileSerializer()
        result = serializer.get_can_track_location(mock_employee)
        assert result is True

        mock_employee.tenant.has_feature.assert_called_once_with('can_use_mobile_app')

    def test_get_can_track_location_without_feature(self):
        """Test get_can_track_location returns False when feature disabled."""
        mock_employee = Mock()
        mock_employee.tenant = Mock()
        mock_employee.tenant.has_feature.return_value = False

        serializer = EmployeeProfileSerializer()
        result = serializer.get_can_track_location(mock_employee)
        assert result is False

    def test_get_can_track_location_without_tenant(self):
        """Test get_can_track_location returns False when no tenant."""
        mock_employee = Mock()
        mock_employee.tenant = None

        serializer = EmployeeProfileSerializer()
        result = serializer.get_can_track_location(mock_employee)
        assert result is False

    def test_full_serialization(self):
        """Test complete serialization of employee profile."""
        mock_domain = Mock()
        mock_domain.domain = "demo.smoothschedule.com"

        mock_tenant = Mock()
        mock_tenant.name = "Demo Business"
        mock_tenant.domains.filter.return_value.first.return_value = mock_domain
        mock_tenant.has_feature.side_effect = lambda feature: feature == 'can_use_mobile_app'

        mock_employee = Mock()
        mock_employee.id = 1
        mock_employee.email = "employee@example.com"
        mock_employee.name = "John Employee"
        mock_employee.phone = "5551234567"
        mock_employee.role = "staff"
        mock_employee.tenant_id = 5
        mock_employee.tenant = mock_tenant

        serializer = EmployeeProfileSerializer(mock_employee)
        data = serializer.data

        assert data['id'] == 1
        assert data['email'] == "employee@example.com"
        assert data['name'] == "John Employee"
        assert data['business_id'] == 5
        assert data['business_name'] == "Demo Business"
        assert data['business_subdomain'] == "demo"
        assert data['can_use_masked_calls'] is False
        assert data['can_track_location'] is True
