"""Tests for LinkedIn integration models.

Tests cover:
- LinkedInPage model creation and validation
- Token encryption/decryption
- Sync status enum values
- Unique constraints
- Soft delete functionality
"""

import pytest
from django.core.exceptions import ValidationError
from django.db import IntegrityError
from django.utils import timezone

from accounts.tests.factories import WorkspaceFactory
from integrations.models import LinkedInPage, LinkedInSyncStatus
from integrations.tests.factories import LinkedInPageFactory
from sources.tests.factories import SourceFactory


@pytest.mark.django_db
class TestLinkedInSyncStatus:
    """Tests for LinkedInSyncStatus enum."""

    def test_status_values(self):
        """Verify all expected status values exist."""
        expected_values = ["PENDING", "ACTIVE", "SYNCING", "ERROR", "PAUSED", "EXPIRED"]
        actual_values = [status.value for status in LinkedInSyncStatus]
        assert set(actual_values) == set(expected_values)

    def test_status_choices(self):
        """Verify choices are properly formatted for Django."""
        choices = LinkedInSyncStatus.choices
        assert len(choices) == 6
        # Each choice should be a tuple of (value, label)
        for value, label in choices:
            assert isinstance(value, str)
            assert isinstance(label, str)


@pytest.mark.django_db
class TestLinkedInPageModel:
    """Tests for LinkedInPage model."""

    def test_create_linkedin_page_with_valid_data(self):
        """Test creating a LinkedInPage with valid data."""
        page = LinkedInPageFactory()

        assert page.pk is not None
        assert page.organization_urn.startswith("urn:li:organization:")
        assert page.organization_id.isdigit()
        assert page.name
        assert page.workspace is not None
        assert page.source is not None

    def test_organization_id_extracted_from_urn(self):
        """Test that organization_id is extracted from URN on save."""
        workspace = WorkspaceFactory()
        source = SourceFactory(workspace=workspace, kind="linkedin_page")

        page = LinkedInPage(
            workspace=workspace,
            source=source,
            organization_urn="urn:li:organization:98765",
            organization_id="",  # Empty - should be filled from URN
            name="Test Org",
        )
        page.save()

        assert page.organization_id == "98765"

    def test_linkedin_url_with_vanity_name(self):
        """Test linkedin_url property when vanity_name is set."""
        page = LinkedInPageFactory(vanity_name="acme-corp")
        assert page.linkedin_url == "https://www.linkedin.com/company/acme-corp"

    def test_linkedin_url_without_vanity_name(self):
        """Test linkedin_url property when vanity_name is empty."""
        page = LinkedInPageFactory(vanity_name="", organization_id="12345")
        assert page.linkedin_url == "https://www.linkedin.com/company/12345"

    def test_str_representation(self):
        """Test string representation of LinkedInPage."""
        page = LinkedInPageFactory(name="Acme Corp", organization_id="12345")
        assert str(page) == "Acme Corp (12345)"


@pytest.mark.django_db
class TestLinkedInPageTokenEncryption:
    """Tests for token encryption/decryption in LinkedInPage."""

    def test_access_token_encryption_round_trip(self):
        """Test that access token is encrypted and can be decrypted."""
        page = LinkedInPageFactory()
        original_token = "test_access_token_abc123"

        # Set token via property (which encrypts)
        page.access_token = original_token
        page.save()

        # Reload from database
        page.refresh_from_db()

        # The raw field should be encrypted (not equal to original)
        assert page._access_token != original_token
        assert page._access_token != ""

        # The property should return decrypted value
        assert page.access_token == original_token

    def test_refresh_token_encryption_round_trip(self):
        """Test that refresh token is encrypted and can be decrypted."""
        page = LinkedInPageFactory()
        original_token = "test_refresh_token_xyz789"

        page.refresh_token = original_token
        page.save()

        page.refresh_from_db()

        assert page._refresh_token != original_token
        assert page._refresh_token != ""
        assert page.refresh_token == original_token

    def test_empty_access_token(self):
        """Test handling of empty access token."""
        page = LinkedInPageFactory()
        page.access_token = ""
        page.save()

        page.refresh_from_db()
        assert page.access_token == ""
        assert page._access_token == ""

    def test_empty_refresh_token(self):
        """Test handling of empty refresh token."""
        page = LinkedInPageFactory()
        page.refresh_token = ""
        page.save()

        page.refresh_from_db()
        assert page.refresh_token == ""
        assert page._refresh_token == ""

    def test_invalid_encrypted_token_returns_empty(self):
        """Test that invalid encrypted data returns empty string."""
        page = LinkedInPageFactory()

        # Directly set invalid encrypted data
        page._access_token = "invalid_not_encrypted_data"
        page.save()

        page.refresh_from_db()
        # Should return empty string rather than raising
        assert page.access_token == ""


@pytest.mark.django_db
class TestLinkedInPageTokenExpiry:
    """Tests for token expiration checking."""

    def test_is_token_expired_when_no_expiry_set(self):
        """Test that token is considered expired when expiry is not set."""
        page = LinkedInPageFactory(token_expires_at=None)
        assert page.is_token_expired is True

    def test_is_token_expired_when_expired(self):
        """Test that token is expired when past expiry date."""
        past_time = timezone.now() - timezone.timedelta(hours=1)
        page = LinkedInPageFactory(token_expires_at=past_time)
        assert page.is_token_expired is True

    def test_is_token_expired_when_not_expired(self):
        """Test that token is not expired when before expiry date."""
        future_time = timezone.now() + timezone.timedelta(hours=1)
        page = LinkedInPageFactory(token_expires_at=future_time)
        assert page.is_token_expired is False

    def test_is_token_expired_at_exact_boundary(self):
        """Test token expiry at the exact boundary time."""
        # At exactly the expiry time, it should be considered expired
        page = LinkedInPageFactory(token_expires_at=timezone.now())
        assert page.is_token_expired is True


@pytest.mark.django_db
class TestLinkedInPageUniqueConstraints:
    """Tests for unique constraints on LinkedInPage."""

    def test_unique_organization_per_workspace(self):
        """Test that same organization can't be connected twice to same workspace."""
        workspace = WorkspaceFactory()
        LinkedInPageFactory(
            workspace=workspace,
            organization_urn="urn:li:organization:12345",
        )

        # Attempting to create another page with same org in same workspace should fail
        with pytest.raises(IntegrityError):
            LinkedInPageFactory(
                workspace=workspace,
                organization_urn="urn:li:organization:12345",
            )

    def test_same_organization_different_workspaces(self):
        """Test that same organization can be connected to different workspaces."""
        workspace1 = WorkspaceFactory()
        workspace2 = WorkspaceFactory()

        page1 = LinkedInPageFactory(
            workspace=workspace1,
            organization_urn="urn:li:organization:12345",
        )
        page2 = LinkedInPageFactory(
            workspace=workspace2,
            organization_urn="urn:li:organization:12345",
        )

        assert page1.pk != page2.pk
        assert page1.organization_urn == page2.organization_urn

    def test_archived_page_allows_reconnection(self):
        """Test that archived page allows same org to be reconnected."""
        workspace = WorkspaceFactory()

        page1 = LinkedInPageFactory(
            workspace=workspace,
            organization_urn="urn:li:organization:12345",
            is_archived=True,
        )

        # Should be able to create new active page for same org
        page2 = LinkedInPageFactory(
            workspace=workspace,
            organization_urn="urn:li:organization:12345",
            is_archived=False,
        )

        assert page1.pk != page2.pk


@pytest.mark.django_db
class TestLinkedInPageValidation:
    """Tests for LinkedInPage validation."""

    def test_valid_organization_urn_format(self):
        """Test that valid URN format passes validation."""
        page = LinkedInPageFactory(organization_urn="urn:li:organization:12345")
        page.full_clean()  # Should not raise

    def test_invalid_organization_urn_format(self):
        """Test that invalid URN format fails validation."""
        page = LinkedInPageFactory.build(organization_urn="invalid-urn-format")

        with pytest.raises(ValidationError) as exc_info:
            page.full_clean()

        assert "organization_urn" in exc_info.value.message_dict


@pytest.mark.django_db
class TestLinkedInPageSoftDelete:
    """Tests for soft delete functionality."""

    def test_is_archived_default_false(self):
        """Test that is_archived defaults to False."""
        page = LinkedInPageFactory()
        assert page.is_archived is False

    def test_archived_page_excluded_from_default_queries(self):
        """Test that archived pages can be excluded via filter."""
        workspace = WorkspaceFactory()
        active_page = LinkedInPageFactory(
            workspace=workspace,
            is_archived=False,
        )
        archived_page = LinkedInPageFactory(
            workspace=workspace,
            is_archived=True,
            organization_urn="urn:li:organization:99999",
        )

        active_pages = LinkedInPage.objects.filter(
            workspace=workspace,
            is_archived=False,
        )

        assert active_page in active_pages
        assert archived_page not in active_pages

    def test_archived_page_still_exists(self):
        """Test that archived pages are not actually deleted."""
        page = LinkedInPageFactory(is_archived=False)
        page.is_archived = True
        page.save()

        page.refresh_from_db()
        assert page.is_archived is True
        assert LinkedInPage.objects.filter(pk=page.pk).exists()


@pytest.mark.django_db
class TestLinkedInPageSyncScheduling:
    """Tests for sync scheduling functionality."""

    def test_schedule_next_sync(self):
        """Test that schedule_next_sync sets next_sync_at correctly."""
        page = LinkedInPageFactory(
            sync_interval_minutes=15,
            next_sync_at=None,
        )

        before_schedule = timezone.now()
        page.schedule_next_sync()
        after_schedule = timezone.now()

        assert page.next_sync_at is not None
        # Should be approximately 15 minutes from now
        expected_min = before_schedule + timezone.timedelta(minutes=15)
        expected_max = after_schedule + timezone.timedelta(minutes=15)
        assert expected_min <= page.next_sync_at <= expected_max

    def test_schedule_next_sync_custom_interval(self):
        """Test scheduling with custom interval."""
        page = LinkedInPageFactory(
            sync_interval_minutes=30,
            next_sync_at=None,
        )

        page.schedule_next_sync()

        # Should be approximately 30 minutes from now
        expected = timezone.now() + timezone.timedelta(minutes=30)
        # Allow 1 second tolerance
        assert abs((page.next_sync_at - expected).total_seconds()) < 1


@pytest.mark.django_db
class TestLinkedInPageFactoryTraits:
    """Tests for factory traits."""

    def test_active_trait(self):
        """Test that active trait sets correct values."""
        page = LinkedInPageFactory(active=True)

        assert page.sync_status == LinkedInSyncStatus.ACTIVE
        assert page.last_sync_at is not None
        assert page.next_sync_at is not None

    def test_expired_token_trait(self):
        """Test that expired_token trait sets correct values."""
        page = LinkedInPageFactory(expired_token=True)

        assert page.sync_status == LinkedInSyncStatus.EXPIRED
        assert page.is_token_expired is True

    def test_error_trait(self):
        """Test that error trait sets correct values."""
        page = LinkedInPageFactory(error=True)

        assert page.sync_status == LinkedInSyncStatus.ERROR
        assert page.sync_error != ""

    def test_paused_trait(self):
        """Test that paused trait sets correct values."""
        page = LinkedInPageFactory(paused=True)
        assert page.sync_status == LinkedInSyncStatus.PAUSED

    def test_archived_trait(self):
        """Test that archived trait sets correct values."""
        page = LinkedInPageFactory(archived=True)
        assert page.is_archived is True
