"""
Tests for news/subscription_manager/base_subscription.py

Tests cover:
- BaseSubscription initialization
- Refresh scheduling
- Status management
- Metadata handling
"""

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


class TestBaseSubscriptionInit:
    """Tests for BaseSubscription initialization."""

    def test_base_subscription_is_abstract(self):
        """BaseSubscription cannot be instantiated directly."""
        from local_deep_research.news.subscription_manager.base_subscription import (
            BaseSubscription,
        )
        from abc import ABC

        assert issubclass(BaseSubscription, ABC)

    def test_base_subscription_has_required_abstract_methods(self):
        """BaseSubscription requires abstract methods."""
        from local_deep_research.news.subscription_manager.base_subscription import (
            BaseSubscription,
        )

        # Check that abstract methods are defined
        assert hasattr(BaseSubscription, "generate_search_query")
        assert hasattr(BaseSubscription, "get_subscription_type")


class TestConcreteSubscription:
    """Tests using a concrete implementation of BaseSubscription."""

    @pytest.fixture
    def mock_storage(self):
        """Mock the storage."""
        with patch(
            "local_deep_research.news.subscription_manager.base_subscription.SQLSubscriptionStorage"
        ) as mock:
            mock.return_value = MagicMock()
            yield mock

    @pytest.fixture
    def card_source(self):
        """Create a mock CardSource."""
        from local_deep_research.news.core.base_card import CardSource

        return CardSource(
            type="subscription",
            source_id="test-source",
            created_from="test",
        )

    def test_subscription_initialization(self, mock_storage, card_source):
        """Subscription initializes with correct attributes."""
        from local_deep_research.news.subscription_manager.base_subscription import (
            BaseSubscription,
        )

        # Create concrete implementation
        class TestSubscription(BaseSubscription):
            def generate_search_query(self):
                return self.query_or_topic

            def get_subscription_type(self):
                return "test"

        sub = TestSubscription(
            user_id="user123",
            source=card_source,
            query_or_topic="test query",
            refresh_interval_minutes=60,
        )

        assert sub.user_id == "user123"
        assert sub.query_or_topic == "test query"
        assert sub.refresh_interval_minutes == 60
        assert sub.is_active is True
        assert sub.refresh_count == 0
        assert sub.error_count == 0

    def test_subscription_generates_id_if_not_provided(
        self, mock_storage, card_source
    ):
        """Subscription generates ID if not provided."""
        from local_deep_research.news.subscription_manager.base_subscription import (
            BaseSubscription,
        )

        class TestSubscription(BaseSubscription):
            def generate_search_query(self):
                return self.query_or_topic

            def get_subscription_type(self):
                return "test"

        sub = TestSubscription(
            user_id="user123",
            source=card_source,
            query_or_topic="test",
        )

        assert sub.id is not None
        assert len(sub.id) > 0

    def test_subscription_uses_provided_id(self, mock_storage, card_source):
        """Subscription uses provided ID."""
        from local_deep_research.news.subscription_manager.base_subscription import (
            BaseSubscription,
        )

        class TestSubscription(BaseSubscription):
            def generate_search_query(self):
                return self.query_or_topic

            def get_subscription_type(self):
                return "test"

        sub = TestSubscription(
            user_id="user123",
            source=card_source,
            query_or_topic="test",
            subscription_id="custom-id-123",
        )

        assert sub.id == "custom-id-123"

    def test_default_refresh_interval(self, mock_storage, card_source):
        """Default refresh interval is 240 minutes (4 hours)."""
        from local_deep_research.news.subscription_manager.base_subscription import (
            BaseSubscription,
        )

        class TestSubscription(BaseSubscription):
            def generate_search_query(self):
                return self.query_or_topic

            def get_subscription_type(self):
                return "test"

        sub = TestSubscription(
            user_id="user123",
            source=card_source,
            query_or_topic="test",
        )

        assert sub.refresh_interval_minutes == 240


class TestRefreshScheduling:
    """Tests for refresh scheduling logic."""

    @pytest.fixture
    def mock_storage(self):
        """Mock the storage."""
        with patch(
            "local_deep_research.news.subscription_manager.base_subscription.SQLSubscriptionStorage"
        ) as mock:
            mock.return_value = MagicMock()
            yield mock

    @pytest.fixture
    def card_source(self):
        """Create a mock CardSource."""
        from local_deep_research.news.core.base_card import CardSource

        return CardSource(type="subscription")

    @pytest.fixture
    def test_subscription_class(self):
        """Create a concrete subscription class for testing."""
        from local_deep_research.news.subscription_manager.base_subscription import (
            BaseSubscription,
        )

        class TestSubscription(BaseSubscription):
            def generate_search_query(self):
                return self.query_or_topic

            def get_subscription_type(self):
                return "test"

        return TestSubscription

    def test_new_subscription_calculates_next_refresh(
        self, mock_storage, card_source, test_subscription_class
    ):
        """New subscription calculates next refresh from created_at."""
        sub = test_subscription_class(
            user_id="user123",
            source=card_source,
            query_or_topic="test",
            refresh_interval_minutes=60,
        )

        # Next refresh should be approximately 60 minutes after creation
        expected = sub.created_at + timedelta(minutes=60)
        assert sub.next_refresh == expected

    def test_should_refresh_returns_false_for_new_subscription(
        self, mock_storage, card_source, test_subscription_class
    ):
        """New subscription should not need refresh immediately."""
        sub = test_subscription_class(
            user_id="user123",
            source=card_source,
            query_or_topic="test",
            refresh_interval_minutes=60,
        )

        # New subscription shouldn't need refresh yet
        assert sub.should_refresh() is False

    def test_should_refresh_returns_false_when_inactive(
        self, mock_storage, card_source, test_subscription_class
    ):
        """Inactive subscription should not refresh."""
        sub = test_subscription_class(
            user_id="user123",
            source=card_source,
            query_or_topic="test",
            refresh_interval_minutes=0,  # Immediate refresh
        )
        sub.is_active = False

        assert sub.should_refresh() is False

    def test_is_due_for_refresh_alias(
        self, mock_storage, card_source, test_subscription_class
    ):
        """is_due_for_refresh is alias for should_refresh."""
        sub = test_subscription_class(
            user_id="user123",
            source=card_source,
            query_or_topic="test",
        )

        assert sub.is_due_for_refresh() == sub.should_refresh()


class TestCardSource:
    """Tests for CardSource dataclass."""

    def test_card_source_initialization(self):
        """CardSource initializes with required fields."""
        from local_deep_research.news.core.base_card import CardSource

        source = CardSource(
            type="news_item",
            source_id="src-123",
            created_from="test",
        )

        assert source.type == "news_item"
        assert source.source_id == "src-123"
        assert source.created_from == "test"

    def test_card_source_default_metadata(self):
        """CardSource has empty default metadata."""
        from local_deep_research.news.core.base_card import CardSource

        source = CardSource(type="test")

        assert source.metadata == {}

    def test_card_source_with_metadata(self):
        """CardSource accepts custom metadata."""
        from local_deep_research.news.core.base_card import CardSource

        source = CardSource(
            type="test",
            metadata={"key": "value"},
        )

        assert source.metadata == {"key": "value"}

    def test_card_source_types(self):
        """CardSource accepts various valid types."""
        from local_deep_research.news.core.base_card import CardSource

        valid_types = [
            "news_item",
            "user_search",
            "subscription",
            "news_research",
        ]

        for type_name in valid_types:
            source = CardSource(type=type_name)
            assert source.type == type_name
