"""
Unit tests for Contract tasks.

Tests Celery tasks for contract email notifications and lifecycle management.
"""
from unittest.mock import Mock, patch, MagicMock
from datetime import timedelta
import pytest
from django.utils import timezone


class TestSendContractEmailTask:
    """Tests for send_contract_email task."""

    def test_task_exists_and_is_shared_task(self):
        """Should be a shared Celery task."""
        from smoothschedule.scheduling.contracts.tasks import send_contract_email

        assert callable(send_contract_email)
        assert hasattr(send_contract_email, 'delay')

    def test_task_is_bound(self):
        """Should be a bound task."""
        from smoothschedule.scheduling.contracts.tasks import send_contract_email

        assert hasattr(send_contract_email, '__wrapped__')

    def test_task_has_max_retries(self):
        """Should have max_retries=3."""
        from smoothschedule.scheduling.contracts.tasks import send_contract_email

        assert send_contract_email.max_retries == 3


class TestSendContractReminderTask:
    """Tests for send_contract_reminder task."""

    def test_task_exists_and_is_shared_task(self):
        """Should be a shared Celery task."""
        from smoothschedule.scheduling.contracts.tasks import send_contract_reminder

        assert callable(send_contract_reminder)
        assert hasattr(send_contract_reminder, 'delay')

    def test_task_is_bound(self):
        """Should be a bound task."""
        from smoothschedule.scheduling.contracts.tasks import send_contract_reminder

        assert hasattr(send_contract_reminder, '__wrapped__')

    def test_task_has_max_retries(self):
        """Should have max_retries=3."""
        from smoothschedule.scheduling.contracts.tasks import send_contract_reminder

        assert send_contract_reminder.max_retries == 3


class TestSendContractSignedEmailsTask:
    """Tests for send_contract_signed_emails task."""

    def test_task_exists_and_is_shared_task(self):
        """Should be a shared Celery task."""
        from smoothschedule.scheduling.contracts.tasks import send_contract_signed_emails

        assert callable(send_contract_signed_emails)
        assert hasattr(send_contract_signed_emails, 'delay')

    def test_task_is_bound(self):
        """Should be a bound task."""
        from smoothschedule.scheduling.contracts.tasks import send_contract_signed_emails

        assert hasattr(send_contract_signed_emails, '__wrapped__')


class TestGenerateContractPdfTask:
    """Tests for generate_contract_pdf task."""

    def test_task_exists_and_is_shared_task(self):
        """Should be a shared Celery task."""
        from smoothschedule.scheduling.contracts.tasks import generate_contract_pdf

        assert callable(generate_contract_pdf)
        assert hasattr(generate_contract_pdf, 'delay')

    def test_task_is_bound(self):
        """Should be a bound task."""
        from smoothschedule.scheduling.contracts.tasks import generate_contract_pdf

        assert hasattr(generate_contract_pdf, '__wrapped__')


class TestCheckExpiredContractsTask:
    """Tests for check_expired_contracts task."""

    def test_task_exists_and_is_shared_task(self):
        """Should be a shared Celery task."""
        from smoothschedule.scheduling.contracts.tasks import check_expired_contracts

        assert callable(check_expired_contracts)
        assert hasattr(check_expired_contracts, 'delay')

    @patch('smoothschedule.scheduling.contracts.tasks.logger')
    def test_marks_expired_contracts(self, mock_logger):
        """Should mark pending contracts as expired when past expiration."""
        from smoothschedule.scheduling.contracts.tasks import check_expired_contracts
        from smoothschedule.scheduling.contracts.models import Contract

        mock_queryset = Mock()
        mock_queryset.count.return_value = 5
        mock_queryset.update.return_value = 5

        with patch.object(Contract.objects, 'filter', return_value=mock_queryset):
            result = check_expired_contracts()

        assert result == {'expired_count': 5}
        mock_queryset.update.assert_called_once_with(status='EXPIRED')

    @patch('smoothschedule.scheduling.contracts.tasks.logger')
    def test_returns_zero_when_no_expired_contracts(self, mock_logger):
        """Should return zero count when no contracts expired."""
        from smoothschedule.scheduling.contracts.tasks import check_expired_contracts
        from smoothschedule.scheduling.contracts.models import Contract

        mock_queryset = Mock()
        mock_queryset.count.return_value = 0
        mock_queryset.update.return_value = 0

        with patch.object(Contract.objects, 'filter', return_value=mock_queryset):
            result = check_expired_contracts()

        assert result == {'expired_count': 0}


class TestSendPendingRemindersTask:
    """Tests for send_pending_reminders task."""

    def test_task_exists_and_is_shared_task(self):
        """Should be a shared Celery task."""
        from smoothschedule.scheduling.contracts.tasks import send_pending_reminders

        assert callable(send_pending_reminders)
        assert hasattr(send_pending_reminders, 'delay')

    @patch('smoothschedule.scheduling.contracts.tasks.send_contract_reminder')
    @patch('smoothschedule.scheduling.contracts.tasks.logger')
    def test_queues_reminders_for_contracts_expiring_soon(self, mock_logger, mock_reminder_task):
        """Should queue reminders for contracts expiring in ~3 days."""
        from smoothschedule.scheduling.contracts.tasks import send_pending_reminders
        from smoothschedule.scheduling.contracts.models import Contract

        mock_contract1 = Mock()
        mock_contract1.id = 1
        mock_contract2 = Mock()
        mock_contract2.id = 2

        mock_queryset = Mock()
        mock_queryset.__iter__ = Mock(return_value=iter([mock_contract1, mock_contract2]))
        mock_queryset.select_related.return_value = mock_queryset

        with patch.object(Contract.objects, 'filter', return_value=mock_queryset):
            result = send_pending_reminders()

        assert result == {'reminders_queued': 2}
        assert mock_reminder_task.delay.call_count == 2

    @patch('smoothschedule.scheduling.contracts.tasks.send_contract_reminder')
    @patch('smoothschedule.scheduling.contracts.tasks.logger')
    def test_returns_zero_when_no_contracts_need_reminders(self, mock_logger, mock_reminder_task):
        """Should return zero count when no contracts need reminders."""
        from smoothschedule.scheduling.contracts.tasks import send_pending_reminders
        from smoothschedule.scheduling.contracts.models import Contract

        mock_queryset = Mock()
        mock_queryset.__iter__ = Mock(return_value=iter([]))
        mock_queryset.select_related.return_value = mock_queryset

        with patch.object(Contract.objects, 'filter', return_value=mock_queryset):
            result = send_pending_reminders()

        assert result == {'reminders_queued': 0}
        mock_reminder_task.delay.assert_not_called()

    @patch('smoothschedule.scheduling.contracts.tasks.send_contract_reminder')
    @patch('smoothschedule.scheduling.contracts.tasks.logger')
    def test_handles_reminder_queue_failure(self, mock_logger, mock_reminder_task):
        """Should continue processing when one reminder fails to queue."""
        from smoothschedule.scheduling.contracts.tasks import send_pending_reminders
        from smoothschedule.scheduling.contracts.models import Contract

        mock_contract1 = Mock()
        mock_contract1.id = 1
        mock_contract2 = Mock()
        mock_contract2.id = 2
        mock_contract3 = Mock()
        mock_contract3.id = 3

        mock_queryset = Mock()
        mock_queryset.__iter__ = Mock(return_value=iter([mock_contract1, mock_contract2, mock_contract3]))
        mock_queryset.select_related.return_value = mock_queryset

        # Second reminder fails
        mock_reminder_task.delay.side_effect = [None, Exception("Queue error"), None]

        with patch.object(Contract.objects, 'filter', return_value=mock_queryset):
            result = send_pending_reminders()

        # Should count 2 successful, 1 failed
        assert result == {'reminders_queued': 2}
        assert mock_reminder_task.delay.call_count == 3
        mock_logger.error.assert_called_once()
