"""
Unit tests for Schedule ViewSets.

Tests viewset methods and actions with mocks to avoid database access.
"""
from unittest.mock import Mock, patch, MagicMock
from rest_framework.test import APIRequestFactory
from rest_framework import status
import pytest


class TestResourceTypeViewSetDestroy:
    """Test ResourceTypeViewSet.destroy method."""

    def test_destroy_blocks_default_types(self):
        """Test that default resource types cannot be deleted."""
        from smoothschedule.scheduling.schedule.views import ResourceTypeViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.delete('/api/resource-types/1/')
        request.user = Mock(is_authenticated=True)

        # Create mock instance
        mock_instance = Mock()
        mock_instance.is_default = True
        mock_instance.name = 'Staff'

        # Create viewset and patch get_object
        viewset = ResourceTypeViewSet()
        viewset.request = request
        viewset.format_kwarg = None
        viewset.kwargs = {'pk': 1}

        with patch.object(viewset, 'get_object', return_value=mock_instance):
            response = viewset.destroy(request)

        # Assert
        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert 'Cannot delete default' in response.data['error']

    def test_destroy_blocks_types_in_use(self):
        """Test that resource types in use cannot be deleted."""
        from smoothschedule.scheduling.schedule.views import ResourceTypeViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.delete('/api/resource-types/1/')
        request.user = Mock(is_authenticated=True)

        # Create mock instance with resources
        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 = 5

        viewset = ResourceTypeViewSet()
        viewset.request = request
        viewset.format_kwarg = None
        viewset.kwargs = {'pk': 1}

        with patch.object(viewset, 'get_object', return_value=mock_instance):
            response = viewset.destroy(request)

        # Assert
        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert 'in use by 5 resource(s)' in response.data['error']

    def test_destroy_allows_unused_custom_types(self):
        """Test that unused custom types can be deleted."""
        from smoothschedule.scheduling.schedule.views import ResourceTypeViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.delete('/api/resource-types/1/')
        request.user = Mock(is_authenticated=True)

        mock_instance = Mock()
        mock_instance.is_default = False
        mock_instance.name = 'Unused Type'
        mock_instance.resources.exists.return_value = False

        viewset = ResourceTypeViewSet()
        viewset.request = request
        viewset.format_kwarg = None
        viewset.kwargs = {'pk': 1}

        with patch.object(viewset, 'get_object', return_value=mock_instance):
            with patch.object(ResourceTypeViewSet, 'destroy', wraps=viewset.destroy) as mock_destroy:
                # Call the parent destroy (which would do the actual delete)
                # We just verify our validation passed
                with patch('rest_framework.mixins.DestroyModelMixin.destroy') as parent_destroy:
                    parent_destroy.return_value = Mock(status_code=204)
                    response = viewset.destroy(request)

        # Assert - should reach parent destroy (204 No Content)
        assert response.status_code == 204


class TestResourceViewSetLocation:
    """Test ResourceViewSet.location action."""

    def test_location_returns_error_when_resource_has_no_user(self):
        """Test location action when resource has no linked user."""
        from smoothschedule.scheduling.schedule.views import ResourceViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.get('/api/resources/1/location/')
        request.user = Mock(is_authenticated=True, role='TENANT_OWNER')
        request.tenant = Mock()

        mock_resource = Mock()
        mock_resource.user = None

        viewset = ResourceViewSet()
        viewset.request = request
        viewset.format_kwarg = None
        viewset.kwargs = {'pk': 1}

        with patch.object(viewset, 'get_object', return_value=mock_resource):
            response = viewset.location(request, pk=1)

        # Assert
        assert response.data['has_location'] is False
        assert 'no linked user' in response.data['message']

    def test_location_returns_error_when_no_tenant(self):
        """Test location action when no tenant context."""
        from smoothschedule.scheduling.schedule.views import ResourceViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.get('/api/resources/1/location/')
        request.user = Mock(is_authenticated=True, role='TENANT_OWNER')
        request.tenant = None

        mock_resource = Mock()
        mock_resource.user = Mock(id=1)

        viewset = ResourceViewSet()
        viewset.request = request
        viewset.format_kwarg = None
        viewset.kwargs = {'pk': 1}

        with patch.object(viewset, 'get_object', return_value=mock_resource):
            response = viewset.location(request, pk=1)

        # Assert
        assert response.data['has_location'] is False
        assert 'No tenant context' in response.data['message']


class TestServiceViewSet:
    """Test ServiceViewSet."""

    def test_get_queryset_filters_active_only(self):
        """Test that get_queryset returns only active services by default."""
        from smoothschedule.scheduling.schedule.views import ServiceViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.get('/api/services/')
        request.user = Mock(is_authenticated=True, role='TENANT_OWNER')
        request.tenant = Mock()

        viewset = ServiceViewSet()
        viewset.request = request
        viewset.action = 'list'

        # The actual filtering is done via TenantFilteredQuerySetMixin
        # We just verify the viewset has correct configuration
        assert hasattr(viewset, 'permission_classes')


class TestEventViewSetActions:
    """Test EventViewSet custom actions."""

    def test_viewset_exists(self):
        """Test that EventViewSet is properly configured."""
        from smoothschedule.scheduling.schedule.views import EventViewSet

        # Verify the viewset exists and has basic configuration
        assert hasattr(EventViewSet, 'queryset')
        assert hasattr(EventViewSet, 'serializer_class')


class TestTimeBlockViewSet:
    """Test TimeBlockViewSet."""

    def test_viewset_has_blocked_dates_action(self):
        """Test that blocked_dates action exists."""
        from smoothschedule.scheduling.schedule.views import TimeBlockViewSet

        assert hasattr(TimeBlockViewSet, 'blocked_dates')

    def test_viewset_has_check_conflicts_action(self):
        """Test that check_conflicts action exists."""
        from smoothschedule.scheduling.schedule.views import TimeBlockViewSet

        assert hasattr(TimeBlockViewSet, 'check_conflicts')


class TestCustomerViewSet:
    """Test CustomerViewSet."""

    def test_uses_user_tenant_filtered_mixin(self):
        """Test that CustomerViewSet uses UserTenantFilteredMixin."""
        from smoothschedule.scheduling.schedule.views import CustomerViewSet
        from smoothschedule.identity.core.mixins import UserTenantFilteredMixin

        assert issubclass(CustomerViewSet, UserTenantFilteredMixin)

    def test_uses_deny_staff_list_permission(self):
        """Test that CustomerViewSet uses DenyStaffListPermission."""
        from smoothschedule.scheduling.schedule.views import CustomerViewSet
        from smoothschedule.identity.core.mixins import DenyStaffListPermission

        assert DenyStaffListPermission in CustomerViewSet.permission_classes


class TestStaffViewSet:
    """Test StaffViewSet."""

    def test_uses_user_tenant_filtered_mixin(self):
        """Test that StaffViewSet uses UserTenantFilteredMixin."""
        from smoothschedule.scheduling.schedule.views import StaffViewSet
        from smoothschedule.identity.core.mixins import UserTenantFilteredMixin

        assert issubclass(StaffViewSet, UserTenantFilteredMixin)


class TestPluginViewSets:
    """Test plugin-related viewsets."""

    def test_plugin_template_viewset_exists(self):
        """Test that PluginTemplateViewSet is properly configured."""
        from smoothschedule.scheduling.schedule.views import PluginTemplateViewSet

        assert hasattr(PluginTemplateViewSet, 'queryset')
        assert hasattr(PluginTemplateViewSet, 'serializer_class')

    def test_scheduled_task_viewset_uses_task_feature_mixin(self):
        """Test that ScheduledTaskViewSet uses TaskFeatureRequiredMixin."""
        from smoothschedule.scheduling.schedule.views import ScheduledTaskViewSet
        from smoothschedule.identity.core.mixins import TaskFeatureRequiredMixin

        assert issubclass(ScheduledTaskViewSet, TaskFeatureRequiredMixin)


class TestEventViewSetCreate:
    """Test EventViewSet.perform_create method."""

    def test_perform_create_sets_created_by(self):
        """Test that perform_create sets created_by to request user."""
        from smoothschedule.scheduling.schedule.views import EventViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/events/', {})
        mock_user = Mock(id=1, username='testuser')
        request.user = mock_user
        request.tenant = Mock()

        viewset = EventViewSet()
        viewset.request = request

        mock_serializer = Mock()

        # Act
        viewset.perform_create(mock_serializer)

        # Assert
        mock_serializer.save.assert_called_once_with(created_by=mock_user)


class TestEventViewSetUpdate:
    """Test EventViewSet.perform_update method."""

    def test_perform_update_calls_save(self):
        """Test that perform_update calls serializer.save()."""
        from smoothschedule.scheduling.schedule.views import EventViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.patch('/api/events/1/', {})
        request.user = Mock()
        request.tenant = Mock()

        viewset = EventViewSet()
        viewset.request = request

        mock_serializer = Mock()

        # Act
        viewset.perform_update(mock_serializer)

        # Assert
        mock_serializer.save.assert_called_once()


class TestEventViewSetSetStatus:
    """Test EventViewSet.set_status action."""

    def test_set_status_requires_tenant_context(self):
        """Test that set_status returns error when no tenant context."""
        from smoothschedule.scheduling.schedule.views import EventViewSet
        from rest_framework.request import Request

        # Arrange
        factory = APIRequestFactory()
        django_request = factory.post('/api/events/1/set_status/', {'status': 'IN_PROGRESS'}, format='json')
        request = Request(django_request)
        request.user = Mock()
        request.tenant = None

        mock_event = Mock()

        viewset = EventViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        with patch.object(viewset, 'get_object', return_value=mock_event):
            response = viewset.set_status(request, pk=1)

        # Assert
        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert 'No tenant context' in response.data['error']

    def test_set_status_requires_status_field(self):
        """Test that set_status returns error when status field missing."""
        from smoothschedule.scheduling.schedule.views import EventViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/events/1/set_status/', {}, format='json')
        # Manually set data attribute to simulate DRF Request
        request.data = {}
        request.user = Mock()
        request.tenant = Mock()

        mock_event = Mock()

        viewset = EventViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        with patch.object(viewset, 'get_object', return_value=mock_event):
            response = viewset.set_status(request, pk=1)

        # Assert
        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert 'status is required' in response.data['error']

    def test_set_status_handles_transition_error(self):
        """Test that set_status returns error when transition fails."""
        from smoothschedule.scheduling.schedule.views import EventViewSet
        from smoothschedule.communication.mobile.services.status_machine import StatusTransitionError

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/events/1/set_status/', {'status': 'COMPLETED'}, format='json')
        # Manually set data attribute to simulate DRF Request
        request.data = {'status': 'COMPLETED'}
        request.user = Mock()
        request.tenant = Mock()

        mock_event = Mock()

        viewset = EventViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        with patch.object(viewset, 'get_object', return_value=mock_event):
            # Patch at the import source, not the views module
            with patch('smoothschedule.communication.mobile.services.StatusMachine') as mock_machine:
                mock_machine.return_value.transition.side_effect = StatusTransitionError('Invalid transition')
                response = viewset.set_status(request, pk=1)

        # Assert
        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert 'Invalid transition' in response.data['error']


class TestEventViewSetStartEnRoute:
    """Test EventViewSet.start_en_route action."""

    def test_start_en_route_requires_tenant_context(self):
        """Test that start_en_route returns error when no tenant context."""
        from smoothschedule.scheduling.schedule.views import EventViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/events/1/start_en_route/', {})
        request.user = Mock()
        request.tenant = None

        mock_event = Mock()

        viewset = EventViewSet()
        viewset.request = request

        with patch.object(viewset, 'get_object', return_value=mock_event):
            response = viewset.start_en_route(request, pk=1)

        # Assert
        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert 'No tenant context' in response.data['error']


class TestEventViewSetStatusHistory:
    """Test EventViewSet.status_history action."""

    def test_status_history_requires_tenant_context(self):
        """Test that status_history returns error when no tenant context."""
        from smoothschedule.scheduling.schedule.views import EventViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.get('/api/events/1/status_history/')
        request.user = Mock()
        request.tenant = None

        mock_event = Mock(id=1)

        viewset = EventViewSet()
        viewset.request = request

        with patch.object(viewset, 'get_object', return_value=mock_event):
            response = viewset.status_history(request, pk=1)

        # Assert
        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert 'No tenant context' in response.data['error']


class TestEventViewSetAllowedTransitions:
    """Test EventViewSet.allowed_transitions action."""

    def test_allowed_transitions_requires_tenant_context(self):
        """Test that allowed_transitions returns error when no tenant context."""
        from smoothschedule.scheduling.schedule.views import EventViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.get('/api/events/1/allowed_transitions/')
        request.user = Mock()
        request.tenant = None

        mock_event = Mock(id=1)

        viewset = EventViewSet()
        viewset.request = request

        with patch.object(viewset, 'get_object', return_value=mock_event):
            response = viewset.allowed_transitions(request, pk=1)

        # Assert
        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert 'No tenant context' in response.data['error']


class TestServiceViewSetReorder:
    """Test ServiceViewSet.reorder action."""

    def test_reorder_requires_list_parameter(self):
        """Test that reorder validates order parameter is a list."""
        from smoothschedule.scheduling.schedule.views import ServiceViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/services/reorder/', {'order': 'not-a-list'}, format='json')
        # Manually set data attribute to simulate DRF Request
        request.data = {'order': 'not-a-list'}
        request.user = Mock()
        request.tenant = Mock()

        viewset = ServiceViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        # Act
        response = viewset.reorder(request)

        # Assert
        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert 'must be a list' in response.data['error']

    def test_reorder_updates_display_order(self):
        """Test that reorder updates service display_order."""
        from smoothschedule.scheduling.schedule.views import ServiceViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/services/reorder/', {'order': [3, 1, 2]}, format='json')
        # Manually set data attribute to simulate DRF Request
        request.data = {'order': [3, 1, 2]}
        request.user = Mock()
        request.tenant = Mock()

        viewset = ServiceViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        # Mock the Service model's filter method
        with patch('smoothschedule.scheduling.schedule.views.Service') as mock_service:
            mock_queryset = Mock()
            mock_service.objects.filter.return_value = mock_queryset

            # Act
            response = viewset.reorder(request)

            # Assert
            assert response.status_code == status.HTTP_200_OK
            assert response.data['updated'] == 3
            # Verify filter was called for each ID
            assert mock_service.objects.filter.call_count == 3


class TestServiceViewSetFilterQueryset:
    """Test ServiceViewSet.filter_queryset_for_tenant method."""

    def test_filters_active_services_by_default(self):
        """Test that only active services are shown by default."""
        from smoothschedule.scheduling.schedule.views import ServiceViewSet
        from rest_framework.request import Request

        # Arrange
        factory = APIRequestFactory()
        django_request = factory.get('/api/services/')
        request = Request(django_request)
        request.user = Mock()
        request.tenant = Mock()

        viewset = ServiceViewSet()
        viewset.request = request
        viewset.action = 'list'

        mock_queryset = Mock()
        mock_queryset.filter.return_value = mock_queryset

        # Act
        result = viewset.filter_queryset_for_tenant(mock_queryset)

        # Assert
        mock_queryset.filter.assert_called_once_with(is_active=True)

    def test_shows_inactive_when_requested(self):
        """Test that inactive services are shown when show_inactive=true."""
        from smoothschedule.scheduling.schedule.views import ServiceViewSet
        from rest_framework.request import Request

        # Arrange
        factory = APIRequestFactory()
        django_request = factory.get('/api/services/?show_inactive=true')
        request = Request(django_request)
        request.user = Mock()
        request.tenant = Mock()

        viewset = ServiceViewSet()
        viewset.request = request
        viewset.action = 'list'

        mock_queryset = Mock()

        # Act
        result = viewset.filter_queryset_for_tenant(mock_queryset)

        # Assert - filter should NOT be called when show_inactive=true
        mock_queryset.filter.assert_not_called()


class TestTimeBlockViewSetBlockedDates:
    """Test TimeBlockViewSet.blocked_dates action."""

    def test_blocked_dates_requires_date_parameters(self):
        """Test that blocked_dates requires start_date and end_date."""
        from smoothschedule.scheduling.schedule.views import TimeBlockViewSet
        from rest_framework.request import Request

        # Arrange
        factory = APIRequestFactory()
        django_request = factory.get('/api/time-blocks/blocked_dates/')
        request = Request(django_request)
        request.user = Mock()

        viewset = TimeBlockViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        # Mock get_queryset to avoid DB access
        with patch.object(viewset, 'get_queryset', return_value=Mock()):
            # Act
            response = viewset.blocked_dates(request)

        # Assert
        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert 'start_date and end_date are required' in response.data['error']

    def test_blocked_dates_validates_date_format(self):
        """Test that blocked_dates validates date format."""
        from smoothschedule.scheduling.schedule.views import TimeBlockViewSet
        from rest_framework.request import Request

        # Arrange
        factory = APIRequestFactory()
        django_request = factory.get('/api/time-blocks/blocked_dates/?start_date=invalid&end_date=invalid')
        request = Request(django_request)
        request.user = Mock()

        viewset = TimeBlockViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        # Mock get_queryset to avoid DB access
        with patch.object(viewset, 'get_queryset', return_value=Mock()):
            # Act
            response = viewset.blocked_dates(request)

        # Assert
        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert 'Invalid date format' in response.data['error']


class TestTimeBlockViewSetCheckConflicts:
    """Test TimeBlockViewSet.check_conflicts action."""

    def test_check_conflicts_validates_input(self):
        """Test that check_conflicts validates input data."""
        from smoothschedule.scheduling.schedule.views import TimeBlockViewSet
        from rest_framework.exceptions import ValidationError
        import pytest

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/time-blocks/check_conflicts/', {}, format='json')
        # Manually set data attribute to simulate DRF Request
        request.data = {}
        request.user = Mock()

        viewset = TimeBlockViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        # Act & Assert - should raise validation error (missing required fields)
        # DRF viewsets raise ValidationError which is caught by DRF's exception handler
        # In unit tests without the full DRF stack, we expect the exception to be raised
        with pytest.raises(ValidationError):
            viewset.check_conflicts(request)


class TestTimeBlockViewSetPerformCreate:
    """Test TimeBlockViewSet.perform_create method."""

    def test_perform_create_auto_approves_for_privileged_users(self):
        """Test that perform_create auto-approves blocks for users with permission."""
        from smoothschedule.scheduling.schedule.views import TimeBlockViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/time-blocks/', {})
        mock_user = Mock()
        mock_user.can_self_approve_time_off.return_value = True
        request.user = mock_user

        viewset = TimeBlockViewSet()
        viewset.request = request

        mock_serializer = Mock()

        # Act
        viewset.perform_create(mock_serializer)

        # Assert
        from smoothschedule.scheduling.schedule.models import TimeBlock
        mock_serializer.save.assert_called_once()
        call_kwargs = mock_serializer.save.call_args[1]
        assert call_kwargs['approval_status'] == TimeBlock.ApprovalStatus.APPROVED
        assert call_kwargs['created_by'] == mock_user

    def test_perform_create_sets_pending_for_staff(self):
        """Test that perform_create sets PENDING status for staff without permission."""
        from smoothschedule.scheduling.schedule.views import TimeBlockViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/time-blocks/', {})
        mock_user = Mock()
        mock_user.can_self_approve_time_off.return_value = False
        request.user = mock_user

        viewset = TimeBlockViewSet()
        viewset.request = request

        mock_serializer = Mock()

        # Act
        viewset.perform_create(mock_serializer)

        # Assert
        from smoothschedule.scheduling.schedule.models import TimeBlock
        mock_serializer.save.assert_called_once()
        call_kwargs = mock_serializer.save.call_args[1]
        assert call_kwargs['approval_status'] == TimeBlock.ApprovalStatus.PENDING


class TestTimeBlockViewSetApproval:
    """Test TimeBlockViewSet.approve action."""

    def test_approve_requires_permission(self):
        """Test that approve checks user permission."""
        from smoothschedule.scheduling.schedule.views import TimeBlockViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/time-blocks/1/approve/', {})
        mock_user = Mock()
        mock_user.can_review_time_off_requests.return_value = False
        request.user = mock_user

        viewset = TimeBlockViewSet()
        viewset.request = request

        mock_block = Mock()

        with patch.object(viewset, 'get_object', return_value=mock_block):
            response = viewset.approve(request, pk=1)

        # Assert
        assert response.status_code == status.HTTP_403_FORBIDDEN
        assert 'permission' in response.data['error'].lower()

    def test_approve_rejects_non_pending_blocks(self):
        """Test that approve rejects blocks not in PENDING status."""
        from smoothschedule.scheduling.schedule.views import TimeBlockViewSet
        from smoothschedule.scheduling.schedule.models import TimeBlock

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/time-blocks/1/approve/', {})
        mock_user = Mock()
        mock_user.can_review_time_off_requests.return_value = True
        request.user = mock_user

        viewset = TimeBlockViewSet()
        viewset.request = request

        mock_block = Mock()
        mock_block.approval_status = TimeBlock.ApprovalStatus.APPROVED
        mock_block.get_approval_status_display.return_value = 'Approved'

        with patch.object(viewset, 'get_object', return_value=mock_block):
            response = viewset.approve(request, pk=1)

        # Assert
        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert 'already Approved' in response.data['error']


class TestTimeBlockViewSetDeny:
    """Test TimeBlockViewSet.deny action."""

    def test_deny_requires_permission(self):
        """Test that deny checks user permission."""
        from smoothschedule.scheduling.schedule.views import TimeBlockViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/time-blocks/1/deny/', {})
        mock_user = Mock()
        mock_user.can_review_time_off_requests.return_value = False
        request.user = mock_user

        viewset = TimeBlockViewSet()
        viewset.request = request

        mock_block = Mock()

        with patch.object(viewset, 'get_object', return_value=mock_block):
            response = viewset.deny(request, pk=1)

        # Assert
        assert response.status_code == status.HTTP_403_FORBIDDEN
        assert 'permission' in response.data['error'].lower()


class TestHolidayViewSetDates:
    """Test HolidayViewSet.dates action."""

    def test_dates_validates_year_parameter(self):
        """Test that dates action validates year parameter."""
        from smoothschedule.scheduling.schedule.views import HolidayViewSet
        from rest_framework.request import Request

        # Arrange
        factory = APIRequestFactory()
        django_request = factory.get('/api/holidays/dates/?year=invalid')
        request = Request(django_request)
        request.user = Mock()

        viewset = HolidayViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        # Act
        with patch.object(viewset, 'get_queryset', return_value=[]):
            response = viewset.dates(request)

        # Assert
        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert 'Invalid year' in response.data['error']

    def test_dates_uses_current_year_by_default(self):
        """Test that dates action uses current year when not specified."""
        from smoothschedule.scheduling.schedule.views import HolidayViewSet
        from rest_framework.request import Request
        from datetime import date

        # Arrange
        factory = APIRequestFactory()
        django_request = factory.get('/api/holidays/dates/')
        request = Request(django_request)
        request.user = Mock()

        viewset = HolidayViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        mock_holiday = Mock()
        mock_holiday.get_date_for_year.return_value = date(2025, 1, 1)
        mock_holiday.code = 'new_years'
        mock_holiday.name = 'New Years Day'

        mock_queryset = [mock_holiday]

        # Act
        with patch.object(viewset, 'get_queryset', return_value=mock_queryset):
            response = viewset.dates(request)

        # Assert
        assert response.status_code == status.HTTP_200_OK
        assert response.data['year'] == date.today().year


class TestScheduledTaskViewSetPause:
    """Test ScheduledTaskViewSet.pause action."""

    def test_pause_pauses_active_task(self):
        """Test that pause changes status to PAUSED."""
        from smoothschedule.scheduling.schedule.views import ScheduledTaskViewSet
        from smoothschedule.scheduling.schedule.models import ScheduledTask

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/scheduled-tasks/1/pause/', {})
        request.user = Mock()
        request.tenant = Mock()

        viewset = ScheduledTaskViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        mock_task = Mock()
        mock_task.status = ScheduledTask.Status.ACTIVE
        mock_task.id = 1

        with patch.object(viewset, 'get_object', return_value=mock_task):
            response = viewset.pause(request, pk=1)

        # Assert
        assert response.status_code == status.HTTP_200_OK
        assert mock_task.status == ScheduledTask.Status.PAUSED
        mock_task.save.assert_called_once()

    def test_pause_fails_if_already_paused(self):
        """Test that pause returns error if task already paused."""
        from smoothschedule.scheduling.schedule.views import ScheduledTaskViewSet
        from smoothschedule.scheduling.schedule.models import ScheduledTask

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/scheduled-tasks/1/pause/', {})
        request.user = Mock()
        request.tenant = Mock()

        viewset = ScheduledTaskViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        mock_task = Mock()
        mock_task.status = ScheduledTask.Status.PAUSED

        with patch.object(viewset, 'get_object', return_value=mock_task):
            response = viewset.pause(request, pk=1)

        # Assert
        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert 'already paused' in response.data['error']


class TestScheduledTaskViewSetResume:
    """Test ScheduledTaskViewSet.resume action."""

    def test_resume_resumes_paused_task(self):
        """Test that resume changes status to ACTIVE."""
        from smoothschedule.scheduling.schedule.views import ScheduledTaskViewSet
        from smoothschedule.scheduling.schedule.models import ScheduledTask

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/scheduled-tasks/1/resume/', {})
        request.user = Mock()
        request.tenant = Mock()

        viewset = ScheduledTaskViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        mock_task = Mock()
        mock_task.status = ScheduledTask.Status.PAUSED
        mock_task.id = 1
        mock_task.next_run_at = None

        with patch.object(viewset, 'get_object', return_value=mock_task):
            response = viewset.resume(request, pk=1)

        # Assert
        assert response.status_code == status.HTTP_200_OK
        assert mock_task.status == ScheduledTask.Status.ACTIVE
        mock_task.update_next_run_time.assert_called_once()
        mock_task.save.assert_called_once()

    def test_resume_fails_if_not_paused(self):
        """Test that resume returns error if task is not paused."""
        from smoothschedule.scheduling.schedule.views import ScheduledTaskViewSet
        from smoothschedule.scheduling.schedule.models import ScheduledTask

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/scheduled-tasks/1/resume/', {})
        request.user = Mock()
        request.tenant = Mock()

        viewset = ScheduledTaskViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        mock_task = Mock()
        mock_task.status = ScheduledTask.Status.ACTIVE

        with patch.object(viewset, 'get_object', return_value=mock_task):
            response = viewset.resume(request, pk=1)

        # Assert
        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert 'not paused' in response.data['error']


class TestScheduledTaskViewSetExecute:
    """Test ScheduledTaskViewSet.execute action."""

    def test_execute_queues_task(self):
        """Test that execute queues task for immediate execution."""
        from smoothschedule.scheduling.schedule.views import ScheduledTaskViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/scheduled-tasks/1/execute/', {})
        request.user = Mock()
        request.tenant = Mock()

        viewset = ScheduledTaskViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        mock_task = Mock()
        mock_task.id = 1

        with patch.object(viewset, 'get_object', return_value=mock_task):
            # Patch the task module at its source since it's imported locally
            with patch('smoothschedule.scheduling.schedule.tasks.execute_scheduled_task') as mock_celery_task:
                mock_result = Mock()
                mock_result.id = 'celery-task-123'
                mock_celery_task.delay.return_value = mock_result

                response = viewset.execute(request, pk=1)

        # Assert
        assert response.status_code == status.HTTP_200_OK
        assert response.data['celery_task_id'] == 'celery-task-123'
        mock_celery_task.delay.assert_called_once_with(1)


class TestScheduledTaskViewSetLogs:
    """Test ScheduledTaskViewSet.logs action."""

    def test_logs_returns_execution_logs(self):
        """Test that logs returns execution logs with pagination."""
        from smoothschedule.scheduling.schedule.views import ScheduledTaskViewSet
        from rest_framework.request import Request

        # Arrange
        factory = APIRequestFactory()
        django_request = factory.get('/api/scheduled-tasks/1/logs/?limit=10&offset=0')
        request = Request(django_request)
        request.user = Mock()
        request.tenant = Mock()

        viewset = ScheduledTaskViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        mock_task = Mock()
        mock_task.id = 1
        mock_task.execution_logs.all.return_value = Mock()
        mock_task.execution_logs.all.return_value.__getitem__ = Mock(return_value=[])
        mock_task.execution_logs.count.return_value = 0

        with patch.object(viewset, 'get_object', return_value=mock_task):
            with patch('smoothschedule.scheduling.schedule.views.TaskExecutionLogSerializer') as mock_serializer:
                mock_serializer.return_value.data = []
                response = viewset.logs(request, pk=1)

        # Assert
        assert response.status_code == status.HTTP_200_OK
        assert 'count' in response.data
        assert 'results' in response.data


class TestPluginViewSetList:
    """Test PluginViewSet.list action."""

    def test_list_returns_all_plugins(self):
        """Test that list returns all registered plugins."""
        from smoothschedule.scheduling.schedule.views import PluginViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.get('/api/plugins/')
        request.user = Mock()

        viewset = PluginViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        # Patch at source since it's a local import
        with patch('smoothschedule.scheduling.automations.registry.registry') as mock_registry:
            mock_registry.list_all.return_value = []

            with patch('smoothschedule.scheduling.schedule.serializers.PluginInfoSerializer') as mock_serializer:
                mock_serializer.return_value.data = []
                response = viewset.list(request)

        # Assert
        assert response.status_code == status.HTTP_200_OK


class TestPluginViewSetRetrieve:
    """Test PluginViewSet.retrieve action."""

    def test_retrieve_returns_plugin_details(self):
        """Test that retrieve returns plugin details."""
        from smoothschedule.scheduling.schedule.views import PluginViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.get('/api/plugins/test_plugin/')
        request.user = Mock()

        viewset = PluginViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        mock_plugin_class = Mock()
        mock_plugin_class.name = 'test_plugin'
        mock_plugin_class.display_name = 'Test Plugin'
        mock_plugin_class.description = 'A test plugin'
        mock_plugin_class.category = 'automation'
        mock_plugin_class.config_schema = {}

        # Patch at source since it's a local import
        with patch('smoothschedule.scheduling.automations.registry.registry') as mock_registry:
            mock_registry.get.return_value = mock_plugin_class

            with patch('smoothschedule.scheduling.schedule.serializers.PluginInfoSerializer') as mock_serializer:
                mock_serializer.return_value.data = {'name': 'test_plugin'}
                response = viewset.retrieve(request, pk='test_plugin')

        # Assert
        assert response.status_code == status.HTTP_200_OK

    def test_retrieve_returns_404_for_unknown_plugin(self):
        """Test that retrieve returns 404 for unknown plugin."""
        from smoothschedule.scheduling.schedule.views import PluginViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.get('/api/plugins/unknown/')
        request.user = Mock()

        viewset = PluginViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        # Patch at source since it's a local import
        with patch('smoothschedule.scheduling.automations.registry.registry') as mock_registry:
            mock_registry.get.return_value = None
            response = viewset.retrieve(request, pk='unknown')

        # Assert
        assert response.status_code == status.HTTP_404_NOT_FOUND
        assert 'not found' in response.data['error']


class TestPluginViewSetByCategory:
    """Test PluginViewSet.by_category action."""

    def test_by_category_returns_grouped_plugins(self):
        """Test that by_category returns plugins grouped by category."""
        from smoothschedule.scheduling.schedule.views import PluginViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.get('/api/plugins/by_category/')
        request.user = Mock()

        viewset = PluginViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        # Patch at source since it's a local import
        with patch('smoothschedule.scheduling.automations.registry.registry') as mock_registry:
            mock_registry.list_by_category.return_value = {'automation': []}
            response = viewset.by_category(request)

        # Assert
        assert response.status_code == status.HTTP_200_OK


class TestPluginTemplateViewSetInstall:
    """Test PluginTemplateViewSet.install action."""

    def test_install_requires_name(self):
        """Test that install requires name field."""
        from smoothschedule.scheduling.schedule.views import PluginTemplateViewSet
        from smoothschedule.scheduling.schedule.models import PluginTemplate

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/plugin-templates/1/install/', {}, format='json')
        request.data = {}
        request.user = Mock(is_authenticated=True)
        request.tenant = Mock()
        request.tenant.has_feature.return_value = True

        viewset = PluginTemplateViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        mock_template = Mock()
        mock_template.visibility = PluginTemplate.Visibility.PUBLIC
        mock_template.is_approved = True
        mock_template.author = Mock()

        with patch.object(viewset, 'get_object', return_value=mock_template):
            response = viewset.install(request, pk=1)

        # Assert
        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert 'name is required' in response.data['error']

    def test_install_returns_403_without_plugin_feature(self):
        """Test that install returns 403 when tenant lacks plugin feature."""
        from smoothschedule.scheduling.schedule.views import PluginTemplateViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/plugin-templates/1/install/', {'name': 'Test'}, format='json')
        request.data = {'name': 'Test'}
        request.user = Mock(is_authenticated=True)
        request.tenant = Mock()
        request.tenant.has_feature.return_value = False

        viewset = PluginTemplateViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        mock_template = Mock()

        with patch.object(viewset, 'get_object', return_value=mock_template):
            response = viewset.install(request, pk=1)

        # Assert
        assert response.status_code == status.HTTP_403_FORBIDDEN

    def test_install_blocks_unapproved_public_templates(self):
        """Test that install blocks unapproved public templates."""
        from smoothschedule.scheduling.schedule.views import PluginTemplateViewSet
        from smoothschedule.scheduling.schedule.models import PluginTemplate

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/plugin-templates/1/install/', {'name': 'Test'}, format='json')
        request.data = {'name': 'Test'}
        request.user = Mock(is_authenticated=True)
        request.tenant = Mock()
        request.tenant.has_feature.return_value = True

        viewset = PluginTemplateViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        mock_template = Mock()
        mock_template.visibility = PluginTemplate.Visibility.PUBLIC
        mock_template.is_approved = False

        with patch.object(viewset, 'get_object', return_value=mock_template):
            response = viewset.install(request, pk=1)

        # Assert
        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert 'not been approved' in response.data['error']


class TestPluginTemplateViewSetPublish:
    """Test PluginTemplateViewSet.publish action."""

    def test_publish_requires_ownership(self):
        """Test that publish requires user to own the template."""
        from smoothschedule.scheduling.schedule.views import PluginTemplateViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/plugin-templates/1/publish/', {})
        mock_user = Mock(id=1)
        request.user = mock_user

        viewset = PluginTemplateViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        mock_template = Mock()
        mock_template.author = Mock(id=2)  # Different user

        with patch.object(viewset, 'get_object', return_value=mock_template):
            response = viewset.publish(request, pk=1)

        # Assert
        assert response.status_code == status.HTTP_403_FORBIDDEN
        assert 'only publish your own' in response.data['error']

    def test_publish_requires_approval(self):
        """Test that publish requires template to be approved."""
        from smoothschedule.scheduling.schedule.views import PluginTemplateViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/plugin-templates/1/publish/', {})
        mock_user = Mock(id=1)
        request.user = mock_user

        viewset = PluginTemplateViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        mock_template = Mock()
        mock_template.author = mock_user
        mock_template.is_approved = False

        with patch.object(viewset, 'get_object', return_value=mock_template):
            response = viewset.publish(request, pk=1)

        # Assert
        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert 'must be approved' in response.data['error']


class TestPluginTemplateViewSetUnpublish:
    """Test PluginTemplateViewSet.unpublish action."""

    def test_unpublish_requires_ownership(self):
        """Test that unpublish requires user to own the template."""
        from smoothschedule.scheduling.schedule.views import PluginTemplateViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/plugin-templates/1/unpublish/', {})
        mock_user = Mock(id=1)
        request.user = mock_user

        viewset = PluginTemplateViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        mock_template = Mock()
        mock_template.author = Mock(id=2)  # Different user

        with patch.object(viewset, 'get_object', return_value=mock_template):
            response = viewset.unpublish(request, pk=1)

        # Assert
        assert response.status_code == status.HTTP_403_FORBIDDEN


class TestPluginTemplateViewSetApprove:
    """Test PluginTemplateViewSet.approve action."""

    def test_approve_rejects_already_approved(self):
        """Test that approve rejects already approved templates."""
        from smoothschedule.scheduling.schedule.views import PluginTemplateViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/plugin-templates/1/approve/', {})
        request.user = Mock(is_authenticated=True)

        viewset = PluginTemplateViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        mock_template = Mock()
        mock_template.is_approved = True

        with patch.object(viewset, 'get_object', return_value=mock_template):
            response = viewset.approve(request, pk=1)

        # Assert
        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert 'already approved' in response.data['error']


class TestPluginTemplateViewSetReject:
    """Test PluginTemplateViewSet.reject action."""

    def test_reject_sets_rejection_reason(self):
        """Test that reject saves rejection reason."""
        from smoothschedule.scheduling.schedule.views import PluginTemplateViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/plugin-templates/1/reject/', {'reason': 'Not suitable'}, format='json')
        request.data = {'reason': 'Not suitable'}
        request.user = Mock(is_authenticated=True)

        viewset = PluginTemplateViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        mock_template = Mock()

        with patch.object(viewset, 'get_object', return_value=mock_template):
            response = viewset.reject(request, pk=1)

        # Assert
        assert response.status_code == status.HTTP_200_OK
        assert mock_template.is_approved is False
        assert mock_template.rejection_reason == 'Not suitable'
        mock_template.save.assert_called_once()


class TestPluginInstallationViewSetRate:
    """Test PluginInstallationViewSet.rate action."""

    def test_rate_validates_rating_range(self):
        """Test that rate validates rating is between 1 and 5."""
        from smoothschedule.scheduling.schedule.views import PluginInstallationViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/plugin-installations/1/rate/', {'rating': 0}, format='json')
        request.data = {'rating': 0}
        request.user = Mock()

        viewset = PluginInstallationViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        mock_installation = Mock()

        with patch.object(viewset, 'get_object', return_value=mock_installation):
            response = viewset.rate(request, pk=1)

        # Assert
        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert 'between 1 and 5' in response.data['error']

    def test_rate_validates_rating_type(self):
        """Test that rate validates rating is an integer."""
        from smoothschedule.scheduling.schedule.views import PluginInstallationViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/plugin-installations/1/rate/', {'rating': 'five'}, format='json')
        request.data = {'rating': 'five'}
        request.user = Mock()

        viewset = PluginInstallationViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        mock_installation = Mock()

        with patch.object(viewset, 'get_object', return_value=mock_installation):
            response = viewset.rate(request, pk=1)

        # Assert
        assert response.status_code == status.HTTP_400_BAD_REQUEST


class TestPluginInstallationViewSetUpdateToLatest:
    """Test PluginInstallationViewSet.update_to_latest action."""

    def test_update_to_latest_returns_error_when_no_update(self):
        """Test that update_to_latest returns error when no update available."""
        from smoothschedule.scheduling.schedule.views import PluginInstallationViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/plugin-installations/1/update_to_latest/', {})
        request.user = Mock()

        viewset = PluginInstallationViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        mock_installation = Mock()
        mock_installation.has_update_available.return_value = False

        with patch.object(viewset, 'get_object', return_value=mock_installation):
            response = viewset.update_to_latest(request, pk=1)

        # Assert
        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert 'No update available' in response.data['error']


class TestEventPluginViewSetList:
    """Test EventPluginViewSet.list action."""

    def test_list_requires_event_id(self):
        """Test that list requires event_id query parameter."""
        from smoothschedule.scheduling.schedule.views import EventPluginViewSet
        from rest_framework.request import Request

        # Arrange
        factory = APIRequestFactory()
        django_request = factory.get('/api/event-plugins/')
        request = Request(django_request)
        request.user = Mock()

        viewset = EventPluginViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        response = viewset.list(request)

        # Assert
        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert 'event_id query parameter is required' in response.data['error']


class TestEventPluginViewSetToggle:
    """Test EventPluginViewSet.toggle action."""

    def test_toggle_toggles_is_active(self):
        """Test that toggle toggles the is_active flag."""
        from smoothschedule.scheduling.schedule.views import EventPluginViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/event-plugins/1/toggle/', {})
        request.user = Mock()

        viewset = EventPluginViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        mock_event_plugin = Mock()
        mock_event_plugin.is_active = True

        with patch.object(viewset, 'get_object', return_value=mock_event_plugin):
            with patch.object(viewset, 'get_serializer') as mock_get_serializer:
                mock_serializer = Mock()
                mock_serializer.data = {}
                mock_get_serializer.return_value = mock_serializer
                response = viewset.toggle(request, pk=1)

        # Assert
        assert response.status_code == status.HTTP_200_OK
        assert mock_event_plugin.is_active is False
        mock_event_plugin.save.assert_called_once()


class TestEventPluginViewSetTriggers:
    """Test EventPluginViewSet.triggers action."""

    def test_triggers_returns_trigger_options(self):
        """Test that triggers returns all available trigger options."""
        from smoothschedule.scheduling.schedule.views import EventPluginViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.get('/api/event-plugins/triggers/')
        request.user = Mock()

        viewset = EventPluginViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        response = viewset.triggers(request)

        # Assert
        assert response.status_code == status.HTTP_200_OK
        assert 'triggers' in response.data
        assert 'offset_presets' in response.data
        assert 'timing_groups' in response.data


class TestGlobalEventPluginViewSetToggle:
    """Test GlobalEventPluginViewSet.toggle action."""

    def test_toggle_toggles_is_active(self):
        """Test that toggle toggles the is_active flag."""
        from smoothschedule.scheduling.schedule.views import GlobalEventPluginViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/global-event-plugins/1/toggle/', {})
        request.user = Mock()

        viewset = GlobalEventPluginViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        mock_global_plugin = Mock()
        mock_global_plugin.is_active = True

        with patch.object(viewset, 'get_object', return_value=mock_global_plugin):
            with patch.object(viewset, 'get_serializer') as mock_get_serializer:
                mock_serializer = Mock()
                mock_serializer.data = {}
                mock_get_serializer.return_value = mock_serializer
                response = viewset.toggle(request, pk=1)

        # Assert
        assert response.status_code == status.HTTP_200_OK
        assert mock_global_plugin.is_active is False
        mock_global_plugin.save.assert_called_once()


class TestGlobalEventPluginViewSetReapply:
    """Test GlobalEventPluginViewSet.reapply action."""

    def test_reapply_requires_active_rule(self):
        """Test that reapply requires rule to be active."""
        from smoothschedule.scheduling.schedule.views import GlobalEventPluginViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/global-event-plugins/1/reapply/', {})
        request.user = Mock()

        viewset = GlobalEventPluginViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        mock_global_plugin = Mock()
        mock_global_plugin.is_active = False

        with patch.object(viewset, 'get_object', return_value=mock_global_plugin):
            response = viewset.reapply(request, pk=1)

        # Assert
        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert 'inactive rule' in response.data['error'].lower()

    def test_reapply_applies_to_all_events(self):
        """Test that reapply applies rule to all events."""
        from smoothschedule.scheduling.schedule.views import GlobalEventPluginViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/global-event-plugins/1/reapply/', {})
        request.user = Mock()

        viewset = GlobalEventPluginViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        mock_global_plugin = Mock()
        mock_global_plugin.is_active = True
        mock_global_plugin.apply_to_all_events.return_value = 10

        with patch.object(viewset, 'get_object', return_value=mock_global_plugin):
            response = viewset.reapply(request, pk=1)

        # Assert
        assert response.status_code == status.HTTP_200_OK
        assert response.data['events_affected'] == 10
        mock_global_plugin.apply_to_all_events.assert_called_once()


class TestCustomerViewSetRetrieve:
    """Test CustomerViewSet.retrieve for staff users."""

    def test_retrieve_returns_limited_fields_for_staff(self):
        """Test that retrieve returns limited fields for staff users."""
        from smoothschedule.scheduling.schedule.views import CustomerViewSet
        from smoothschedule.identity.users.models import User

        # Arrange
        factory = APIRequestFactory()
        request = factory.get('/api/customers/1/')
        mock_user = Mock()
        mock_user.role = User.Role.TENANT_STAFF
        request.user = mock_user
        request.tenant = Mock()

        viewset = CustomerViewSet()
        viewset.request = request
        viewset.format_kwarg = None
        viewset.kwargs = {'pk': 1}

        mock_customer = Mock()
        mock_customer.id = 1
        mock_customer.full_name = 'John Doe'
        mock_customer.first_name = 'John'
        mock_customer.last_name = 'Doe'
        mock_customer.city = 'New York'
        mock_customer.state = 'NY'
        mock_customer.zip = '10001'

        with patch.object(viewset, 'get_object', return_value=mock_customer):
            response = viewset.retrieve(request)

        # Assert
        assert response.status_code == status.HTTP_200_OK
        assert response.data['name'] == 'John Doe'
        # Should not include sensitive fields
        assert 'email' not in response.data


class TestCustomerViewSetPerformCreate:
    """Test CustomerViewSet.perform_create method."""

    def test_perform_create_sets_customer_role(self):
        """Test that perform_create sets role to CUSTOMER."""
        from smoothschedule.scheduling.schedule.views import CustomerViewSet
        from smoothschedule.identity.users.models import User

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/customers/', {})
        mock_user = Mock()
        mock_user.is_authenticated = True
        mock_user.tenant = Mock()
        request.user = mock_user
        request.sandbox_mode = False

        viewset = CustomerViewSet()
        viewset.request = request

        mock_serializer = Mock()

        # Act
        viewset.perform_create(mock_serializer)

        # Assert
        mock_serializer.save.assert_called_once()
        call_kwargs = mock_serializer.save.call_args[1]
        assert call_kwargs['role'] == User.Role.CUSTOMER
        assert call_kwargs['is_sandbox'] is False
        assert call_kwargs['tenant'] == mock_user.tenant


class TestStaffViewSetToggleActive:
    """Test StaffViewSet.toggle_active action."""

    def test_toggle_active_toggles_status(self):
        """Test that toggle_active toggles is_active status."""
        from smoothschedule.scheduling.schedule.views import StaffViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/staff/1/toggle_active/', {})
        request.user = Mock()
        request.tenant = Mock()

        viewset = StaffViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        mock_staff = Mock()
        mock_staff.id = 1
        mock_staff.is_active = True

        with patch.object(viewset, 'get_object', return_value=mock_staff):
            response = viewset.toggle_active(request, pk=1)

        # Assert
        assert response.status_code == status.HTTP_200_OK
        assert mock_staff.is_active is False
        mock_staff.save.assert_called_once()
        assert 'deactivated' in response.data['message']


class TestTimeBlockViewSetToggle:
    """Test TimeBlockViewSet.toggle action."""

    def test_toggle_toggles_is_active(self):
        """Test that toggle toggles is_active flag."""
        from smoothschedule.scheduling.schedule.views import TimeBlockViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/time-blocks/1/toggle/', {})
        request.user = Mock()

        viewset = TimeBlockViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        mock_block = Mock()
        mock_block.id = 1
        mock_block.is_active = True

        with patch.object(viewset, 'get_object', return_value=mock_block):
            response = viewset.toggle(request, pk=1)

        # Assert
        assert response.status_code == status.HTTP_200_OK
        assert mock_block.is_active is False
        mock_block.save.assert_called_once()


class TestTimeBlockViewSetPendingReviews:
    """Test TimeBlockViewSet.pending_reviews action."""

    def test_pending_reviews_requires_permission(self):
        """Test that pending_reviews checks user permission."""
        from smoothschedule.scheduling.schedule.views import TimeBlockViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.get('/api/time-blocks/pending_reviews/')
        mock_user = Mock()
        mock_user.can_review_time_off_requests.return_value = False
        request.user = mock_user

        viewset = TimeBlockViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        response = viewset.pending_reviews(request)

        # Assert
        assert response.status_code == status.HTTP_403_FORBIDDEN


class TestTimeBlockViewSetApproveSuccess:
    """Test TimeBlockViewSet.approve action success path."""

    def test_approve_approves_pending_block(self):
        """Test that approve approves pending blocks."""
        from smoothschedule.scheduling.schedule.views import TimeBlockViewSet
        from smoothschedule.scheduling.schedule.models import TimeBlock

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/time-blocks/1/approve/', {})
        # Manually set data attribute to simulate DRF Request
        request.data = {}
        mock_user = Mock()
        mock_user.can_review_time_off_requests.return_value = True
        request.user = mock_user

        viewset = TimeBlockViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        mock_block = Mock()
        mock_block.id = 1
        mock_block.approval_status = TimeBlock.ApprovalStatus.PENDING
        mock_block.reviewed_at = None

        with patch.object(viewset, 'get_object', return_value=mock_block):
            with patch('django.utils.timezone.now') as mock_now:
                mock_now.return_value = 'now'
                response = viewset.approve(request, pk=1)

        # Assert
        assert response.status_code == status.HTTP_200_OK
        assert mock_block.approval_status == TimeBlock.ApprovalStatus.APPROVED
        assert mock_block.reviewed_by == mock_user
        mock_block.save.assert_called_once()


class TestTimeBlockViewSetDenySuccess:
    """Test TimeBlockViewSet.deny action success path."""

    def test_deny_denies_pending_block(self):
        """Test that deny denies pending blocks."""
        from smoothschedule.scheduling.schedule.views import TimeBlockViewSet
        from smoothschedule.scheduling.schedule.models import TimeBlock

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/time-blocks/1/deny/', {'notes': 'Not available'})
        request.data = {'notes': 'Not available'}
        mock_user = Mock()
        mock_user.can_review_time_off_requests.return_value = True
        request.user = mock_user

        viewset = TimeBlockViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        mock_block = Mock()
        mock_block.id = 1
        mock_block.approval_status = TimeBlock.ApprovalStatus.PENDING

        with patch.object(viewset, 'get_object', return_value=mock_block):
            response = viewset.deny(request, pk=1)

        # Assert
        assert response.status_code == status.HTTP_200_OK
        assert mock_block.approval_status == TimeBlock.ApprovalStatus.DENIED
        assert mock_block.reviewed_by == mock_user
        mock_block.save.assert_called_once()


class TestEventViewSetSetStatusSuccess:
    """Test EventViewSet.set_status success path."""

    def test_set_status_succeeds_with_valid_transition(self):
        """Test that set_status succeeds when transition is valid."""
        from smoothschedule.scheduling.schedule.views import EventViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/events/1/set_status/', {'status': 'IN_PROGRESS'}, format='json')
        request.data = {'status': 'IN_PROGRESS'}
        mock_user = Mock()
        request.user = mock_user
        request.tenant = Mock()

        mock_event = Mock()
        mock_event.id = 1

        viewset = EventViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        with patch.object(viewset, 'get_object', return_value=mock_event):
            with patch('smoothschedule.communication.mobile.services.StatusMachine') as mock_machine_class:
                mock_machine = Mock()
                mock_machine.transition.return_value = mock_event
                mock_machine_class.return_value = mock_machine

                with patch.object(viewset, 'get_serializer') as mock_get_serializer:
                    mock_serializer = Mock()
                    mock_serializer.data = {'id': 1, 'status': 'IN_PROGRESS'}
                    mock_get_serializer.return_value = mock_serializer

                    response = viewset.set_status(request, pk=1)

        # Assert
        assert response.status_code == status.HTTP_200_OK
        assert response.data['success'] is True
        mock_machine.transition.assert_called_once()


class TestEventViewSetStatusHistorySuccess:
    """Test EventViewSet.status_history success path."""

    def test_status_history_returns_history(self):
        """Test that status_history returns event history."""
        from smoothschedule.scheduling.schedule.views import EventViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.get('/api/events/1/status_history/')
        request.user = Mock()
        request.tenant = Mock(id=1)

        mock_event = Mock(id=1)

        viewset = EventViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        mock_history = Mock()
        mock_history.old_status = 'SCHEDULED'
        mock_history.new_status = 'IN_PROGRESS'
        mock_history.changed_by = Mock(full_name='John Doe')
        mock_history.changed_at = Mock()
        mock_history.changed_at.isoformat.return_value = '2025-01-01T12:00:00'
        mock_history.notes = 'Started work'
        mock_history.source = 'web_app'
        mock_history.latitude = None
        mock_history.longitude = None

        with patch.object(viewset, 'get_object', return_value=mock_event):
            # Patch at source since it's a local import
            with patch('smoothschedule.communication.mobile.models.EventStatusHistory') as mock_model:
                mock_qs = Mock()
                mock_qs.select_related.return_value.order_by.return_value.__getitem__ = Mock(return_value=[mock_history])
                mock_model.objects.filter.return_value = mock_qs

                response = viewset.status_history(request, pk=1)

        # Assert
        assert response.status_code == status.HTTP_200_OK
        assert response.data['event_id'] == 1
        assert 'history' in response.data


class TestEventViewSetAllowedTransitionsSuccess:
    """Test EventViewSet.allowed_transitions success path."""

    def test_allowed_transitions_returns_transitions(self):
        """Test that allowed_transitions returns valid transitions."""
        from smoothschedule.scheduling.schedule.views import EventViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.get('/api/events/1/allowed_transitions/')
        request.user = Mock()
        request.tenant = Mock()

        mock_event = Mock()
        mock_event.id = 1
        mock_event.status = 'SCHEDULED'
        mock_event.get_status_display.return_value = 'Scheduled'

        viewset = EventViewSet()
        viewset.request = request
        viewset.format_kwarg = None

        with patch.object(viewset, 'get_object', return_value=mock_event):
            with patch('smoothschedule.communication.mobile.services.StatusMachine') as mock_machine_class:
                mock_machine = Mock()
                mock_machine.get_allowed_transitions.return_value = ['IN_PROGRESS', 'CANCELED']
                mock_machine_class.return_value = mock_machine

                with patch('smoothschedule.scheduling.schedule.views.Event') as mock_event_model:
                    mock_event_model.Status.choices = [
                        ('SCHEDULED', 'Scheduled'),
                        ('IN_PROGRESS', 'In Progress'),
                        ('CANCELED', 'Canceled'),
                    ]

                    response = viewset.allowed_transitions(request, pk=1)

        # Assert
        assert response.status_code == status.HTTP_200_OK
        assert response.data['event_id'] == 1
        assert response.data['current_status'] == 'SCHEDULED'
        assert len(response.data['allowed_transitions']) == 2


class TestEventViewSetGetStaffAssignedEvents:
    """Test EventViewSet._get_staff_assigned_events method."""

    def test_method_exists(self):
        """Test _get_staff_assigned_events method exists."""
        from smoothschedule.scheduling.schedule.views import EventViewSet

        viewset = EventViewSet()
        assert hasattr(viewset, '_get_staff_assigned_events')
        assert callable(viewset._get_staff_assigned_events)

    def test_method_signature(self):
        """Test method accepts user and queryset parameters."""
        from smoothschedule.scheduling.schedule.views import EventViewSet
        import inspect

        sig = inspect.signature(EventViewSet._get_staff_assigned_events)
        params = list(sig.parameters.keys())
        assert 'self' in params
        assert 'user' in params
        assert 'queryset' in params


class TestEventViewSetFilterQuerysetForTenant:
    """Test EventViewSet.filter_queryset_for_tenant method."""

    def test_method_exists(self):
        """Test filter_queryset_for_tenant method exists."""
        from smoothschedule.scheduling.schedule.views import EventViewSet

        viewset = EventViewSet()
        assert hasattr(viewset, 'filter_queryset_for_tenant')
        assert callable(viewset.filter_queryset_for_tenant)

    def test_staff_only_sees_assigned_events(self):
        """Test that staff role only sees events they are assigned to."""
        from smoothschedule.scheduling.schedule.views import EventViewSet
        from smoothschedule.identity.users.models import User

        # Arrange
        factory = APIRequestFactory()
        request = factory.get('/api/events/')
        mock_user = Mock()
        mock_user.role = User.Role.TENANT_STAFF
        mock_user.id = 123
        request.user = mock_user
        request.tenant = Mock()
        request.query_params = {}

        viewset = EventViewSet()
        viewset.request = request

        mock_queryset = Mock()

        with patch.object(viewset, '_get_staff_assigned_events') as mock_method:
            mock_method.return_value = mock_queryset
            result = viewset.filter_queryset_for_tenant(mock_queryset)

        mock_method.assert_called_once()

    def test_manager_sees_all_by_default(self):
        """Test that manager sees all events by default."""
        from smoothschedule.scheduling.schedule.views import EventViewSet
        from smoothschedule.identity.users.models import User

        # Arrange
        factory = APIRequestFactory()
        request = factory.get('/api/events/')
        mock_user = Mock()
        mock_user.role = User.Role.TENANT_MANAGER
        mock_user.id = 123
        request.user = mock_user
        request.tenant = Mock()
        request.query_params = {}

        viewset = EventViewSet()
        viewset.request = request

        mock_queryset = Mock()
        mock_queryset.filter.return_value = mock_queryset

        with patch.object(viewset, '_get_staff_assigned_events') as mock_method:
            result = viewset.filter_queryset_for_tenant(mock_queryset)

        # Should not call _get_staff_assigned_events for manager without filter
        mock_method.assert_not_called()

    def test_manager_with_assigned_to_me_filter(self):
        """Test that manager can filter to only their events."""
        from smoothschedule.scheduling.schedule.views import EventViewSet
        from smoothschedule.identity.users.models import User

        # Arrange
        factory = APIRequestFactory()
        request = factory.get('/api/events/?assigned_to=me')
        mock_user = Mock()
        mock_user.role = User.Role.TENANT_MANAGER
        mock_user.id = 123
        request.user = mock_user
        request.tenant = Mock()
        request.query_params = {'assigned_to': 'me'}

        viewset = EventViewSet()
        viewset.request = request

        mock_queryset = Mock()

        with patch.object(viewset, '_get_staff_assigned_events') as mock_method:
            mock_method.return_value = mock_queryset
            result = viewset.filter_queryset_for_tenant(mock_queryset)

        mock_method.assert_called_once()

    def test_filters_by_date_range(self):
        """Test filtering by start_date and end_date."""
        from smoothschedule.scheduling.schedule.views import EventViewSet
        from smoothschedule.identity.users.models import User

        # Arrange
        factory = APIRequestFactory()
        request = factory.get('/api/events/?start_date=2024-01-01T00:00:00Z&end_date=2024-01-31T23:59:59Z')
        mock_user = Mock()
        mock_user.role = User.Role.TENANT_OWNER
        mock_user.id = 123
        request.user = mock_user
        request.tenant = Mock()
        request.query_params = {
            'start_date': '2024-01-01T00:00:00Z',
            'end_date': '2024-01-31T23:59:59Z'
        }

        viewset = EventViewSet()
        viewset.request = request

        mock_queryset = Mock()
        mock_queryset.filter.return_value = mock_queryset

        result = viewset.filter_queryset_for_tenant(mock_queryset)

        # Should have called filter for date range
        assert mock_queryset.filter.call_count >= 2


class TestStaffViewSetFilterQueryset:
    """Test StaffViewSet filtering methods."""

    def test_method_exists(self):
        """Test filter_queryset_for_tenant method exists."""
        from smoothschedule.scheduling.schedule.views import StaffViewSet

        viewset = StaffViewSet()
        assert hasattr(viewset, 'filter_queryset_for_tenant')
        assert callable(viewset.filter_queryset_for_tenant)

    def test_viewset_has_search_fields(self):
        """Test StaffViewSet has search_fields defined."""
        from smoothschedule.scheduling.schedule.views import StaffViewSet

        assert hasattr(StaffViewSet, 'search_fields')
        assert 'email' in StaffViewSet.search_fields
        assert 'first_name' in StaffViewSet.search_fields
        assert 'last_name' in StaffViewSet.search_fields


class TestStaffViewSetPartialUpdate:
    """Test StaffViewSet.partial_update method."""

    def test_method_exists(self):
        """Test partial_update method exists."""
        from smoothschedule.scheduling.schedule.views import StaffViewSet

        viewset = StaffViewSet()
        assert hasattr(viewset, 'partial_update')
        assert callable(viewset.partial_update)

    def test_partial_update_filters_fields(self):
        """Test that partial_update only allows specific fields."""
        from smoothschedule.scheduling.schedule.views import StaffViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.patch('/api/staff/1/', data='{}', content_type='application/json')
        request.user = Mock(is_authenticated=True)
        request.data = {
            'is_active': False,
            'permissions': {'can_edit': True},
            'email': 'hacker@evil.com',
            'role': 'TENANT_OWNER',
        }

        mock_instance = Mock()
        mock_serializer = Mock()
        mock_serializer.data = {'id': 1, 'is_active': False}

        viewset = StaffViewSet()
        viewset.request = request
        viewset.format_kwarg = None
        viewset.kwargs = {'pk': 1}

        with patch.object(viewset, 'get_object', return_value=mock_instance):
            with patch.object(viewset, 'get_serializer', return_value=mock_serializer) as mock_get_ser:
                response = viewset.partial_update(request)

                # Verify serializer was called with only allowed fields
                call_args = mock_get_ser.call_args
                data = call_args[1]['data']
                assert 'is_active' in data
                assert 'permissions' in data
                assert 'email' not in data
                assert 'role' not in data


class TestPluginTemplateViewSetGetQueryset:
    """Test PluginTemplateViewSet.get_queryset method."""

    def test_marketplace_view_shows_public_approved(self):
        """Test marketplace view shows public approved templates."""
        from smoothschedule.scheduling.schedule.views import PluginTemplateViewSet
        from smoothschedule.scheduling.schedule.models import PluginTemplate

        # Arrange
        factory = APIRequestFactory()
        request = factory.get('/api/plugin-templates/?view=marketplace')
        request.user = Mock(is_authenticated=True)
        request.tenant = Mock()
        request.query_params = {'view': 'marketplace'}

        mock_queryset = Mock()
        mock_filtered = Mock()
        mock_queryset.filter.return_value = mock_filtered

        viewset = PluginTemplateViewSet()
        viewset.request = request

        with patch.object(PluginTemplateViewSet, 'queryset', mock_queryset):
            with patch('smoothschedule.scheduling.schedule.views.viewsets.ModelViewSet.get_queryset') as mock_parent:
                mock_parent.return_value = mock_queryset
                result = viewset.get_queryset()

        mock_queryset.filter.assert_called()

    def test_my_plugins_view_requires_permission(self):
        """Test my_plugins view requires plugin permission."""
        from smoothschedule.scheduling.schedule.views import PluginTemplateViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.get('/api/plugin-templates/?view=my_plugins')
        request.user = Mock(is_authenticated=True)
        request.tenant = Mock()
        request.tenant.has_feature.return_value = False
        request.query_params = {'view': 'my_plugins'}

        mock_queryset = Mock()
        mock_empty = Mock()
        mock_queryset.none.return_value = mock_empty

        viewset = PluginTemplateViewSet()
        viewset.request = request

        with patch.object(PluginTemplateViewSet, 'queryset', mock_queryset):
            with patch('smoothschedule.scheduling.schedule.views.viewsets.ModelViewSet.get_queryset') as mock_parent:
                mock_parent.return_value = mock_queryset
                result = viewset.get_queryset()

        # Should return empty queryset when no permission
        mock_queryset.none.assert_called()

    def test_my_plugins_view_filters_by_author(self):
        """Test my_plugins view filters by current user."""
        from smoothschedule.scheduling.schedule.views import PluginTemplateViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.get('/api/plugin-templates/?view=my_plugins')
        mock_user = Mock(is_authenticated=True, id=123)
        request.user = mock_user
        request.tenant = Mock()
        request.tenant.has_feature.return_value = True
        request.query_params = {'view': 'my_plugins'}

        mock_queryset = Mock()
        mock_filtered = Mock()
        mock_queryset.filter.return_value = mock_filtered

        viewset = PluginTemplateViewSet()
        viewset.request = request

        with patch.object(PluginTemplateViewSet, 'queryset', mock_queryset):
            with patch('smoothschedule.scheduling.schedule.views.viewsets.ModelViewSet.get_queryset') as mock_parent:
                mock_parent.return_value = mock_queryset
                result = viewset.get_queryset()

        # Should filter by author
        mock_queryset.filter.assert_called()

    def test_category_filter(self):
        """Test filtering by category."""
        from smoothschedule.scheduling.schedule.views import PluginTemplateViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.get('/api/plugin-templates/?view=marketplace&category=automation')
        request.user = Mock(is_authenticated=True)
        request.tenant = Mock()
        request.query_params = {'view': 'marketplace', 'category': 'automation'}

        mock_queryset = Mock()
        mock_filtered = Mock()
        mock_queryset.filter.return_value = mock_filtered
        mock_filtered.filter.return_value = mock_filtered

        viewset = PluginTemplateViewSet()
        viewset.request = request

        with patch.object(PluginTemplateViewSet, 'queryset', mock_queryset):
            with patch('smoothschedule.scheduling.schedule.views.viewsets.ModelViewSet.get_queryset') as mock_parent:
                mock_parent.return_value = mock_queryset
                result = viewset.get_queryset()

        # Should filter by category
        assert mock_queryset.filter.call_count >= 1

    def test_search_filter(self):
        """Test filtering by search query."""
        from smoothschedule.scheduling.schedule.views import PluginTemplateViewSet

        # Arrange
        factory = APIRequestFactory()
        request = factory.get('/api/plugin-templates/?view=marketplace&search=notification')
        request.user = Mock(is_authenticated=True)
        request.tenant = Mock()
        request.query_params = {'view': 'marketplace', 'search': 'notification'}

        mock_queryset = Mock()
        mock_filtered = Mock()
        mock_queryset.filter.return_value = mock_filtered
        mock_filtered.filter.return_value = mock_filtered

        viewset = PluginTemplateViewSet()
        viewset.request = request

        with patch.object(PluginTemplateViewSet, 'queryset', mock_queryset):
            with patch('smoothschedule.scheduling.schedule.views.viewsets.ModelViewSet.get_queryset') as mock_parent:
                mock_parent.return_value = mock_queryset
                result = viewset.get_queryset()

        # Should filter by search
        assert mock_queryset.filter.call_count >= 1


class TestPluginTemplateViewSetPerformCreate:
    """Test PluginTemplateViewSet.perform_create method."""

    def test_method_exists(self):
        """Test perform_create method exists."""
        from smoothschedule.scheduling.schedule.views import PluginTemplateViewSet

        viewset = PluginTemplateViewSet()
        assert hasattr(viewset, 'perform_create')
        assert callable(viewset.perform_create)

    def test_denies_without_plugin_permission(self):
        """Test that create is denied without plugin feature."""
        from smoothschedule.scheduling.schedule.views import PluginTemplateViewSet
        from rest_framework.exceptions import PermissionDenied

        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/api/plugin-templates/')
        request.user = Mock(is_authenticated=True)
        request.tenant = Mock()
        request.tenant.has_feature.return_value = False

        mock_serializer = Mock()
        mock_serializer.validated_data = {'plugin_code': 'test'}

        viewset = PluginTemplateViewSet()
        viewset.request = request

        # Assert
        with pytest.raises(PermissionDenied):
            viewset.perform_create(mock_serializer)


class TestResourceViewSetLocationAction:
    """Test ResourceViewSet.location action (employee location tracking)."""

    def test_action_exists(self):
        """Test location action exists on ResourceViewSet."""
        from smoothschedule.scheduling.schedule.views import ResourceViewSet

        viewset = ResourceViewSet()
        assert hasattr(viewset, 'location')
        assert callable(viewset.location)

    def test_returns_no_linked_user_message(self):
        """Test response when resource has no linked user."""
        from smoothschedule.scheduling.schedule.views import ResourceViewSet

        factory = APIRequestFactory()
        request = factory.get('/api/resources/1/location/')
        request.user = Mock(is_authenticated=True)
        request.tenant = Mock()

        mock_resource = Mock()
        mock_resource.user = None

        viewset = ResourceViewSet()
        viewset.request = request
        viewset.format_kwarg = None
        viewset.kwargs = {'pk': 1}

        with patch.object(viewset, 'get_object', return_value=mock_resource):
            response = viewset.location(request, pk=1)

        assert response.data['has_location'] is False
        assert 'no linked user' in response.data['message']
