"""Tests for LinkedIn manual sync GraphQL mutations (US4 - Manual Sync).

Tests cover:
- syncLinkedInPage mutation triggers sync
- syncLinkedInPage mutation returns updated page
- syncLinkedInPage mutation rate limits rapid requests
- updateLinkedInSyncConfig mutation updates interval
- pauseLinkedInPage mutation sets status to PAUSED
- resumeLinkedInPage mutation sets status to ACTIVE
"""

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

import pytest
from django.core.cache import cache
from django.utils import timezone

from accounts.tests.factories import UserFactory, WorkspaceFactory, WorkspaceMembershipFactory
from integrations.models import LinkedInSyncStatus
from integrations.tests.factories import LinkedInPageFactory


@pytest.fixture
def user():
    """Create a test user."""
    return UserFactory()


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


@pytest.fixture
def admin_membership(user, workspace):
    """Create admin membership for user in workspace."""
    return WorkspaceMembershipFactory(user=user, workspace=workspace, role="admin")


@pytest.fixture
def linkedin_page(workspace):
    """Create a connected LinkedIn page."""
    return LinkedInPageFactory(
        workspace=workspace,
        sync_status=LinkedInSyncStatus.ACTIVE,
        last_sync_at=timezone.now() - timedelta(minutes=30),
    )


@pytest.fixture
def context(user):
    """Create a mock GraphQL context with authenticated user."""
    mock_context = MagicMock()
    mock_context.user = user
    return mock_context


class MockInfo:
    """Mock info object for GraphQL mutations."""

    def __init__(self, context):
        self.context = context


@pytest.mark.django_db
class TestSyncLinkedInPageMutation:
    """Tests for syncLinkedInPage mutation."""

    @patch("integrations.graphql.django_rq")
    def test_sync_page_triggers_sync_task(self, mock_django_rq, context, workspace, admin_membership, linkedin_page):
        """Test that mutation triggers sync task."""
        from strawberry import ID

        from integrations.graphql import IntegrationsMutation

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

        mutation = IntegrationsMutation()
        result = mutation.sync_linkedin_page(
            MockInfo(context),
            page_id=ID(str(linkedin_page.pk)),
        )

        assert result.success is True
        mock_queue.enqueue.assert_called_once()

    @patch("integrations.graphql.django_rq")
    def test_sync_page_returns_updated_page(self, mock_django_rq, context, workspace, admin_membership, linkedin_page):
        """Test that mutation returns updated page with SYNCING status."""
        from strawberry import ID

        from integrations.graphql import IntegrationsMutation

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

        mutation = IntegrationsMutation()
        result = mutation.sync_linkedin_page(
            MockInfo(context),
            page_id=ID(str(linkedin_page.pk)),
        )

        assert result.success is True
        assert result.page is not None
        # Status should be set to SYNCING
        linkedin_page.refresh_from_db()
        assert linkedin_page.sync_status == LinkedInSyncStatus.SYNCING

    @patch("integrations.graphql.django_rq")
    def test_sync_page_rate_limits_rapid_requests(
        self, mock_django_rq, context, workspace, admin_membership, linkedin_page
    ):
        """Test that mutation rate limits rapid sync requests."""
        from strawberry import ID

        from integrations.graphql import IntegrationsMutation

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

        mutation = IntegrationsMutation()
        page_id = ID(str(linkedin_page.pk))

        # First call should succeed
        result1 = mutation.sync_linkedin_page(MockInfo(context), page_id=page_id)
        assert result1.success is True

        # Second call within rate limit window should fail
        result2 = mutation.sync_linkedin_page(MockInfo(context), page_id=page_id)
        assert result2.success is False
        assert "rate limit" in result2.error.lower() or "recently" in result2.error.lower()

    @patch("integrations.graphql.django_rq")
    def test_sync_page_rejects_nonexistent_page(self, mock_django_rq, context, workspace, admin_membership):
        """Test that mutation rejects non-existent page."""
        from strawberry import ID

        from integrations.graphql import IntegrationsMutation

        mutation = IntegrationsMutation()
        result = mutation.sync_linkedin_page(
            MockInfo(context),
            page_id=ID("999999"),
        )

        assert result.success is False
        assert "not found" in result.error.lower()

    @patch("integrations.graphql.django_rq")
    def test_sync_page_rejects_unauthorized_user(self, mock_django_rq, context, workspace, linkedin_page):
        """Test that mutation rejects users without admin access."""
        from strawberry import ID

        from integrations.graphql import IntegrationsMutation

        # No membership created for the user
        mutation = IntegrationsMutation()

        # Should raise permission exception
        with pytest.raises(Exception, match="Permission denied"):
            mutation.sync_linkedin_page(
                MockInfo(context),
                page_id=ID(str(linkedin_page.pk)),
            )

    def setup_method(self):
        """Clear cache before each test."""
        cache.clear()


@pytest.mark.django_db
class TestUpdateLinkedInSyncConfigMutation:
    """Tests for updateLinkedInSyncConfig mutation."""

    def test_updates_sync_interval(self, context, workspace, admin_membership, linkedin_page):
        """Test that mutation updates sync interval."""
        from strawberry import ID

        from integrations.graphql import IntegrationsMutation

        mutation = IntegrationsMutation()
        result = mutation.update_linkedin_sync_config(
            MockInfo(context),
            page_id=ID(str(linkedin_page.pk)),
            sync_interval_minutes=120,
        )

        assert result.success is True
        linkedin_page.refresh_from_db()
        assert linkedin_page.sync_interval_minutes == 120

    def test_validates_minimum_interval(self, context, workspace, admin_membership, linkedin_page):
        """Test that mutation validates minimum sync interval."""
        from strawberry import ID

        from integrations.graphql import IntegrationsMutation

        mutation = IntegrationsMutation()
        result = mutation.update_linkedin_sync_config(
            MockInfo(context),
            page_id=ID(str(linkedin_page.pk)),
            sync_interval_minutes=5,  # Too low
        )

        assert result.success is False
        assert "minimum" in result.error.lower() or "interval" in result.error.lower()


@pytest.mark.django_db
class TestPauseLinkedInPageMutation:
    """Tests for pauseLinkedInPage mutation."""

    def test_pauses_active_page(self, context, workspace, admin_membership, linkedin_page):
        """Test that mutation pauses an active page."""
        from strawberry import ID

        from integrations.graphql import IntegrationsMutation

        mutation = IntegrationsMutation()
        result = mutation.pause_linkedin_page(
            MockInfo(context),
            page_id=ID(str(linkedin_page.pk)),
        )

        assert result.success is True
        linkedin_page.refresh_from_db()
        assert linkedin_page.sync_status == LinkedInSyncStatus.PAUSED

    def test_pause_preserves_last_sync_time(self, context, workspace, admin_membership, linkedin_page):
        """Test that pausing preserves the last sync time."""
        from strawberry import ID

        from integrations.graphql import IntegrationsMutation

        original_last_sync = linkedin_page.last_sync_at

        mutation = IntegrationsMutation()
        mutation.pause_linkedin_page(
            MockInfo(context),
            page_id=ID(str(linkedin_page.pk)),
        )

        linkedin_page.refresh_from_db()
        assert linkedin_page.last_sync_at == original_last_sync


@pytest.mark.django_db
class TestResumeLinkedInPageMutation:
    """Tests for resumeLinkedInPage mutation."""

    def test_resumes_paused_page(self, context, workspace, admin_membership):
        """Test that mutation resumes a paused page."""
        from strawberry import ID

        from integrations.graphql import IntegrationsMutation

        paused_page = LinkedInPageFactory(
            workspace=workspace,
            sync_status=LinkedInSyncStatus.PAUSED,
            organization_urn="urn:li:organization:55555",
        )

        mutation = IntegrationsMutation()
        result = mutation.resume_linkedin_page(
            MockInfo(context),
            page_id=ID(str(paused_page.pk)),
        )

        assert result.success is True
        paused_page.refresh_from_db()
        assert paused_page.sync_status == LinkedInSyncStatus.ACTIVE

    def test_resume_clears_sync_error(self, context, workspace, admin_membership):
        """Test that resuming clears any previous sync error."""
        from strawberry import ID

        from integrations.graphql import IntegrationsMutation

        error_page = LinkedInPageFactory(
            workspace=workspace,
            sync_status=LinkedInSyncStatus.ERROR,
            sync_error="Previous error message",
            organization_urn="urn:li:organization:66666",
        )

        mutation = IntegrationsMutation()
        mutation.resume_linkedin_page(
            MockInfo(context),
            page_id=ID(str(error_page.pk)),
        )

        error_page.refresh_from_db()
        assert error_page.sync_status == LinkedInSyncStatus.ACTIVE
        assert error_page.sync_error == ""
