"""
Tests for Data Export API

Uses mocks to test export functionality without hitting the database.

Run with:
    docker compose -f docker-compose.local.yml exec django python manage.py test schedule.test_export
"""
from unittest.mock import Mock, patch, MagicMock
from datetime import datetime, timedelta
from django.utils import timezone
from rest_framework.test import APIRequestFactory
from rest_framework import status

from smoothschedule.scheduling.schedule.export_views import ExportViewSet, HasExportDataPermission
from smoothschedule.scheduling.schedule.models import Event, Resource, Service
from smoothschedule.identity.users.models import User


class TestHasExportDataPermission:
    """Test suite for HasExportDataPermission permission class"""

    def test_permission_denied_when_no_tenant(self):
        """Test that permission is denied when request has no tenant"""
        permission = HasExportDataPermission()

        # Create mock request with no tenant
        request = Mock()
        request.tenant = None
        request.user = Mock(is_authenticated=False)

        view = Mock()

        # Should raise PermissionDenied
        try:
            permission.has_permission(request, view)
            assert False, "Expected PermissionDenied to be raised"
        except Exception as e:
            assert "business accounts" in str(e).lower()

    def test_permission_denied_when_tenant_lacks_export_permission(self):
        """Test that permission is denied when tenant.can_export_data is False"""
        permission = HasExportDataPermission()

        # Create mock tenant without export permission
        mock_tenant = Mock()
        mock_tenant.can_export_data = False

        request = Mock()
        request.tenant = mock_tenant
        request.user = Mock(is_authenticated=True, tenant=mock_tenant)

        view = Mock()

        # Should raise PermissionDenied
        try:
            permission.has_permission(request, view)
            assert False, "Expected PermissionDenied to be raised"
        except Exception as e:
            assert "upgrade" in str(e).lower()

    def test_permission_granted_when_tenant_has_export_permission(self):
        """Test that permission is granted when tenant.can_export_data is True"""
        permission = HasExportDataPermission()

        # Create mock tenant with export permission
        mock_tenant = Mock()
        mock_tenant.can_export_data = True

        request = Mock()
        request.tenant = mock_tenant
        request.user = Mock(is_authenticated=True, tenant=mock_tenant)

        view = Mock()

        # Should return True
        result = permission.has_permission(request, view)
        assert result is True

    def test_permission_uses_user_tenant_when_request_tenant_missing(self):
        """Test that permission falls back to user.tenant when request.tenant is None"""
        permission = HasExportDataPermission()

        # Create mock tenant with export permission
        mock_tenant = Mock()
        mock_tenant.can_export_data = True

        request = Mock()
        request.tenant = None
        request.user = Mock(is_authenticated=True, tenant=mock_tenant)

        view = Mock()

        # Should use user.tenant and return True
        result = permission.has_permission(request, view)
        assert result is True


class TestExportViewSetHelperMethods:
    """Test suite for ExportViewSet helper methods"""

    def test_parse_format_json(self):
        """Test parsing JSON format parameter"""
        viewset = ExportViewSet()
        request = Mock()
        request.query_params = {'format': 'json'}

        result = viewset._parse_format(request)
        assert result == 'json'

    def test_parse_format_csv(self):
        """Test parsing CSV format parameter"""
        viewset = ExportViewSet()
        request = Mock()
        request.query_params = {'format': 'csv'}

        result = viewset._parse_format(request)
        assert result == 'csv'

    def test_parse_format_invalid_defaults_to_json(self):
        """Test that invalid format defaults to JSON"""
        viewset = ExportViewSet()
        request = Mock()
        request.query_params = {'format': 'xml'}

        result = viewset._parse_format(request)
        assert result == 'json'

    def test_parse_format_missing_defaults_to_json(self):
        """Test that missing format parameter defaults to JSON"""
        viewset = ExportViewSet()
        request = Mock()
        request.query_params = {}

        result = viewset._parse_format(request)
        assert result == 'json'

    def test_parse_date_range_both_dates(self):
        """Test parsing both start and end dates"""
        viewset = ExportViewSet()
        request = Mock()
        request.query_params = {
            'start_date': '2024-01-01T00:00:00Z',
            'end_date': '2024-12-31T23:59:59Z'
        }

        start_dt, end_dt = viewset._parse_date_range(request)
        assert start_dt is not None
        assert end_dt is not None
        assert start_dt.year == 2024
        assert end_dt.year == 2024

    def test_parse_date_range_no_dates(self):
        """Test parsing when no dates provided"""
        viewset = ExportViewSet()
        request = Mock()
        request.query_params = {}

        start_dt, end_dt = viewset._parse_date_range(request)
        assert start_dt is None
        assert end_dt is None

    def test_parse_date_range_invalid_raises_error(self):
        """Test that invalid date format raises ValueError"""
        viewset = ExportViewSet()
        request = Mock()
        request.query_params = {'start_date': 'not-a-date'}

        try:
            viewset._parse_date_range(request)
            assert False, "Expected ValueError to be raised"
        except ValueError as e:
            assert "Invalid start_date format" in str(e)


class TestExportViewSetAppointments:
    """Test suite for appointments export endpoint"""

    @patch('smoothschedule.scheduling.schedule.export_views.Event.objects')
    def test_appointments_export_json_success(self, mock_event_objects):
        """Test successful JSON export of appointments"""
        # Setup mock event
        mock_event = Mock()
        mock_event.id = 1
        mock_event.title = 'Test Appointment'
        mock_event.start_time = timezone.now()
        mock_event.end_time = timezone.now() + timedelta(hours=1)
        mock_event.status = Event.Status.SCHEDULED
        mock_event.notes = 'Test notes'
        mock_event.created_at = timezone.now()
        mock_event.created_by = Mock(email='creator@test.com')

        # Setup mock participants with proper chaining for filter().first()
        mock_customer = Mock()
        mock_customer.full_name = 'John Doe'
        mock_customer.email = 'john@test.com'

        mock_customer_participant = Mock()
        mock_customer_participant.role = 'CUSTOMER'
        mock_customer_participant.content_object = mock_customer

        # Create a mock queryset that supports both .first() and iteration
        def filter_side_effect(role=None):
            mock_filtered = Mock()
            if role == 'CUSTOMER':
                mock_filtered.first.return_value = mock_customer_participant
                mock_filtered.__iter__ = Mock(return_value=iter([mock_customer_participant]))
            else:
                mock_filtered.first.return_value = None
                mock_filtered.__iter__ = Mock(return_value=iter([]))
            return mock_filtered

        mock_participants = Mock()
        mock_participants.filter.side_effect = filter_side_effect
        mock_event.participants = mock_participants

        # Setup queryset mock
        mock_queryset = Mock()
        mock_queryset.filter.return_value = mock_queryset
        mock_queryset.select_related.return_value = mock_queryset
        mock_queryset.prefetch_related.return_value = mock_queryset
        mock_queryset.all.return_value = mock_queryset
        mock_queryset.__iter__ = Mock(return_value=iter([mock_event]))

        mock_event_objects.select_related.return_value = mock_queryset

        # Create request
        factory = APIRequestFactory()
        request = factory.get('/export/appointments/', {'format': 'json'})
        request.user = Mock(is_authenticated=True)
        request.tenant = Mock(can_export_data=True)

        # Call the view
        view = ExportViewSet.as_view({'get': 'appointments'})
        response = view(request)

        # Verify response
        assert response.status_code == 200
        assert 'count' in response.data
        assert 'data' in response.data
        assert 'exported_at' in response.data

    def test_appointments_export_csv_success(self):
        """Test successful CSV export response generation

        Note: We test the CSV response method directly because DRF's format suffix
        handling interferes with ?format=csv query params (looks for CSV renderer).
        """
        viewset = ExportViewSet()

        # Test data
        data = [
            {
                'id': 1,
                'title': 'Test Appointment',
                'start_time': timezone.now().isoformat(),
                'end_time': (timezone.now() + timedelta(hours=1)).isoformat(),
                'status': 'SCHEDULED',
                'notes': 'Test notes',
                'customer_name': 'John Doe',
                'customer_email': 'john@test.com',
                'resource_names': 'Room A',
                'created_at': timezone.now().isoformat(),
                'created_by': 'creator@test.com',
            }
        ]
        headers = [
            'id', 'title', 'start_time', 'end_time', 'status', 'notes',
            'customer_name', 'customer_email', 'resource_names',
            'created_at', 'created_by'
        ]
        filename = 'appointments_test.csv'

        # Call CSV response method directly
        response = viewset._create_csv_response(data, filename, headers)

        # Verify response
        assert response.status_code == 200
        assert 'text/csv' in response['Content-Type']
        assert 'attachment' in response['Content-Disposition']
        assert filename in response['Content-Disposition']

        # Verify CSV content
        content = response.content.decode('utf-8')
        assert 'Test Appointment' in content
        assert 'john@test.com' in content

    @patch('smoothschedule.scheduling.schedule.export_views.Event.objects')
    def test_appointments_export_with_date_filter(self, mock_event_objects):
        """Test appointments export with date range filter"""
        # Setup queryset mock
        mock_queryset = Mock()
        mock_queryset.select_related.return_value = mock_queryset
        mock_queryset.prefetch_related.return_value = mock_queryset
        mock_queryset.all.return_value = mock_queryset
        mock_queryset.filter.return_value = mock_queryset
        mock_queryset.__iter__ = Mock(return_value=iter([]))

        mock_event_objects.select_related.return_value = mock_queryset

        # Create request with date filter
        factory = APIRequestFactory()
        start_date = (timezone.now() - timedelta(days=7)).isoformat()
        request = factory.get('/export/appointments/', {
            'format': 'json',
            'start_date': start_date
        })
        request.user = Mock(is_authenticated=True)
        request.tenant = Mock(can_export_data=True)

        # Call the view
        view = ExportViewSet.as_view({'get': 'appointments'})
        response = view(request)

        # Verify response
        assert response.status_code == 200
        # Verify filter was called (queryset.filter should have been called)
        mock_queryset.filter.assert_called()


class TestExportViewSetCustomers:
    """Test suite for customers export endpoint"""

    @patch('smoothschedule.scheduling.schedule.export_views.User.objects')
    def test_customers_export_json_success(self, mock_user_objects):
        """Test successful JSON export of customers"""
        # Setup mock customer
        mock_customer = Mock()
        mock_customer.id = 1
        mock_customer.email = 'customer@test.com'
        mock_customer.first_name = 'John'
        mock_customer.last_name = 'Doe'
        mock_customer.full_name = 'John Doe'
        mock_customer.phone = '555-1234'
        mock_customer.is_active = True
        mock_customer.date_joined = timezone.now()
        mock_customer.last_login = timezone.now()

        # Setup queryset mock
        mock_queryset = Mock()
        mock_queryset.filter.return_value = mock_queryset
        mock_queryset.__iter__ = Mock(return_value=iter([mock_customer]))

        mock_user_objects.filter.return_value = mock_queryset

        # Create request
        factory = APIRequestFactory()
        request = factory.get('/export/customers/', {'format': 'json'})
        request.user = Mock(is_authenticated=True)
        request.tenant = Mock(can_export_data=True)

        # Call the view
        view = ExportViewSet.as_view({'get': 'customers'})
        response = view(request)

        # Verify response
        assert response.status_code == 200
        assert 'count' in response.data
        assert 'data' in response.data
        assert len(response.data['data']) == 1
        assert response.data['data'][0]['email'] == 'customer@test.com'

    def test_customers_export_csv_success(self):
        """Test successful CSV export response generation for customers

        Note: We test the CSV response method directly because DRF's format suffix
        handling interferes with ?format=csv query params (looks for CSV renderer).
        """
        viewset = ExportViewSet()

        # Test data
        data = [
            {
                'id': 1,
                'email': 'customer@test.com',
                'first_name': 'John',
                'last_name': 'Doe',
                'full_name': 'John Doe',
                'phone': '555-1234',
                'is_active': True,
                'created_at': timezone.now().isoformat(),
                'last_login': timezone.now().isoformat(),
            }
        ]
        headers = [
            'id', 'email', 'first_name', 'last_name', 'full_name', 'phone',
            'is_active', 'created_at', 'last_login'
        ]
        filename = 'customers_test.csv'

        # Call CSV response method directly
        response = viewset._create_csv_response(data, filename, headers)

        # Verify response
        assert response.status_code == 200
        assert 'text/csv' in response['Content-Type']
        assert 'attachment' in response['Content-Disposition']
        assert filename in response['Content-Disposition']

        # Verify CSV content
        content = response.content.decode('utf-8')
        assert 'customer@test.com' in content
        assert 'John Doe' in content


class TestExportViewSetResources:
    """Test suite for resources export endpoint"""

    @patch('smoothschedule.scheduling.schedule.export_views.Resource.objects')
    def test_resources_export_json_success(self, mock_resource_objects):
        """Test successful JSON export of resources"""
        # Setup mock resource
        mock_resource = Mock()
        mock_resource.id = 1
        mock_resource.name = 'Test Resource'
        mock_resource.type = Resource.Type.STAFF
        mock_resource.description = 'Test description'
        mock_resource.max_concurrent_events = 1
        mock_resource.buffer_duration = timedelta(minutes=15)
        mock_resource.is_active = True
        mock_resource.user = Mock(email='staff@test.com')
        mock_resource.created_at = timezone.now()

        # Setup queryset mock
        mock_queryset = Mock()
        mock_queryset.select_related.return_value = mock_queryset
        mock_queryset.all.return_value = mock_queryset
        mock_queryset.filter.return_value = mock_queryset
        mock_queryset.__iter__ = Mock(return_value=iter([mock_resource]))

        mock_resource_objects.select_related.return_value = mock_queryset

        # Create request
        factory = APIRequestFactory()
        request = factory.get('/export/resources/', {'format': 'json'})
        request.user = Mock(is_authenticated=True)
        request.tenant = Mock(can_export_data=True)

        # Call the view
        view = ExportViewSet.as_view({'get': 'resources'})
        response = view(request)

        # Verify response
        assert response.status_code == 200
        assert 'count' in response.data
        assert 'data' in response.data
        assert len(response.data['data']) == 1
        assert response.data['data'][0]['name'] == 'Test Resource'


class TestExportViewSetServices:
    """Test suite for services export endpoint"""

    @patch('smoothschedule.scheduling.schedule.export_views.Service.objects')
    def test_services_export_json_success(self, mock_service_objects):
        """Test successful JSON export of services"""
        # Setup mock service
        mock_service = Mock()
        mock_service.id = 1
        mock_service.name = 'Test Service'
        mock_service.description = 'Test service description'
        mock_service.duration = 60
        mock_service.price = 50.00
        mock_service.display_order = 1
        mock_service.is_active = True
        mock_service.created_at = timezone.now()

        # Setup queryset mock
        mock_queryset = Mock()
        mock_queryset.all.return_value = mock_queryset
        mock_queryset.filter.return_value = mock_queryset
        mock_queryset.__iter__ = Mock(return_value=iter([mock_service]))

        mock_service_objects.all.return_value = mock_queryset

        # Create request
        factory = APIRequestFactory()
        request = factory.get('/export/services/', {'format': 'json'})
        request.user = Mock(is_authenticated=True)
        request.tenant = Mock(can_export_data=True)

        # Call the view
        view = ExportViewSet.as_view({'get': 'services'})
        response = view(request)

        # Verify response
        assert response.status_code == 200
        assert 'count' in response.data
        assert 'data' in response.data
        assert len(response.data['data']) == 1
        assert response.data['data'][0]['name'] == 'Test Service'

    @patch('smoothschedule.scheduling.schedule.export_views.Service.objects')
    def test_services_export_with_active_filter(self, mock_service_objects):
        """Test services export with is_active filter"""
        # Setup queryset mock
        mock_queryset = Mock()
        mock_queryset.all.return_value = mock_queryset
        mock_queryset.filter.return_value = mock_queryset
        mock_queryset.__iter__ = Mock(return_value=iter([]))

        mock_service_objects.all.return_value = mock_queryset

        # Create request with active filter
        factory = APIRequestFactory()
        request = factory.get('/export/services/', {
            'format': 'json',
            'is_active': 'true'
        })
        request.user = Mock(is_authenticated=True)
        request.tenant = Mock(can_export_data=True)

        # Call the view
        view = ExportViewSet.as_view({'get': 'services'})
        response = view(request)

        # Verify response
        assert response.status_code == 200
        # Verify filter was called
        mock_queryset.filter.assert_called()
