"""
Unit tests for Ticket serializers.

Tests serializer validation logic without hitting the database.
"""
from unittest.mock import Mock, patch, MagicMock
from rest_framework.test import APIRequestFactory
import pytest

from smoothschedule.commerce.tickets.serializers import (
    TicketSerializer,
    TicketListSerializer,
    TicketCommentSerializer,
    TicketTemplateSerializer,
    CannedResponseSerializer,
    TicketEmailAddressSerializer,
)


class TestTicketSerializerValidation:
    """Test TicketSerializer validation logic."""

    def test_read_only_fields_not_writable(self):
        """Test that read-only fields are not included in writable fields."""
        serializer = TicketSerializer()
        writable_fields = [
            f for f in serializer.fields
            if not serializer.fields[f].read_only
        ]

        # These should NOT be writable
        assert 'id' not in writable_fields
        assert 'creator' not in writable_fields
        assert 'creator_email' not in writable_fields
        assert 'is_overdue' not in writable_fields
        assert 'created_at' not in writable_fields
        assert 'comments' not in writable_fields

    def test_writable_fields_present(self):
        """Test that writable fields are present."""
        serializer = TicketSerializer()
        writable_fields = [
            f for f in serializer.fields
            if not serializer.fields[f].read_only
        ]

        # These should be writable
        assert 'subject' in writable_fields
        assert 'description' in writable_fields
        assert 'priority' in writable_fields
        assert 'status' in writable_fields
        assert 'assignee' in writable_fields


class TestTicketSerializerCreate:
    """Test TicketSerializer create logic."""

    def test_create_sets_creator_from_request(self):
        """Test that create sets creator from authenticated user."""
        # Arrange
        factory = APIRequestFactory()
        request = factory.post('/tickets/')
        request.user = Mock(is_authenticated=True, tenant=Mock(id=1))

        serializer = TicketSerializer(context={'request': request})

        # Patch the Ticket model
        with patch.object(TicketSerializer, 'create') as mock_create:
            mock_create.return_value = Mock()

            validated_data = {
                'subject': 'Test Ticket',
                'description': 'Test description',
                'ticket_type': 'PLATFORM',
            }

            # The actual create method should set creator
            # This test verifies the serializer has the context it needs
            assert serializer.context['request'].user.is_authenticated

    def test_create_requires_tenant_for_non_platform_tickets(self):
        """Test that non-platform tickets require tenant."""
        factory = APIRequestFactory()
        request = factory.post('/tickets/')
        # User without tenant
        request.user = Mock(is_authenticated=True, tenant=None)

        serializer = TicketSerializer(
            data={
                'subject': 'Test Ticket',
                'description': 'Test description',
                'ticket_type': 'SUPPORT',  # Non-platform ticket
            },
            context={'request': request}
        )

        # Validation should pass at field level but fail in create
        # (The actual validation happens in create method)
        assert 'subject' in serializer.fields


class TestTicketSerializerUpdate:
    """Test TicketSerializer update logic."""

    def test_update_removes_tenant_from_data(self):
        """Test that update prevents changing tenant."""
        # Arrange
        mock_instance = Mock()
        mock_instance.tenant = Mock(id=1)
        mock_instance.creator = Mock(id=1)

        factory = APIRequestFactory()
        request = factory.patch('/tickets/1/')
        request.user = Mock(is_authenticated=True)

        serializer = TicketSerializer(
            instance=mock_instance,
            context={'request': request}
        )

        # The update method should strip tenant and creator
        with patch.object(TicketSerializer, 'update') as mock_update:
            validated_data = {
                'subject': 'Updated Subject',
                'tenant': Mock(id=2),  # Trying to change tenant
                'creator': Mock(id=2),  # Trying to change creator
            }

            # Simulate calling update
            # In real usage, tenant and creator would be stripped
            assert 'subject' in serializer.fields


class TestTicketListSerializer:
    """Test TicketListSerializer."""

    def test_excludes_comments(self):
        """Test that list serializer excludes comments for performance."""
        serializer = TicketListSerializer()
        assert 'comments' not in serializer.fields


class TestTicketCommentSerializer:
    """Test TicketCommentSerializer."""

    def test_all_fields_read_only_except_comment_text(self):
        """Test that most fields are read-only."""
        serializer = TicketCommentSerializer()

        # comment_text should be writable
        assert not serializer.fields['comment_text'].read_only

        # These should be read-only
        assert serializer.fields['id'].read_only
        assert serializer.fields['ticket'].read_only
        assert serializer.fields['author'].read_only
        assert serializer.fields['created_at'].read_only


class TestTicketTemplateSerializer:
    """Test TicketTemplateSerializer."""

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

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

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

        assert 'name' in writable
        assert 'description' in writable
        assert 'ticket_type' in writable
        assert 'subject_template' in writable


class TestCannedResponseSerializer:
    """Test CannedResponseSerializer."""

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

        assert serializer.fields['id'].read_only
        assert serializer.fields['use_count'].read_only
        assert serializer.fields['created_by'].read_only
        assert serializer.fields['created_at'].read_only

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

        assert 'title' in writable
        assert 'content' in writable
        assert 'category' in writable
        assert 'is_active' in writable


class TestTicketEmailAddressSerializer:
    """Test TicketEmailAddressSerializer."""

    def test_password_fields_write_only(self):
        """Test that password fields are write-only."""
        serializer = TicketEmailAddressSerializer()

        # Passwords should be write-only (not exposed in responses)
        assert serializer.fields['imap_password'].write_only
        assert serializer.fields['smtp_password'].write_only

    def test_computed_fields_read_only(self):
        """Test that computed fields are read-only."""
        serializer = TicketEmailAddressSerializer()

        assert serializer.fields['is_imap_configured'].read_only
        assert serializer.fields['is_smtp_configured'].read_only
        assert serializer.fields['is_fully_configured'].read_only

    def test_writable_configuration_fields(self):
        """Test that configuration fields are writable."""
        serializer = TicketEmailAddressSerializer()
        writable = [f for f in serializer.fields if not serializer.fields[f].read_only]

        assert 'display_name' in writable
        assert 'email_address' in writable
        assert 'imap_host' in writable
        assert 'imap_port' in writable
        assert 'smtp_host' in writable
        assert 'smtp_port' in writable


# ============================================================================
# Tests for serializer create/update methods (covering missing lines)
# ============================================================================


class TestTicketSerializerCreateMethod:
    """Test TicketSerializer.create method implementation."""

    def test_create_sets_creator_and_tenant_for_non_platform_ticket(self):
        """Test create sets both creator and tenant for non-platform tickets."""
        from smoothschedule.commerce.tickets.models import Ticket

        mock_tenant = Mock()
        mock_user = Mock()
        mock_user.is_authenticated = True
        mock_user.tenant = mock_tenant

        mock_request = Mock()
        mock_request.user = mock_user

        serializer = TicketSerializer(context={'request': mock_request})

        validated_data = {
            'subject': 'Test ticket',
            'description': 'Test description',
            'ticket_type': 'tenant',  # Not PLATFORM
        }

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

            serializer.create(validated_data)

        assert validated_data['creator'] == mock_user
        assert validated_data['tenant'] == mock_tenant

    def test_create_raises_error_when_no_tenant_for_non_platform(self):
        """Test create raises ValidationError when user has no tenant for non-platform ticket."""
        from rest_framework.exceptions import ValidationError

        mock_user = Mock()
        mock_user.is_authenticated = True
        mock_user.tenant = None

        mock_request = Mock()
        mock_request.user = mock_user

        serializer = TicketSerializer(context={'request': mock_request})

        validated_data = {
            'subject': 'Test ticket',
            'ticket_type': 'tenant',  # Non-platform
        }

        with pytest.raises(ValidationError) as exc_info:
            serializer.create(validated_data)

        assert 'tenant' in exc_info.value.detail

    def test_create_allows_platform_ticket_without_explicit_tenant(self):
        """Test create allows platform tickets without tenant."""
        from smoothschedule.commerce.tickets.models import Ticket

        mock_user = Mock()
        mock_user.is_authenticated = True
        mock_user.tenant = None

        mock_request = Mock()
        mock_request.user = mock_user

        serializer = TicketSerializer(context={'request': mock_request})

        validated_data = {
            'subject': 'Platform ticket',
            'ticket_type': Ticket.TicketType.PLATFORM,
        }

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

            result = serializer.create(validated_data)

        assert result.id == 1


class TestTicketSerializerUpdateMethod:
    """Test TicketSerializer.update method implementation."""

    def test_update_removes_tenant_and_creator(self):
        """Test update prevents changing tenant and creator."""
        mock_instance = Mock()
        new_tenant = Mock()
        new_creator = Mock()

        validated_data = {
            'subject': 'Updated subject',
            'tenant': new_tenant,
            'creator': new_creator,
        }

        serializer = TicketSerializer()

        with patch('smoothschedule.commerce.tickets.serializers.serializers.ModelSerializer.update') as mock_super_update:
            mock_super_update.return_value = mock_instance

            serializer.update(mock_instance, validated_data)

        # Verify super().update was called without tenant/creator
        call_args = mock_super_update.call_args
        assert call_args[0][0] == mock_instance
        updated_data = call_args[0][1]
        assert 'tenant' not in updated_data
        assert 'creator' not in updated_data


class TestTicketTemplateSerializerCreateMethod:
    """Test TicketTemplateSerializer.create method implementation."""

    def test_create_sets_tenant_from_user_when_not_provided(self):
        """Test create sets tenant from authenticated user when not provided."""
        mock_tenant = Mock()
        mock_user = Mock()
        mock_user.tenant = mock_tenant

        mock_request = Mock()
        mock_request.user = mock_user

        serializer = TicketTemplateSerializer(context={'request': mock_request})

        validated_data = {
            'name': 'Template 1',
            'description': 'A template',
        }

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

            serializer.create(validated_data)

        assert validated_data['tenant'] == mock_tenant

    def test_create_sets_tenant_when_provided_as_none(self):
        """Test create sets tenant when validated_data has tenant=None."""
        mock_tenant = Mock()
        mock_user = Mock()
        mock_user.tenant = mock_tenant

        mock_request = Mock()
        mock_request.user = mock_user

        serializer = TicketTemplateSerializer(context={'request': mock_request})

        validated_data = {
            'name': 'Template 1',
            'tenant': None,
        }

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

            serializer.create(validated_data)

        assert validated_data['tenant'] == mock_tenant

    def test_create_allows_platform_template_without_tenant(self):
        """Test create allows platform-wide templates for platform admins."""
        mock_user = Mock(spec=['id', 'email'])  # No tenant attr

        mock_request = Mock()
        mock_request.user = mock_user

        serializer = TicketTemplateSerializer(context={'request': mock_request})

        validated_data = {
            'name': 'Platform Template',
        }

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

            result = serializer.create(validated_data)

        assert result.id == 1


class TestCannedResponseSerializerCreateMethod:
    """Test CannedResponseSerializer.create method implementation."""

    def test_create_sets_created_by_and_tenant(self):
        """Test create sets both created_by and tenant."""
        mock_tenant = Mock()
        mock_user = Mock()
        mock_user.tenant = mock_tenant

        mock_request = Mock()
        mock_request.user = mock_user

        serializer = CannedResponseSerializer(context={'request': mock_request})

        validated_data = {
            'title': 'Canned Response',
            'content': 'Thank you for contacting us.',
        }

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

            serializer.create(validated_data)

        assert validated_data['created_by'] == mock_user
        assert validated_data['tenant'] == mock_tenant

    def test_create_sets_tenant_when_provided_as_none(self):
        """Test create sets tenant when validated_data has tenant=None."""
        mock_tenant = Mock()
        mock_user = Mock()
        mock_user.tenant = mock_tenant

        mock_request = Mock()
        mock_request.user = mock_user

        serializer = CannedResponseSerializer(context={'request': mock_request})

        validated_data = {
            'title': 'Canned Response',
            'content': 'Thank you.',
            'tenant': None,
        }

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

            serializer.create(validated_data)

        assert validated_data['tenant'] == mock_tenant

    def test_create_allows_platform_canned_response_without_tenant(self):
        """Test create allows platform-wide canned responses."""
        mock_user = Mock(spec=['id', 'email'])  # No tenant attr

        mock_request = Mock()
        mock_request.user = mock_user

        serializer = CannedResponseSerializer(context={'request': mock_request})

        validated_data = {
            'title': 'Platform Response',
            'content': 'Generic response.',
        }

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

            result = serializer.create(validated_data)

        assert result.id == 1


class TestTicketEmailAddressSerializerGetTenantName:
    """Test TicketEmailAddressSerializer.get_tenant_name method."""

    def test_get_tenant_name_returns_tenant_name(self):
        """Test returns tenant name when tenant exists."""
        serializer = TicketEmailAddressSerializer()

        mock_obj = Mock()
        mock_obj.tenant = Mock()
        mock_obj.tenant.name = 'Acme Corp'

        result = serializer.get_tenant_name(mock_obj)

        assert result == 'Acme Corp'

    def test_get_tenant_name_returns_platform_when_no_tenant(self):
        """Test returns 'Platform' when tenant is None."""
        serializer = TicketEmailAddressSerializer()

        mock_obj = Mock()
        mock_obj.tenant = None

        result = serializer.get_tenant_name(mock_obj)

        assert result == 'Platform'


class TestTicketEmailAddressSerializerCreateMethod:
    """Test TicketEmailAddressSerializer.create method implementation."""

    def test_create_sets_tenant_from_authenticated_user(self):
        """Test create sets tenant from authenticated user with tenant."""
        mock_tenant = Mock()
        mock_user = Mock()
        mock_user.is_authenticated = True
        mock_user.tenant = mock_tenant

        mock_request = Mock()
        mock_request.user = mock_user

        serializer = TicketEmailAddressSerializer(context={'request': mock_request})

        validated_data = {
            'email_address': 'support@example.com',
            'display_name': 'Support',
        }

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

            serializer.create(validated_data)

        assert validated_data['tenant'] == mock_tenant

    def test_create_does_not_set_tenant_when_already_provided(self):
        """Test create does not override explicitly provided tenant."""
        explicit_tenant = Mock(id=99)
        mock_user = Mock()
        mock_user.is_authenticated = True
        mock_user.tenant = Mock(id=1)

        mock_request = Mock()
        mock_request.user = mock_user

        serializer = TicketEmailAddressSerializer(context={'request': mock_request})

        validated_data = {
            'email_address': 'support@example.com',
            'tenant': explicit_tenant,
        }

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

            serializer.create(validated_data)

        assert validated_data['tenant'] == explicit_tenant

    def test_create_does_not_set_tenant_for_unauthenticated(self):
        """Test create does not set tenant for unauthenticated users."""
        mock_user = Mock()
        mock_user.is_authenticated = False

        mock_request = Mock()
        mock_request.user = mock_user

        serializer = TicketEmailAddressSerializer(context={'request': mock_request})

        validated_data = {
            'email_address': 'support@example.com',
        }

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

            result = serializer.create(validated_data)

        assert 'tenant' not in validated_data
        assert result.id == 1
