"""
Unit tests for platform/admin/tasks.py

Tests Celery tasks for platform admin operations.
"""

from datetime import timedelta
from unittest.mock import Mock, patch, MagicMock

import pytest
from django.utils import timezone

from smoothschedule.platform.admin.tasks import (
    get_base_url,
    send_tenant_invitation_email,
    send_appointment_reminder_email,
    send_bulk_appointment_reminders,
    sync_subscription_plan_to_tenants,
)


class TestGetBaseUrl:
    """Tests for get_base_url helper function."""

    def test_returns_default_url(self):
        """Should return default URL when setting not configured."""
        result = get_base_url()
        assert 'platform' in result or 'lvh.me' in result

    @patch('smoothschedule.platform.admin.tasks.settings')
    def test_returns_configured_url(self, mock_settings):
        """Should return configured URL from settings."""
        mock_settings.PLATFORM_BASE_URL = 'https://custom.example.com'

        result = get_base_url()

        assert result == 'https://custom.example.com'


class TestSendTenantInvitationEmailTask:
    """Tests for send_tenant_invitation_email Celery task."""

    @patch('smoothschedule.platform.admin.models.TenantInvitation')
    def test_returns_error_when_invitation_not_found(self, MockInvitation):
        """Should return error when invitation doesn't exist."""
        # Set up the DoesNotExist exception
        class DoesNotExist(Exception):
            pass
        MockInvitation.DoesNotExist = DoesNotExist
        MockInvitation.objects.select_related.return_value.get.side_effect = DoesNotExist()

        # Call task with bound mock
        mock_self = Mock()
        mock_self.request.retries = 0
        result = send_tenant_invitation_email.run(999)

        assert result['success'] is False
        assert 'not found' in result['error']

    @patch('smoothschedule.platform.admin.models.TenantInvitation')
    def test_skips_non_pending_invitation(self, MockInvitation):
        """Should skip invitation that's not in PENDING status."""
        MockInvitation.Status.PENDING = 'PENDING'

        mock_invitation = Mock()
        mock_invitation.status = 'ACCEPTED'  # Not PENDING
        MockInvitation.objects.select_related.return_value.get.return_value = mock_invitation

        result = send_tenant_invitation_email.run(1)

        assert result['success'] is False
        assert 'status is' in result['error']

    @patch('smoothschedule.platform.admin.models.TenantInvitation')
    def test_skips_expired_invitation(self, MockInvitation):
        """Should skip expired invitation."""
        MockInvitation.Status.PENDING = 'PENDING'

        mock_invitation = Mock()
        mock_invitation.status = 'PENDING'
        mock_invitation.is_valid.return_value = False  # Expired
        MockInvitation.objects.select_related.return_value.get.return_value = mock_invitation

        result = send_tenant_invitation_email.run(1)

        assert result['success'] is False
        assert 'expired' in result['error']

    @patch('smoothschedule.platform.admin.tasks.EmailMultiAlternatives')
    @patch('smoothschedule.platform.admin.tasks.render_to_string')
    @patch('smoothschedule.platform.admin.tasks.get_base_url')
    @patch('smoothschedule.platform.admin.models.TenantInvitation')
    def test_sends_email_successfully(self, MockInvitation, mock_get_url, mock_render, mock_email_class):
        """Should send email for valid invitation."""
        MockInvitation.Status.PENDING = 'PENDING'
        mock_get_url.return_value = 'https://platform.example.com'
        mock_render.return_value = '<html>Test</html>'

        mock_email = Mock()
        mock_email_class.return_value = mock_email

        mock_invitation = Mock()
        mock_invitation.status = 'PENDING'
        mock_invitation.is_valid.return_value = True
        mock_invitation.email = 'test@example.com'
        mock_invitation.token = 'test-token'
        mock_invitation.invited_by = Mock()
        mock_invitation.invited_by.get_full_name.return_value = 'John Doe'
        mock_invitation.invited_by.email = 'john@example.com'
        mock_invitation.suggested_business_name = 'Test Business'
        mock_invitation.personal_message = 'Welcome!'
        mock_invitation.expires_at = timezone.now() + timedelta(days=7)
        MockInvitation.objects.select_related.return_value.get.return_value = mock_invitation

        result = send_tenant_invitation_email.run(1)

        assert result['success'] is True
        assert result['email'] == 'test@example.com'
        mock_email.send.assert_called_once()


class TestSendAppointmentReminderEmailTask:
    """Tests for send_appointment_reminder_email Celery task."""

    @patch('smoothschedule.scheduling.schedule.models.Event')
    def test_returns_error_when_event_not_found(self, MockEvent):
        """Should return error when event doesn't exist."""
        class DoesNotExist(Exception):
            pass
        MockEvent.DoesNotExist = DoesNotExist
        MockEvent.objects.select_related.return_value.prefetch_related.return_value.get.side_effect = DoesNotExist()

        result = send_appointment_reminder_email.run(999, 'test@example.com')

        assert result['success'] is False
        assert 'not found' in result['error']

    @patch('smoothschedule.scheduling.schedule.models.Event')
    def test_skips_cancelled_event(self, MockEvent):
        """Should skip cancelled event."""
        MockEvent.Status.CANCELED = 'CANCELED'

        mock_event = Mock()
        mock_event.status = 'CANCELED'
        MockEvent.objects.select_related.return_value.prefetch_related.return_value.get.return_value = mock_event

        result = send_appointment_reminder_email.run(1, 'test@example.com')

        assert result['success'] is False
        assert 'cancelled' in result['error']

    @patch('django.db.connection')
    @patch('smoothschedule.platform.admin.tasks.EmailMultiAlternatives')
    @patch('smoothschedule.platform.admin.tasks.render_to_string')
    @patch('smoothschedule.scheduling.schedule.models.Event')
    def test_sends_reminder_successfully(self, MockEvent, mock_render, mock_email_class, mock_conn):
        """Should send reminder email for valid event."""
        MockEvent.Status.CANCELED = 'CANCELED'
        mock_render.return_value = '<html>Reminder</html>'
        mock_email = Mock()
        mock_email_class.return_value = mock_email
        mock_conn.tenant = Mock(name='Test Business')

        mock_event = Mock()
        mock_event.status = 'SCHEDULED'  # Not CANCELED
        mock_event.start_time = timezone.now() + timedelta(hours=24)
        mock_event.duration = timedelta(hours=1)
        mock_event.participants.filter.return_value = []
        MockEvent.objects.select_related.return_value.prefetch_related.return_value.get.return_value = mock_event

        result = send_appointment_reminder_email.run(1, 'customer@example.com', 24)

        assert result['success'] is True
        assert result['email'] == 'customer@example.com'
        mock_email.send.assert_called_once()


class TestSendBulkAppointmentRemindersTask:
    """Tests for send_bulk_appointment_reminders Celery task."""

    @patch('smoothschedule.platform.admin.tasks.send_appointment_reminder_email')
    @patch('smoothschedule.scheduling.schedule.models.Event')
    def test_queues_reminders_for_matching_events(self, MockEvent, mock_send_task):
        """Should queue reminder emails for events in time window."""
        MockEvent.Status.SCHEDULED = 'SCHEDULED'
        mock_send_task.delay = Mock()

        mock_participant = Mock()
        mock_participant.customer = Mock(email='test@example.com')

        mock_event = Mock(id=1)
        mock_event.participants.all.return_value = [mock_participant]

        MockEvent.objects.filter.return_value.prefetch_related.return_value = [mock_event]

        result = send_bulk_appointment_reminders(hours_before=24)

        assert result['reminders_queued'] == 1
        mock_send_task.delay.assert_called_once()

    @patch('smoothschedule.platform.admin.tasks.send_appointment_reminder_email')
    @patch('smoothschedule.scheduling.schedule.models.Event')
    def test_skips_participants_without_email(self, MockEvent, mock_send_task):
        """Should skip participants without email."""
        MockEvent.Status.SCHEDULED = 'SCHEDULED'
        mock_send_task.delay = Mock()

        mock_participant = Mock()
        mock_participant.customer = Mock(email='')  # Empty email

        mock_event = Mock(id=1)
        mock_event.participants.all.return_value = [mock_participant]

        MockEvent.objects.filter.return_value.prefetch_related.return_value = [mock_event]

        result = send_bulk_appointment_reminders(hours_before=24)

        assert result['reminders_queued'] == 0
        mock_send_task.delay.assert_not_called()

    @patch('smoothschedule.scheduling.schedule.models.Event')
    def test_returns_zero_when_no_events(self, MockEvent):
        """Should return zero when no events in window."""
        MockEvent.Status.SCHEDULED = 'SCHEDULED'
        MockEvent.objects.filter.return_value.prefetch_related.return_value = []

        result = send_bulk_appointment_reminders(hours_before=24)

        assert result['reminders_queued'] == 0


class TestSyncSubscriptionPlanToTenantsTask:
    """Tests for sync_subscription_plan_to_tenants Celery task."""

    @patch('smoothschedule.identity.core.models.Tenant')
    @patch('smoothschedule.platform.admin.models.SubscriptionPlan')
    def test_returns_error_when_plan_not_found(self, MockPlan, MockTenant):
        """Should return error when plan doesn't exist."""
        class DoesNotExist(Exception):
            pass
        MockPlan.DoesNotExist = DoesNotExist
        MockPlan.objects.get.side_effect = DoesNotExist()

        result = sync_subscription_plan_to_tenants.run(999)

        assert result['success'] is False
        assert 'not found' in result['error']

    @patch('smoothschedule.identity.core.models.Tenant')
    @patch('smoothschedule.platform.admin.models.SubscriptionPlan')
    def test_returns_success_when_no_tenants(self, MockPlan, MockTenant):
        """Should return success with zero tenants updated."""
        mock_plan = Mock(id=1, name='Test Plan')
        MockPlan.objects.get.return_value = mock_plan

        mock_qs = Mock()
        mock_qs.count.return_value = 0
        MockTenant.objects.filter.return_value = mock_qs

        result = sync_subscription_plan_to_tenants.run(1)

        assert result['success'] is True
        assert result['tenants_updated'] == 0

    @patch('smoothschedule.identity.core.models.Tenant')
    @patch('smoothschedule.platform.admin.models.SubscriptionPlan')
    def test_updates_tenant_permissions(self, MockPlan, MockTenant):
        """Should update tenant permissions from plan."""
        mock_plan = Mock(
            id=1,
            name='Professional',
            permissions={'can_use_sms_reminders': True},
            limits={'max_users': 50}
        )
        MockPlan.objects.get.return_value = mock_plan

        mock_tenant = Mock()
        mock_tenant.id = 1
        mock_tenant.name = 'Test Tenant'
        mock_tenant.can_use_sms_reminders = False
        mock_tenant.max_users = 10
        mock_tenant.subscription_tier = 'FREE'

        mock_qs = Mock()
        mock_qs.count.return_value = 1
        mock_qs.__iter__ = Mock(return_value=iter([mock_tenant]))
        MockTenant.objects.filter.return_value = mock_qs

        result = sync_subscription_plan_to_tenants.run(1)

        assert result['success'] is True
        assert result['tenants_updated'] == 1
        mock_tenant.save.assert_called_once()

    @patch('smoothschedule.identity.core.models.Tenant')
    @patch('smoothschedule.platform.admin.models.SubscriptionPlan')
    def test_skips_unchanged_tenants(self, MockPlan, MockTenant):
        """Should skip tenants that don't need updates."""
        mock_plan = Mock(
            id=1,
            name='Unknown',  # Not in tier mapping
            permissions={},
            limits={}
        )
        MockPlan.objects.get.return_value = mock_plan

        mock_tenant = Mock()
        mock_tenant.id = 1
        mock_tenant.subscription_tier = 'FREE'

        mock_qs = Mock()
        mock_qs.count.return_value = 1
        mock_qs.__iter__ = Mock(return_value=iter([mock_tenant]))
        MockTenant.objects.filter.return_value = mock_qs

        result = sync_subscription_plan_to_tenants.run(1)

        assert result['tenants_updated'] == 0
        mock_tenant.save.assert_not_called()

    @patch('smoothschedule.identity.core.models.Tenant')
    @patch('smoothschedule.platform.admin.models.SubscriptionPlan')
    def test_handles_tenant_update_error(self, MockPlan, MockTenant):
        """Should handle errors when updating individual tenant."""
        mock_plan = Mock(
            id=1,
            name='Professional',
            permissions={'can_use_automations': True},
            limits={}
        )
        MockPlan.objects.get.return_value = mock_plan

        mock_tenant = Mock()
        mock_tenant.id = 1
        mock_tenant.can_use_automations = False
        mock_tenant.subscription_tier = 'FREE'
        mock_tenant.save.side_effect = Exception("DB error")

        mock_qs = Mock()
        mock_qs.count.return_value = 1
        mock_qs.__iter__ = Mock(return_value=iter([mock_tenant]))
        MockTenant.objects.filter.return_value = mock_qs

        result = sync_subscription_plan_to_tenants.run(1)

        assert 'errors' in result
        assert len(result['errors']) == 1
