"""
Unit tests for scheduling/schedule/consumers.py

Tests WebSocket consumers and helper functions.
Note: Async tests are avoided as pytest-asyncio is not configured.
"""
from unittest.mock import Mock, patch, MagicMock
import pytest
import json


class TestCalendarConsumerClass:
    """Tests for CalendarConsumer class structure."""

    def test_class_exists(self):
        """Should have CalendarConsumer class."""
        from smoothschedule.scheduling.schedule.consumers import CalendarConsumer

        assert CalendarConsumer is not None

    def test_is_async_websocket_consumer(self):
        """Should be an AsyncWebsocketConsumer subclass."""
        from smoothschedule.scheduling.schedule.consumers import CalendarConsumer
        from channels.generic.websocket import AsyncWebsocketConsumer

        assert issubclass(CalendarConsumer, AsyncWebsocketConsumer)

    def test_has_connect_method(self):
        """Should have connect method."""
        from smoothschedule.scheduling.schedule.consumers import CalendarConsumer

        assert hasattr(CalendarConsumer, 'connect')
        assert callable(getattr(CalendarConsumer, 'connect'))

    def test_has_disconnect_method(self):
        """Should have disconnect method."""
        from smoothschedule.scheduling.schedule.consumers import CalendarConsumer

        assert hasattr(CalendarConsumer, 'disconnect')
        assert callable(getattr(CalendarConsumer, 'disconnect'))

    def test_has_receive_method(self):
        """Should have receive method."""
        from smoothschedule.scheduling.schedule.consumers import CalendarConsumer

        assert hasattr(CalendarConsumer, 'receive')
        assert callable(getattr(CalendarConsumer, 'receive'))

    def test_has_event_handlers(self):
        """Should have event handler methods."""
        from smoothschedule.scheduling.schedule.consumers import CalendarConsumer

        assert hasattr(CalendarConsumer, 'event_created')
        assert hasattr(CalendarConsumer, 'event_updated')
        assert hasattr(CalendarConsumer, 'event_deleted')
        assert hasattr(CalendarConsumer, 'event_status_changed')
        assert hasattr(CalendarConsumer, 'job_assigned')
        assert hasattr(CalendarConsumer, 'job_unassigned')


class TestResourceLocationConsumerClass:
    """Tests for ResourceLocationConsumer class structure."""

    def test_class_exists(self):
        """Should have ResourceLocationConsumer class."""
        from smoothschedule.scheduling.schedule.consumers import ResourceLocationConsumer

        assert ResourceLocationConsumer is not None

    def test_is_async_websocket_consumer(self):
        """Should be an AsyncWebsocketConsumer subclass."""
        from smoothschedule.scheduling.schedule.consumers import ResourceLocationConsumer
        from channels.generic.websocket import AsyncWebsocketConsumer

        assert issubclass(ResourceLocationConsumer, AsyncWebsocketConsumer)

    def test_has_connect_method(self):
        """Should have connect method."""
        from smoothschedule.scheduling.schedule.consumers import ResourceLocationConsumer

        assert hasattr(ResourceLocationConsumer, 'connect')
        assert callable(getattr(ResourceLocationConsumer, 'connect'))

    def test_has_disconnect_method(self):
        """Should have disconnect method."""
        from smoothschedule.scheduling.schedule.consumers import ResourceLocationConsumer

        assert hasattr(ResourceLocationConsumer, 'disconnect')

    def test_has_receive_method(self):
        """Should have receive method."""
        from smoothschedule.scheduling.schedule.consumers import ResourceLocationConsumer

        assert hasattr(ResourceLocationConsumer, 'receive')

    def test_has_location_update_handler(self):
        """Should have location_update handler."""
        from smoothschedule.scheduling.schedule.consumers import ResourceLocationConsumer

        assert hasattr(ResourceLocationConsumer, 'location_update')

    def test_has_tracking_stopped_handler(self):
        """Should have tracking_stopped handler."""
        from smoothschedule.scheduling.schedule.consumers import ResourceLocationConsumer

        assert hasattr(ResourceLocationConsumer, 'tracking_stopped')


class TestGetEventBroadcastData:
    """Tests for get_event_broadcast_data helper function."""

    def test_function_exists(self):
        """Should have get_event_broadcast_data function."""
        from smoothschedule.scheduling.schedule.consumers import get_event_broadcast_data

        assert callable(get_event_broadcast_data)

    def test_returns_basic_event_data(self):
        """Should return event data as dict."""
        from smoothschedule.scheduling.schedule.consumers import get_event_broadcast_data
        from datetime import datetime

        mock_event = Mock()
        mock_event.id = 123
        mock_event.title = 'Test Event'
        mock_event.start_time = datetime(2024, 1, 15, 10, 0)
        mock_event.end_time = datetime(2024, 1, 15, 11, 0)
        mock_event.status = 'scheduled'
        mock_event.notes = 'Test notes'
        mock_event.created_at = datetime(2024, 1, 1)
        mock_event.updated_at = datetime(2024, 1, 2)
        mock_event.service = None
        mock_event.deposit_amount = None
        mock_event.final_price = None

        result = get_event_broadcast_data(mock_event)

        assert result['id'] == 123
        assert result['title'] == 'Test Event'
        assert result['status'] == 'scheduled'
        assert result['notes'] == 'Test notes'
        assert '2024-01-15' in result['start_time']

    def test_includes_service_data_when_available(self):
        """Should include service data when event has service."""
        from smoothschedule.scheduling.schedule.consumers import get_event_broadcast_data
        from datetime import datetime
        from decimal import Decimal

        mock_service = Mock()
        mock_service.id = 10
        mock_service.name = 'Haircut'
        mock_service.duration = 30
        mock_service.price = Decimal('25.00')

        mock_event = Mock()
        mock_event.id = 123
        mock_event.title = 'Test Event'
        mock_event.start_time = datetime(2024, 1, 15, 10, 0)
        mock_event.end_time = datetime(2024, 1, 15, 11, 0)
        mock_event.status = 'scheduled'
        mock_event.notes = ''
        mock_event.created_at = datetime(2024, 1, 1)
        mock_event.updated_at = datetime(2024, 1, 2)
        mock_event.service = mock_service
        mock_event.deposit_amount = None
        mock_event.final_price = None

        result = get_event_broadcast_data(mock_event)

        assert 'service' in result
        assert result['service']['id'] == 10
        assert result['service']['name'] == 'Haircut'
        assert result['service']['duration'] == 30

    def test_includes_pricing_when_available(self):
        """Should include pricing data when available."""
        from smoothschedule.scheduling.schedule.consumers import get_event_broadcast_data
        from datetime import datetime
        from decimal import Decimal

        mock_event = Mock()
        mock_event.id = 123
        mock_event.title = 'Test Event'
        mock_event.start_time = datetime(2024, 1, 15, 10, 0)
        mock_event.end_time = datetime(2024, 1, 15, 11, 0)
        mock_event.status = 'scheduled'
        mock_event.notes = ''
        mock_event.created_at = datetime(2024, 1, 1)
        mock_event.updated_at = datetime(2024, 1, 2)
        mock_event.service = None
        mock_event.deposit_amount = Decimal('10.00')
        mock_event.final_price = Decimal('50.00')

        result = get_event_broadcast_data(mock_event)

        assert 'deposit_amount' in result
        assert 'final_price' in result
        assert result['deposit_amount'] == '10.00'
        assert result['final_price'] == '50.00'


class TestGetEventStaffUserIds:
    """Tests for get_event_staff_user_ids helper function."""

    def test_function_exists(self):
        """Should have get_event_staff_user_ids function."""
        from smoothschedule.scheduling.schedule.consumers import get_event_staff_user_ids

        assert callable(get_event_staff_user_ids)

    def test_function_signature(self):
        """Should accept event parameter."""
        from smoothschedule.scheduling.schedule.consumers import get_event_staff_user_ids
        import inspect

        sig = inspect.signature(get_event_staff_user_ids)
        params = list(sig.parameters.keys())

        assert 'event' in params


class TestBroadcastEventUpdate:
    """Tests for broadcast_event_update helper function."""

    def test_function_exists(self):
        """Should have broadcast_event_update function."""
        from smoothschedule.scheduling.schedule.consumers import broadcast_event_update

        assert callable(broadcast_event_update)

    def test_function_signature(self):
        """Should accept expected parameters."""
        from smoothschedule.scheduling.schedule.consumers import broadcast_event_update
        import inspect

        sig = inspect.signature(broadcast_event_update)
        params = list(sig.parameters.keys())

        assert 'event' in params
        assert 'update_type' in params
        assert 'changed_fields' in params
        assert 'old_status' in params


class TestBroadcastResourceLocationUpdate:
    """Tests for broadcast_resource_location_update helper function."""

    def test_function_exists(self):
        """Should have broadcast_resource_location_update function."""
        from smoothschedule.scheduling.schedule.consumers import broadcast_resource_location_update

        assert callable(broadcast_resource_location_update)

    def test_function_signature(self):
        """Should accept expected parameters."""
        from smoothschedule.scheduling.schedule.consumers import broadcast_resource_location_update
        import inspect

        sig = inspect.signature(broadcast_resource_location_update)
        params = list(sig.parameters.keys())

        assert 'resource_id' in params
        assert 'location_data' in params
        assert 'active_job' in params


class TestCalendarConsumerEventHandlers:
    """Tests for CalendarConsumer event handler methods."""

    def test_has_event_created_method(self):
        """Should have event_created handler."""
        from smoothschedule.scheduling.schedule.consumers import CalendarConsumer

        assert hasattr(CalendarConsumer, 'event_created')

    def test_has_event_updated_method(self):
        """Should have event_updated handler."""
        from smoothschedule.scheduling.schedule.consumers import CalendarConsumer

        assert hasattr(CalendarConsumer, 'event_updated')

    def test_has_event_deleted_method(self):
        """Should have event_deleted handler."""
        from smoothschedule.scheduling.schedule.consumers import CalendarConsumer

        assert hasattr(CalendarConsumer, 'event_deleted')

    def test_has_event_status_changed_method(self):
        """Should have event_status_changed handler."""
        from smoothschedule.scheduling.schedule.consumers import CalendarConsumer

        assert hasattr(CalendarConsumer, 'event_status_changed')

    def test_has_job_assigned_method(self):
        """Should have job_assigned handler."""
        from smoothschedule.scheduling.schedule.consumers import CalendarConsumer

        assert hasattr(CalendarConsumer, 'job_assigned')

    def test_has_job_unassigned_method(self):
        """Should have job_unassigned handler."""
        from smoothschedule.scheduling.schedule.consumers import CalendarConsumer

        assert hasattr(CalendarConsumer, 'job_unassigned')


class TestResourceLocationConsumerEventHandlers:
    """Tests for ResourceLocationConsumer event handler methods."""

    def test_has_location_update_method(self):
        """Should have location_update handler."""
        from smoothschedule.scheduling.schedule.consumers import ResourceLocationConsumer

        assert hasattr(ResourceLocationConsumer, 'location_update')

    def test_has_tracking_stopped_method(self):
        """Should have tracking_stopped handler."""
        from smoothschedule.scheduling.schedule.consumers import ResourceLocationConsumer

        assert hasattr(ResourceLocationConsumer, 'tracking_stopped')


class TestGetEventBroadcastDataNullHandling:
    """Tests for get_event_broadcast_data null handling."""

    def test_handles_null_start_time(self):
        """Should handle null start_time."""
        from smoothschedule.scheduling.schedule.consumers import get_event_broadcast_data

        mock_event = Mock()
        mock_event.id = 1
        mock_event.title = 'Test'
        mock_event.start_time = None
        mock_event.end_time = None
        mock_event.status = 'scheduled'
        mock_event.notes = ''
        mock_event.created_at = None
        mock_event.updated_at = None
        mock_event.service = None
        mock_event.deposit_amount = None
        mock_event.final_price = None

        result = get_event_broadcast_data(mock_event)

        assert result['start_time'] is None
        assert result['end_time'] is None

    def test_handles_null_created_at(self):
        """Should handle null created_at."""
        from smoothschedule.scheduling.schedule.consumers import get_event_broadcast_data
        from datetime import datetime

        mock_event = Mock()
        mock_event.id = 1
        mock_event.title = 'Test'
        mock_event.start_time = datetime(2024, 1, 15, 10, 0)
        mock_event.end_time = datetime(2024, 1, 15, 11, 0)
        mock_event.status = 'scheduled'
        mock_event.notes = ''
        mock_event.created_at = None
        mock_event.updated_at = None
        mock_event.service = None
        mock_event.deposit_amount = None
        mock_event.final_price = None

        result = get_event_broadcast_data(mock_event)

        assert result['created_at'] is None
        assert result['updated_at'] is None


class TestGetEventStaffUserIdsExecution:
    """Tests for get_event_staff_user_ids execution."""

    def test_returns_list_type(self):
        """Should return a list from get_event_staff_user_ids."""
        from smoothschedule.scheduling.schedule.consumers import get_event_staff_user_ids
        import inspect

        sig = inspect.signature(get_event_staff_user_ids)
        params = list(sig.parameters.keys())

        # Verify function accepts event parameter
        assert 'event' in params


class TestBroadcastEventUpdateExecution:
    """Tests for broadcast_event_update execution."""

    @patch('channels.layers.get_channel_layer')
    def test_skips_when_no_channel_layer(self, mock_get_layer):
        """Should skip when no channel layer configured."""
        import asyncio
        from smoothschedule.scheduling.schedule.consumers import broadcast_event_update

        mock_get_layer.return_value = None
        mock_event = Mock()

        # Run async function
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        try:
            result = loop.run_until_complete(broadcast_event_update(mock_event, 'event_created'))
        finally:
            loop.close()

        # Should not raise
        assert result is None


class TestBroadcastResourceLocationUpdateExecution:
    """Tests for broadcast_resource_location_update execution."""

    @patch('channels.layers.get_channel_layer')
    def test_skips_when_no_channel_layer(self, mock_get_layer):
        """Should skip when no channel layer configured."""
        import asyncio
        from smoothschedule.scheduling.schedule.consumers import broadcast_resource_location_update

        mock_get_layer.return_value = None

        # Run async function
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        try:
            result = loop.run_until_complete(
                broadcast_resource_location_update(1, {'lat': 0, 'lng': 0})
            )
        finally:
            loop.close()

        # Should not raise
        assert result is None


class TestCalendarConsumerGroupNames:
    """Tests for CalendarConsumer group name generation."""

    def test_has_calendar_group_property(self):
        """Should have calendar_group property or method."""
        from smoothschedule.scheduling.schedule.consumers import CalendarConsumer

        # The consumer should have a way to identify calendar groups
        consumer = CalendarConsumer()
        assert consumer is not None


class TestResourceLocationConsumerGroupNames:
    """Tests for ResourceLocationConsumer group name generation."""

    def test_has_tracking_group_property(self):
        """Should have tracking group property or method."""
        from smoothschedule.scheduling.schedule.consumers import ResourceLocationConsumer

        # The consumer should have a way to identify tracking groups
        consumer = ResourceLocationConsumer()
        assert consumer is not None
