"""Tests for LinkedIn background tasks (US2 - Feed Integration).

Tests cover:
- sync_linkedin_page task calls sync service
- sync_linkedin_page task handles service errors
- sync_all_linkedin_pages finds pages due for sync
- sync_all_linkedin_pages skips paused pages
"""

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

import pytest
from django.utils import timezone

from accounts.tests.factories import WorkspaceFactory
from integrations.models import LinkedInSyncStatus
from integrations.tests.factories import LinkedInPageFactory


@pytest.fixture
def workspace():
    """Create a test workspace."""
    return WorkspaceFactory()


@pytest.fixture
def active_page(workspace):
    """Create an active LinkedIn page due for sync."""
    return LinkedInPageFactory(
        workspace=workspace,
        sync_status=LinkedInSyncStatus.ACTIVE,
        last_sync_at=timezone.now() - timedelta(hours=2),
        sync_interval_minutes=60,  # Due for sync (last sync 2 hours ago)
    )


@pytest.fixture
def paused_page(workspace):
    """Create a paused LinkedIn page."""
    return LinkedInPageFactory(
        workspace=workspace,
        sync_status=LinkedInSyncStatus.PAUSED,
        organization_urn="urn:li:organization:99999",
    )


@pytest.fixture
def recently_synced_page(workspace):
    """Create a recently synced page (not due for sync)."""
    return LinkedInPageFactory(
        workspace=workspace,
        sync_status=LinkedInSyncStatus.ACTIVE,
        last_sync_at=timezone.now() - timedelta(minutes=5),  # Recent
        sync_interval_minutes=60,
        organization_urn="urn:li:organization:88888",
    )


@pytest.mark.django_db
class TestSyncLinkedInPageTask:
    """Tests for sync_linkedin_page task."""

    @patch("integrations.services.linkedin_sync.LinkedInSyncService")
    def test_sync_task_calls_sync_service(self, mock_service_class, active_page):
        """Test that task instantiates and calls sync service."""
        from integrations.tasks import sync_linkedin_page

        mock_service = MagicMock()
        mock_service_class.return_value = mock_service
        mock_service.sync_page.return_value = {"signals_created": 5}

        result = sync_linkedin_page(active_page.pk)

        mock_service_class.assert_called_once()
        mock_service.sync_page.assert_called_once()
        assert result["signals_created"] == 5

    @patch("integrations.services.linkedin_sync.LinkedInSyncService")
    def test_sync_task_handles_service_errors_gracefully(self, mock_service_class, active_page):
        """Test that task handles sync service errors."""
        from integrations.exceptions import LinkedInAPIError
        from integrations.tasks import sync_linkedin_page

        mock_service = MagicMock()
        mock_service_class.return_value = mock_service
        mock_service.sync_page.side_effect = LinkedInAPIError("API Error")

        # Should not raise, but return error info
        result = sync_linkedin_page(active_page.pk)

        assert result.get("error") is not None
        assert "API Error" in result["error"]

    @patch("integrations.services.linkedin_sync.LinkedInSyncService")
    def test_sync_task_handles_nonexistent_page(self, mock_service_class):
        """Test that task handles non-existent page ID."""
        from integrations.tasks import sync_linkedin_page

        result = sync_linkedin_page(999999)  # Non-existent ID

        assert result.get("error") is not None
        mock_service_class.assert_not_called()

    @patch("integrations.services.linkedin_sync.LinkedInSyncService")
    def test_sync_task_skips_archived_page(self, mock_service_class, workspace):
        """Test that task skips archived pages."""
        from integrations.tasks import sync_linkedin_page

        archived_page = LinkedInPageFactory(
            workspace=workspace,
            is_archived=True,
            organization_urn="urn:li:organization:77777",
        )

        result = sync_linkedin_page(archived_page.pk)

        assert result.get("skipped") is True
        mock_service_class.assert_not_called()

    @patch("integrations.services.linkedin_sync.LinkedInSyncService")
    def test_sync_task_passes_full_sync_parameter(self, mock_service_class, active_page):
        """Test that task passes full_sync parameter to sync service."""
        from integrations.tasks import sync_linkedin_page

        mock_service = MagicMock()
        mock_service_class.return_value = mock_service
        mock_service.sync_page.return_value = {"signals_created": 50}

        # Call with full_sync=True
        result = sync_linkedin_page(active_page.pk, full_sync=True)

        mock_service.sync_page.assert_called_once_with(full_sync=True)
        assert result["full_sync"] is True
        assert result["signals_created"] == 50

    @patch("integrations.services.linkedin_sync.LinkedInSyncService")
    def test_sync_task_defaults_to_incremental_sync(self, mock_service_class, active_page):
        """Test that task defaults to incremental sync (full_sync=False)."""
        from integrations.tasks import sync_linkedin_page

        mock_service = MagicMock()
        mock_service_class.return_value = mock_service
        mock_service.sync_page.return_value = {"signals_created": 5}

        # Call without full_sync parameter
        result = sync_linkedin_page(active_page.pk)

        mock_service.sync_page.assert_called_once_with(full_sync=False)
        assert result["full_sync"] is False


@pytest.mark.django_db
class TestSyncAllLinkedInPagesTask:
    """Tests for sync_all_linkedin_pages scheduled task."""

    @patch("integrations.tasks.django_rq")
    def test_finds_pages_due_for_sync(self, mock_django_rq, active_page, recently_synced_page):
        """Test that scheduled task finds pages due for sync."""
        from integrations.tasks import sync_all_linkedin_pages

        mock_queue = MagicMock()
        mock_django_rq.get_queue.return_value = mock_queue

        sync_all_linkedin_pages()

        # Should queue sync for active_page (due) but not recently_synced_page
        call_args = [call.kwargs.get("page_id") for call in mock_queue.enqueue.call_args_list]
        assert active_page.pk in call_args
        assert recently_synced_page.pk not in call_args

    @patch("integrations.tasks.django_rq")
    def test_skips_paused_pages(self, mock_django_rq, active_page, paused_page):
        """Test that scheduled task skips paused pages."""
        from integrations.tasks import sync_all_linkedin_pages

        mock_queue = MagicMock()
        mock_django_rq.get_queue.return_value = mock_queue

        sync_all_linkedin_pages()

        call_args = [call.kwargs.get("page_id") for call in mock_queue.enqueue.call_args_list]
        assert paused_page.pk not in call_args

    @patch("integrations.tasks.django_rq")
    def test_skips_expired_pages(self, mock_django_rq, workspace):
        """Test that scheduled task skips pages with expired tokens."""
        from integrations.tasks import sync_all_linkedin_pages

        expired_page = LinkedInPageFactory(
            workspace=workspace,
            sync_status=LinkedInSyncStatus.EXPIRED,
            organization_urn="urn:li:organization:66666",
        )

        mock_queue = MagicMock()
        mock_django_rq.get_queue.return_value = mock_queue

        sync_all_linkedin_pages()

        call_args = [call.kwargs.get("page_id") for call in mock_queue.enqueue.call_args_list]
        assert expired_page.pk not in call_args

    @patch("integrations.tasks.django_rq")
    def test_skips_archived_pages(self, mock_django_rq, workspace):
        """Test that scheduled task skips archived pages."""
        from integrations.tasks import sync_all_linkedin_pages

        archived_page = LinkedInPageFactory(
            workspace=workspace,
            is_archived=True,
            organization_urn="urn:li:organization:55555",
        )

        mock_queue = MagicMock()
        mock_django_rq.get_queue.return_value = mock_queue

        sync_all_linkedin_pages()

        call_args = [call.kwargs.get("page_id") for call in mock_queue.enqueue.call_args_list]
        assert archived_page.pk not in call_args

    @patch("integrations.tasks.django_rq")
    def test_handles_no_pages_gracefully(self, mock_django_rq):
        """Test that scheduled task handles no pages case."""
        from integrations.tasks import sync_all_linkedin_pages

        mock_queue = MagicMock()
        mock_django_rq.get_queue.return_value = mock_queue

        # Should not raise
        result = sync_all_linkedin_pages()

        assert result["pages_queued"] == 0
        mock_queue.enqueue.assert_not_called()

    @patch("integrations.tasks.django_rq")
    def test_returns_count_of_queued_pages(self, mock_django_rq, active_page, workspace):
        """Test that scheduled task returns count of queued pages."""
        from integrations.tasks import sync_all_linkedin_pages

        # Create another page due for sync
        LinkedInPageFactory(
            workspace=workspace,
            sync_status=LinkedInSyncStatus.ACTIVE,
            last_sync_at=timezone.now() - timedelta(hours=3),
            sync_interval_minutes=60,
            organization_urn="urn:li:organization:44444",
        )

        mock_queue = MagicMock()
        mock_django_rq.get_queue.return_value = mock_queue

        result = sync_all_linkedin_pages()

        assert result["pages_queued"] == 2
