"""
Comprehensive tests for base_preference module.
Tests BasePreferenceManager and TopicRegistry classes.
"""

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


class TestBasePreferenceManagerInit:
    """Tests for BasePreferenceManager initialization."""

    def test_stores_storage_backend(self):
        """Test stores storage_backend reference."""
        from local_deep_research.news.preference_manager.base_preference import (
            BasePreferenceManager,
        )

        # Create a concrete implementation for testing
        class ConcretePreferenceManager(BasePreferenceManager):
            def get_preferences(self, user_id):
                return {}

            def update_preferences(self, user_id, preferences):
                return preferences

        mock_storage = Mock()
        manager = ConcretePreferenceManager(storage_backend=mock_storage)

        assert manager.storage_backend is mock_storage

    def test_accepts_none_storage(self):
        """Test accepts None for storage_backend."""
        from local_deep_research.news.preference_manager.base_preference import (
            BasePreferenceManager,
        )

        class ConcretePreferenceManager(BasePreferenceManager):
            def get_preferences(self, user_id):
                return {}

            def update_preferences(self, user_id, preferences):
                return preferences

        manager = ConcretePreferenceManager(storage_backend=None)

        assert manager.storage_backend is None


class TestAddInterest:
    """Tests for add_interest method."""

    def test_adds_interest_to_preferences(self):
        """Test adds interest to user preferences."""
        from local_deep_research.news.preference_manager.base_preference import (
            BasePreferenceManager,
        )

        preferences = {}

        class ConcretePreferenceManager(BasePreferenceManager):
            def get_preferences(self, user_id):
                return preferences

            def update_preferences(self, user_id, prefs):
                preferences.update(prefs)
                return prefs

        manager = ConcretePreferenceManager()
        manager.add_interest("user123", "AI")

        assert "interests" in preferences
        assert "AI" in preferences["interests"]

    def test_sets_weight_for_interest(self):
        """Test sets weight for interest."""
        from local_deep_research.news.preference_manager.base_preference import (
            BasePreferenceManager,
        )

        preferences = {}

        class ConcretePreferenceManager(BasePreferenceManager):
            def get_preferences(self, user_id):
                return preferences

            def update_preferences(self, user_id, prefs):
                preferences.update(prefs)
                return prefs

        manager = ConcretePreferenceManager()
        manager.add_interest("user123", "Machine Learning", weight=2.0)

        assert preferences["interests"]["Machine Learning"] == 2.0

    def test_default_weight_is_1(self):
        """Test default weight is 1.0."""
        from local_deep_research.news.preference_manager.base_preference import (
            BasePreferenceManager,
        )

        preferences = {}

        class ConcretePreferenceManager(BasePreferenceManager):
            def get_preferences(self, user_id):
                return preferences

            def update_preferences(self, user_id, prefs):
                preferences.update(prefs)
                return prefs

        manager = ConcretePreferenceManager()
        manager.add_interest("user123", "Tech")

        assert preferences["interests"]["Tech"] == 1.0

    def test_updates_timestamp(self):
        """Test updates interests_updated_at timestamp."""
        from local_deep_research.news.preference_manager.base_preference import (
            BasePreferenceManager,
        )

        preferences = {}

        class ConcretePreferenceManager(BasePreferenceManager):
            def get_preferences(self, user_id):
                return preferences

            def update_preferences(self, user_id, prefs):
                preferences.update(prefs)
                return prefs

        manager = ConcretePreferenceManager()
        manager.add_interest("user123", "Science")

        assert "interests_updated_at" in preferences


class TestRemoveInterest:
    """Tests for remove_interest method."""

    def test_removes_interest_from_preferences(self):
        """Test removes interest from preferences."""
        from local_deep_research.news.preference_manager.base_preference import (
            BasePreferenceManager,
        )

        preferences = {"interests": {"AI": 1.0, "Tech": 1.5}}

        class ConcretePreferenceManager(BasePreferenceManager):
            def get_preferences(self, user_id):
                return preferences

            def update_preferences(self, user_id, prefs):
                preferences.update(prefs)
                return prefs

        manager = ConcretePreferenceManager()
        manager.remove_interest("user123", "AI")

        assert "AI" not in preferences["interests"]
        assert "Tech" in preferences["interests"]

    def test_does_nothing_if_interest_not_found(self):
        """Test does nothing if interest doesn't exist."""
        from local_deep_research.news.preference_manager.base_preference import (
            BasePreferenceManager,
        )

        preferences = {"interests": {"AI": 1.0}}

        class ConcretePreferenceManager(BasePreferenceManager):
            def get_preferences(self, user_id):
                return preferences

            def update_preferences(self, user_id, prefs):
                return prefs

        manager = ConcretePreferenceManager()

        # Should not raise
        manager.remove_interest("user123", "NonExistent")


class TestIgnoreTopic:
    """Tests for ignore_topic method."""

    def test_adds_topic_to_disliked_list(self):
        """Test adds topic to disliked_topics list."""
        from local_deep_research.news.preference_manager.base_preference import (
            BasePreferenceManager,
        )

        preferences = {}

        class ConcretePreferenceManager(BasePreferenceManager):
            def get_preferences(self, user_id):
                return preferences

            def update_preferences(self, user_id, prefs):
                preferences.update(prefs)
                return prefs

        manager = ConcretePreferenceManager()
        manager.ignore_topic("user123", "Politics")

        assert "Politics" in preferences["disliked_topics"]

    def test_does_not_add_duplicate_topic(self):
        """Test does not add duplicate topic."""
        from local_deep_research.news.preference_manager.base_preference import (
            BasePreferenceManager,
        )

        preferences = {"disliked_topics": ["Politics"]}

        class ConcretePreferenceManager(BasePreferenceManager):
            def get_preferences(self, user_id):
                return preferences

            def update_preferences(self, user_id, prefs):
                preferences.update(prefs)
                return prefs

        manager = ConcretePreferenceManager()
        manager.ignore_topic("user123", "Politics")

        assert preferences["disliked_topics"].count("Politics") == 1

    def test_updates_timestamp(self):
        """Test updates preferences_updated_at timestamp."""
        from local_deep_research.news.preference_manager.base_preference import (
            BasePreferenceManager,
        )

        preferences = {}

        class ConcretePreferenceManager(BasePreferenceManager):
            def get_preferences(self, user_id):
                return preferences

            def update_preferences(self, user_id, prefs):
                preferences.update(prefs)
                return prefs

        manager = ConcretePreferenceManager()
        manager.ignore_topic("user123", "Sports")

        assert "preferences_updated_at" in preferences


class TestBoostSource:
    """Tests for boost_source method."""

    def test_adds_source_weight(self):
        """Test adds source to source_weights."""
        from local_deep_research.news.preference_manager.base_preference import (
            BasePreferenceManager,
        )

        preferences = {}

        class ConcretePreferenceManager(BasePreferenceManager):
            def get_preferences(self, user_id):
                return preferences

            def update_preferences(self, user_id, prefs):
                preferences.update(prefs)
                return prefs

        manager = ConcretePreferenceManager()
        manager.boost_source("user123", "reuters.com", weight=2.0)

        assert "reuters.com" in preferences["source_weights"]
        assert preferences["source_weights"]["reuters.com"] == 2.0

    def test_default_weight_is_1_5(self):
        """Test default weight is 1.5."""
        from local_deep_research.news.preference_manager.base_preference import (
            BasePreferenceManager,
        )

        preferences = {}

        class ConcretePreferenceManager(BasePreferenceManager):
            def get_preferences(self, user_id):
                return preferences

            def update_preferences(self, user_id, prefs):
                preferences.update(prefs)
                return prefs

        manager = ConcretePreferenceManager()
        manager.boost_source("user123", "bbc.com")

        assert preferences["source_weights"]["bbc.com"] == 1.5


class TestGetDefaultPreferences:
    """Tests for get_default_preferences method."""

    def test_returns_dict(self):
        """Test returns a dictionary."""
        from local_deep_research.news.preference_manager.base_preference import (
            BasePreferenceManager,
        )

        class ConcretePreferenceManager(BasePreferenceManager):
            def get_preferences(self, user_id):
                return {}

            def update_preferences(self, user_id, prefs):
                return prefs

        manager = ConcretePreferenceManager()
        result = manager.get_default_preferences()

        assert isinstance(result, dict)

    def test_includes_liked_categories(self):
        """Test includes liked_categories list."""
        from local_deep_research.news.preference_manager.base_preference import (
            BasePreferenceManager,
        )

        class ConcretePreferenceManager(BasePreferenceManager):
            def get_preferences(self, user_id):
                return {}

            def update_preferences(self, user_id, prefs):
                return prefs

        manager = ConcretePreferenceManager()
        result = manager.get_default_preferences()

        assert "liked_categories" in result
        assert isinstance(result["liked_categories"], list)

    def test_includes_impact_threshold(self):
        """Test includes impact_threshold with default 5."""
        from local_deep_research.news.preference_manager.base_preference import (
            BasePreferenceManager,
        )

        class ConcretePreferenceManager(BasePreferenceManager):
            def get_preferences(self, user_id):
                return {}

            def update_preferences(self, user_id, prefs):
                return prefs

        manager = ConcretePreferenceManager()
        result = manager.get_default_preferences()

        assert result["impact_threshold"] == 5

    def test_includes_focus_preferences(self):
        """Test includes focus_preferences dict."""
        from local_deep_research.news.preference_manager.base_preference import (
            BasePreferenceManager,
        )

        class ConcretePreferenceManager(BasePreferenceManager):
            def get_preferences(self, user_id):
                return {}

            def update_preferences(self, user_id, prefs):
                return prefs

        manager = ConcretePreferenceManager()
        result = manager.get_default_preferences()

        assert "focus_preferences" in result
        assert "breaking" in result["focus_preferences"]
        assert result["focus_preferences"]["breaking"] is True

    def test_includes_search_strategy(self):
        """Test includes search_strategy."""
        from local_deep_research.news.preference_manager.base_preference import (
            BasePreferenceManager,
        )

        class ConcretePreferenceManager(BasePreferenceManager):
            def get_preferences(self, user_id):
                return {}

            def update_preferences(self, user_id, prefs):
                return prefs

        manager = ConcretePreferenceManager()
        result = manager.get_default_preferences()

        assert "search_strategy" in result

    def test_includes_timestamps(self):
        """Test includes created_at and preferences_updated_at."""
        from local_deep_research.news.preference_manager.base_preference import (
            BasePreferenceManager,
        )

        class ConcretePreferenceManager(BasePreferenceManager):
            def get_preferences(self, user_id):
                return {}

            def update_preferences(self, user_id, prefs):
                return prefs

        manager = ConcretePreferenceManager()
        result = manager.get_default_preferences()

        assert "created_at" in result
        assert "preferences_updated_at" in result


class TestTopicRegistryInit:
    """Tests for TopicRegistry initialization."""

    def test_creates_instance(self):
        """Test creates instance successfully."""
        from local_deep_research.news.preference_manager.base_preference import (
            TopicRegistry,
        )

        registry = TopicRegistry()

        assert registry is not None

    def test_accepts_llm_client(self):
        """Test accepts optional llm_client."""
        from local_deep_research.news.preference_manager.base_preference import (
            TopicRegistry,
        )

        mock_llm = Mock()
        registry = TopicRegistry(llm_client=mock_llm)

        assert registry.llm_client is mock_llm

    def test_initializes_empty_topics_dict(self):
        """Test initializes empty topics dictionary."""
        from local_deep_research.news.preference_manager.base_preference import (
            TopicRegistry,
        )

        registry = TopicRegistry()

        assert registry.topics == {}


class TestExtractTopics:
    """Tests for extract_topics method."""

    def test_extract_topics_returns_list(self):
        """Test extract_topics returns a list."""
        from local_deep_research.news.preference_manager.base_preference import (
            TopicRegistry,
        )

        registry = TopicRegistry()

        # Mock the internal call to generate_topics via patching the import
        with patch(
            "local_deep_research.news.utils.topic_generator.generate_topics"
        ) as mock_gen:
            mock_gen.return_value = ["AI", "Tech"]

            result = registry.extract_topics("Some content")

            assert isinstance(result, list)

    def test_extract_topics_registers_topics(self):
        """Test extract_topics registers discovered topics."""
        from local_deep_research.news.preference_manager.base_preference import (
            TopicRegistry,
        )

        registry = TopicRegistry()

        # Manually add topics via register_topic to test registration
        registry.register_topic("ManualTopic")

        assert "ManualTopic" in registry.topics

    def test_extract_topics_accepts_max_topics(self):
        """Test extract_topics accepts max_topics parameter."""
        from local_deep_research.news.preference_manager.base_preference import (
            TopicRegistry,
        )
        import inspect

        registry = TopicRegistry()

        # Check method signature
        sig = inspect.signature(registry.extract_topics)
        params = list(sig.parameters.keys())

        assert "max_topics" in params

    def test_extract_topics_default_max_is_5(self):
        """Test extract_topics default max_topics is 5."""
        from local_deep_research.news.preference_manager.base_preference import (
            TopicRegistry,
        )
        import inspect

        registry = TopicRegistry()

        sig = inspect.signature(registry.extract_topics)
        max_topics_param = sig.parameters.get("max_topics")

        assert max_topics_param.default == 5


class TestRegisterTopic:
    """Tests for register_topic method."""

    def test_adds_new_topic(self):
        """Test adds new topic to registry."""
        from local_deep_research.news.preference_manager.base_preference import (
            TopicRegistry,
        )

        registry = TopicRegistry()
        registry.register_topic("NewTopic")

        assert "NewTopic" in registry.topics

    def test_sets_first_seen_for_new_topic(self):
        """Test sets first_seen for new topic."""
        from local_deep_research.news.preference_manager.base_preference import (
            TopicRegistry,
        )

        registry = TopicRegistry()
        registry.register_topic("Fresh")

        assert "first_seen" in registry.topics["Fresh"]

    def test_initializes_count_to_1(self):
        """Test initializes count to 1 for new topic."""
        from local_deep_research.news.preference_manager.base_preference import (
            TopicRegistry,
        )

        registry = TopicRegistry()
        registry.register_topic("New")

        assert registry.topics["New"]["count"] == 1

    def test_increments_count_for_existing_topic(self):
        """Test increments count for existing topic."""
        from local_deep_research.news.preference_manager.base_preference import (
            TopicRegistry,
        )

        registry = TopicRegistry()
        registry.register_topic("Existing")
        registry.register_topic("Existing")

        assert registry.topics["Existing"]["count"] == 2

    def test_updates_last_seen(self):
        """Test updates last_seen timestamp."""
        from local_deep_research.news.preference_manager.base_preference import (
            TopicRegistry,
        )

        registry = TopicRegistry()
        registry.register_topic("Topic")

        first_seen = registry.topics["Topic"]["last_seen"]

        registry.register_topic("Topic")

        # Should be updated (or same if executed very quickly)
        assert registry.topics["Topic"]["last_seen"] >= first_seen


class TestGetTrendingTopics:
    """Tests for get_trending_topics method."""

    def test_returns_list(self):
        """Test returns a list."""
        from local_deep_research.news.preference_manager.base_preference import (
            TopicRegistry,
        )

        registry = TopicRegistry()
        result = registry.get_trending_topics()

        assert isinstance(result, list)

    def test_returns_empty_for_no_topics(self):
        """Test returns empty list when no topics."""
        from local_deep_research.news.preference_manager.base_preference import (
            TopicRegistry,
        )

        registry = TopicRegistry()
        result = registry.get_trending_topics()

        assert result == []

    def test_filters_by_hours(self):
        """Test filters topics by hours parameter."""
        from local_deep_research.news.preference_manager.base_preference import (
            TopicRegistry,
        )
        from local_deep_research.news.core.utils import utc_now

        registry = TopicRegistry()

        # Add old topic
        old_time = utc_now() - timedelta(hours=48)
        registry.topics["OldTopic"] = {
            "first_seen": old_time,
            "last_seen": old_time,
            "count": 10,
        }

        # Add recent topic
        registry.register_topic("RecentTopic")

        result = registry.get_trending_topics(hours=24)

        assert "RecentTopic" in result
        assert "OldTopic" not in result

    def test_sorts_by_count(self):
        """Test sorts topics by count (highest first)."""
        from local_deep_research.news.preference_manager.base_preference import (
            TopicRegistry,
        )

        registry = TopicRegistry()

        # Register topics with different counts
        for _ in range(5):
            registry.register_topic("HighCount")
        for _ in range(2):
            registry.register_topic("LowCount")

        result = registry.get_trending_topics()

        assert result.index("HighCount") < result.index("LowCount")

    def test_respects_limit(self):
        """Test respects limit parameter."""
        from local_deep_research.news.preference_manager.base_preference import (
            TopicRegistry,
        )

        registry = TopicRegistry()

        for i in range(20):
            registry.register_topic(f"Topic{i}")

        result = registry.get_trending_topics(limit=5)

        assert len(result) <= 5

    def test_default_limit_is_10(self):
        """Test default limit is 10."""
        from local_deep_research.news.preference_manager.base_preference import (
            TopicRegistry,
        )

        registry = TopicRegistry()

        for i in range(20):
            registry.register_topic(f"Topic{i}")

        result = registry.get_trending_topics()

        assert len(result) <= 10


class TestGetTopicInfo:
    """Tests for get_topic_info method."""

    def test_returns_topic_data(self):
        """Test returns topic data dictionary."""
        from local_deep_research.news.preference_manager.base_preference import (
            TopicRegistry,
        )

        registry = TopicRegistry()
        registry.register_topic("MyTopic")

        result = registry.get_topic_info("MyTopic")

        assert isinstance(result, dict)
        assert "count" in result
        assert "first_seen" in result

    def test_returns_none_for_unknown_topic(self):
        """Test returns None for unknown topic."""
        from local_deep_research.news.preference_manager.base_preference import (
            TopicRegistry,
        )

        registry = TopicRegistry()
        result = registry.get_topic_info("Unknown")

        assert result is None


class TestBasePreferenceAbstract:
    """Tests for abstract base class behavior."""

    def test_is_abstract(self):
        """Test BasePreferenceManager is abstract."""
        from local_deep_research.news.preference_manager.base_preference import (
            BasePreferenceManager,
        )
        from abc import ABC

        assert issubclass(BasePreferenceManager, ABC)

    def test_cannot_instantiate_directly(self):
        """Test cannot instantiate BasePreferenceManager directly."""
        from local_deep_research.news.preference_manager.base_preference import (
            BasePreferenceManager,
        )

        with pytest.raises(TypeError):
            BasePreferenceManager()
