"""
Tests for billing Celery tasks.
"""
from datetime import timedelta
from unittest.mock import Mock, patch
import pytest
from django.utils import timezone

from smoothschedule.billing.models import TenantCustomTier
from smoothschedule.billing.tasks import check_subscription_grace_periods


class TestCheckSubscriptionGracePeriods:
    """Test the check_subscription_grace_periods task."""

    def test_task_clears_grace_period_when_subscription_becomes_active(self):
        """When subscription becomes active, grace_period_started_at should be cleared."""
        # Arrange
        mock_custom_tier = Mock(spec=TenantCustomTier)
        mock_custom_tier.grace_period_started_at = timezone.now() - timedelta(days=5)
        mock_custom_tier.business.billing_subscription.is_active = True

        with patch('smoothschedule.billing.models.TenantCustomTier.objects.select_related') as mock_qs:
            mock_qs.return_value.all.return_value = [mock_custom_tier]

            # Act
            result = check_subscription_grace_periods()

        # Assert
        assert mock_custom_tier.grace_period_started_at is None
        mock_custom_tier.save.assert_called_once()
        assert result['grace_periods_cleared'] == 1
        assert result['grace_periods_started'] == 0
        assert result['custom_tiers_deleted'] == 0

    def test_task_starts_grace_period_when_subscription_becomes_inactive(self):
        """When subscription becomes inactive, grace_period_started_at should be set."""
        # Arrange
        mock_custom_tier = Mock(spec=TenantCustomTier)
        mock_custom_tier.grace_period_started_at = None
        mock_custom_tier.business.billing_subscription.is_active = False

        with patch('smoothschedule.billing.models.TenantCustomTier.objects.select_related') as mock_qs:
            mock_qs.return_value.all.return_value = [mock_custom_tier]

            with patch('smoothschedule.billing.tasks.timezone.now') as mock_now:
                now = timezone.now()
                mock_now.return_value = now

                # Act
                result = check_subscription_grace_periods()

        # Assert
        assert mock_custom_tier.grace_period_started_at == now
        mock_custom_tier.save.assert_called_once()
        assert result['grace_periods_cleared'] == 0
        assert result['grace_periods_started'] == 1
        assert result['custom_tiers_deleted'] == 0

    def test_task_deletes_custom_tier_when_grace_period_expired(self):
        """When grace period exceeds 30 days, custom tier should be deleted."""
        # Arrange
        mock_custom_tier = Mock(spec=TenantCustomTier)
        mock_custom_tier.id = 123
        mock_custom_tier.business.name = "Test Business"
        mock_custom_tier.grace_period_started_at = timezone.now() - timedelta(days=31)
        mock_custom_tier.business.billing_subscription.is_active = False

        with patch('smoothschedule.billing.models.TenantCustomTier.objects.select_related') as mock_qs:
            mock_qs.return_value.all.return_value = [mock_custom_tier]

            # Act
            result = check_subscription_grace_periods()

        # Assert
        mock_custom_tier.delete.assert_called_once()
        assert result['grace_periods_cleared'] == 0
        assert result['grace_periods_started'] == 0
        assert result['custom_tiers_deleted'] == 1

    def test_task_no_action_when_subscription_active_and_no_grace_period(self):
        """When subscription is active and no grace period is set, no action needed."""
        # Arrange
        mock_custom_tier = Mock(spec=TenantCustomTier)
        mock_custom_tier.grace_period_started_at = None
        mock_custom_tier.business.billing_subscription.is_active = True

        with patch('smoothschedule.billing.models.TenantCustomTier.objects.select_related') as mock_qs:
            mock_qs.return_value.all.return_value = [mock_custom_tier]

            # Act
            result = check_subscription_grace_periods()

        # Assert
        mock_custom_tier.save.assert_not_called()
        mock_custom_tier.delete.assert_not_called()
        assert result['grace_periods_cleared'] == 0
        assert result['grace_periods_started'] == 0
        assert result['custom_tiers_deleted'] == 0

    def test_task_no_action_when_subscription_inactive_with_grace_period_not_expired(self):
        """When subscription is inactive but grace period not expired, no action needed."""
        # Arrange
        mock_custom_tier = Mock(spec=TenantCustomTier)
        mock_custom_tier.grace_period_started_at = timezone.now() - timedelta(days=15)
        mock_custom_tier.business.billing_subscription.is_active = False

        with patch('smoothschedule.billing.models.TenantCustomTier.objects.select_related') as mock_qs:
            mock_qs.return_value.all.return_value = [mock_custom_tier]

            # Act
            result = check_subscription_grace_periods()

        # Assert
        mock_custom_tier.save.assert_not_called()
        mock_custom_tier.delete.assert_not_called()
        assert result['grace_periods_cleared'] == 0
        assert result['grace_periods_started'] == 0
        assert result['custom_tiers_deleted'] == 0

    def test_task_handles_multiple_custom_tiers_with_different_states(self):
        """Task should handle multiple custom tiers with different states correctly."""
        # Arrange
        now = timezone.now()

        # Custom tier 1: Active subscription with grace period (should clear)
        ct1 = Mock(spec=TenantCustomTier)
        ct1.grace_period_started_at = now - timedelta(days=5)
        ct1.business.billing_subscription.is_active = True

        # Custom tier 2: Inactive subscription without grace period (should start)
        ct2 = Mock(spec=TenantCustomTier)
        ct2.grace_period_started_at = None
        ct2.business.billing_subscription.is_active = False

        # Custom tier 3: Expired grace period (should delete)
        ct3 = Mock(spec=TenantCustomTier)
        ct3.id = 456
        ct3.business.name = "Expired Business"
        ct3.grace_period_started_at = now - timedelta(days=35)
        ct3.business.billing_subscription.is_active = False

        with patch('smoothschedule.billing.models.TenantCustomTier.objects.select_related') as mock_qs:
            mock_qs.return_value.all.return_value = [ct1, ct2, ct3]

            with patch('smoothschedule.billing.tasks.timezone.now') as mock_now:
                mock_now.return_value = now

                # Act
                result = check_subscription_grace_periods()

        # Assert
        assert result['custom_tiers_processed'] == 3
        assert result['grace_periods_cleared'] == 1
        assert result['grace_periods_started'] == 1
        assert result['custom_tiers_deleted'] == 1

    def test_task_handles_custom_tier_without_subscription(self):
        """Task should handle custom tiers where billing_subscription is None."""
        # Arrange
        mock_custom_tier = Mock(spec=TenantCustomTier)
        mock_custom_tier.grace_period_started_at = None
        mock_custom_tier.business.billing_subscription = None

        with patch('smoothschedule.billing.models.TenantCustomTier.objects.select_related') as mock_qs:
            mock_qs.return_value.all.return_value = [mock_custom_tier]

            with patch('smoothschedule.billing.tasks.timezone.now') as mock_now:
                now = timezone.now()
                mock_now.return_value = now

                # Act
                result = check_subscription_grace_periods()

        # Assert - should start grace period since no subscription
        assert mock_custom_tier.grace_period_started_at == now
        mock_custom_tier.save.assert_called_once()
        assert result['grace_periods_started'] == 1

    def test_task_handles_errors_gracefully(self):
        """Task should log errors but continue processing other custom tiers."""
        # Arrange
        ct1 = Mock(spec=TenantCustomTier)
        ct1.grace_period_started_at = None
        ct1.business.billing_subscription.is_active = False
        ct1.save.side_effect = Exception("Database error")

        ct2 = Mock(spec=TenantCustomTier)
        ct2.grace_period_started_at = None
        ct2.business.billing_subscription.is_active = False

        with patch('smoothschedule.billing.models.TenantCustomTier.objects.select_related') as mock_qs:
            mock_qs.return_value.all.return_value = [ct1, ct2]

            with patch('smoothschedule.billing.tasks.timezone.now') as mock_now:
                now = timezone.now()
                mock_now.return_value = now

                # Act
                result = check_subscription_grace_periods()

        # Assert - ct1 should have error, ct2 should succeed
        assert result['custom_tiers_processed'] == 2
        assert result['grace_periods_started'] == 1  # Only ct2 succeeded
        assert len(result['errors']) == 1
        assert "Database error" in result['errors'][0]
