"""
Tests for the RelevanceService class.

Tests cover:
- Relevance calculation with and without preferences
- Trending score calculation
- Filtering by minimum impact
"""

from unittest.mock import Mock


class TestRelevanceService:
    """Tests for the RelevanceService class."""

    def test_calculate_relevance_no_prefs(self):
        """Relevance calculation with no user preferences."""
        from local_deep_research.news.core.relevance_service import (
            RelevanceService,
        )

        service = RelevanceService()

        # Create mock card with impact_score
        card = Mock()
        card.impact_score = 7

        result = service.calculate_relevance(card, None)

        # Should return impact_score / 10
        assert result == 0.7

    def test_calculate_relevance_no_prefs_no_impact(self):
        """Relevance calculation with no preferences and no impact score."""
        from local_deep_research.news.core.relevance_service import (
            RelevanceService,
        )

        service = RelevanceService()

        # Card without impact_score attribute
        card = Mock(spec=[])

        result = service.calculate_relevance(card, None)

        # Should return default 5/10 = 0.5
        assert result == 0.5

    def test_calculate_relevance_category_matching(self):
        """Category preference boosting."""
        from local_deep_research.news.core.relevance_service import (
            RelevanceService,
        )

        service = RelevanceService()

        card = Mock()
        card.category = "Technology"
        card.impact_score = 5
        card.topics = []  # Empty topics list

        # Liked category should boost score
        prefs_liked = {
            "liked_categories": ["Technology", "Science"],
            "disliked_categories": [],
            "impact_threshold": 5,
        }
        result_liked = service.calculate_relevance(card, prefs_liked)

        # Disliked category should reduce score
        prefs_disliked = {
            "liked_categories": [],
            "disliked_categories": ["Technology"],
            "impact_threshold": 5,
        }
        result_disliked = service.calculate_relevance(card, prefs_disliked)

        # Neutral category
        prefs_neutral = {
            "liked_categories": ["Sports"],
            "disliked_categories": ["Politics"],
            "impact_threshold": 5,
        }
        result_neutral = service.calculate_relevance(card, prefs_neutral)

        assert result_liked > result_neutral
        assert result_neutral > result_disliked

    def test_calculate_trending_score(self):
        """Trending score calculation based on impact and engagement."""
        from local_deep_research.news.core.relevance_service import (
            RelevanceService,
        )

        service = RelevanceService()

        card = Mock()
        card.impact_score = 8
        card.interaction = {
            "views": 100,
            "votes_up": 20,
            "votes_down": 5,
        }

        result = service.calculate_trending_score(card)

        # Expected: 8 + ((100 + 20*2 - 5) / 10) = 8 + 13.5 = 21.5
        expected = 8 + (100 + 20 * 2 - 5) / 10
        assert result == expected

    def test_calculate_trending_score_no_impact(self):
        """Trending score for card without impact_score."""
        from local_deep_research.news.core.relevance_service import (
            RelevanceService,
        )

        service = RelevanceService()

        card = Mock(spec=[])  # No impact_score attribute

        result = service.calculate_trending_score(card)

        assert result == 0.0

    def test_filter_trending_min_impact(self):
        """Filter by minimum impact score."""
        from local_deep_research.news.core.relevance_service import (
            RelevanceService,
        )

        service = RelevanceService()

        # Create cards with different impact scores
        cards = []
        for impact in [9, 8, 7, 6, 5, 4]:
            card = Mock()
            card.impact_score = impact
            card.interaction = {"views": 10, "votes_up": 1, "votes_down": 0}
            cards.append(card)

        # Filter with min_impact=7
        result = service.filter_trending(cards, min_impact=7, limit=10)

        assert len(result) == 3  # Only scores 9, 8, 7

        # Verify all results have impact >= 7
        for card in result:
            assert card.impact_score >= 7

        # Verify results are sorted by trending score (descending)
        for i in range(len(result) - 1):
            assert result[i].trending_score >= result[i + 1].trending_score

    def test_filter_trending_limit(self):
        """Test limit parameter in filter_trending."""
        from local_deep_research.news.core.relevance_service import (
            RelevanceService,
        )

        service = RelevanceService()

        # Create 10 high-impact cards
        cards = []
        for i in range(10):
            card = Mock()
            card.impact_score = 8
            card.interaction = {"views": i * 10, "votes_up": i, "votes_down": 0}
            cards.append(card)

        result = service.filter_trending(cards, min_impact=7, limit=3)

        assert len(result) == 3

    def test_personalize_feed_with_prefs(self):
        """Test feed personalization with user preferences."""
        from local_deep_research.news.core.relevance_service import (
            RelevanceService,
        )

        service = RelevanceService()

        # Create cards with different categories
        cards = []
        for i, category in enumerate(["Technology", "Sports", "Politics"]):
            card = Mock()
            card.category = category
            card.impact_score = 5 + i
            card.topics = []
            card.interaction = {"viewed": False}
            cards.append(card)

        prefs = {
            "liked_categories": ["Technology"],
            "disliked_categories": ["Politics"],
            "impact_threshold": 5,
        }

        result = service.personalize_feed(cards, prefs)

        assert len(result) == 3
        # Technology card should be first (highest relevance)
        assert result[0].category == "Technology"
        # All cards should have relevance_score set
        for card in result:
            assert hasattr(card, "relevance_score")

    def test_personalize_feed_without_prefs(self):
        """Test feed personalization without user preferences."""
        from local_deep_research.news.core.relevance_service import (
            RelevanceService,
        )

        service = RelevanceService()

        cards = []
        for impact in [8, 5, 9]:
            card = Mock()
            card.impact_score = impact
            card.interaction = {}
            cards.append(card)

        result = service.personalize_feed(cards, None)

        assert len(result) == 3
        # Should be sorted by impact_score / 10
        assert result[0].impact_score == 9  # 0.9 relevance
        assert result[1].impact_score == 8  # 0.8 relevance
        assert result[2].impact_score == 5  # 0.5 relevance

    def test_personalize_feed_exclude_seen(self):
        """Test excluding seen cards from feed."""
        from local_deep_research.news.core.relevance_service import (
            RelevanceService,
        )

        service = RelevanceService()

        cards = []
        for i in range(3):
            card = Mock()
            card.impact_score = 7
            card.interaction = {"viewed": i == 1}  # Middle card is viewed
            cards.append(card)

        # Include seen
        result_with_seen = service.personalize_feed(
            cards, None, include_seen=True
        )
        assert len(result_with_seen) == 3

        # Exclude seen
        result_without_seen = service.personalize_feed(
            cards, None, include_seen=False
        )
        assert len(result_without_seen) == 2

    def test_personalize_feed_empty(self):
        """Test personalization with empty card list."""
        from local_deep_research.news.core.relevance_service import (
            RelevanceService,
        )

        service = RelevanceService()

        result = service.personalize_feed([], None)

        assert len(result) == 0

    def test_calculate_relevance_topic_matching(self):
        """Test relevance boosting based on topic matching."""
        from local_deep_research.news.core.relevance_service import (
            RelevanceService,
        )

        service = RelevanceService()

        card = Mock()
        card.impact_score = 5
        card.topics = ["Artificial Intelligence", "Machine Learning"]

        prefs = {
            "liked_topics": ["artificial", "programming"],
            "impact_threshold": 5,
        }

        result = service.calculate_relevance(card, prefs)

        # Should have topic match bonus
        # Base 0.5 + impact match 0.1 + topic match 0.1 = 0.7
        assert result == 0.7

    def test_calculate_relevance_score_clamping(self):
        """Test that relevance score is clamped to [0, 1]."""
        from local_deep_research.news.core.relevance_service import (
            RelevanceService,
        )

        service = RelevanceService()

        # Card with everything liked
        card = Mock()
        card.category = "Technology"
        card.impact_score = 10
        card.topics = ["AI", "Tech"]

        prefs = {
            "liked_categories": ["Technology"],
            "disliked_categories": [],
            "impact_threshold": 3,
            "liked_topics": ["ai", "tech"],
        }

        result = service.calculate_relevance(card, prefs)

        # Should be clamped to 1.0
        assert result <= 1.0
        assert result >= 0.0

    def test_get_relevance_service_singleton(self):
        """Test that get_relevance_service returns singleton."""
        from local_deep_research.news.core.relevance_service import (
            get_relevance_service,
            RelevanceService,
        )

        service1 = get_relevance_service()
        service2 = get_relevance_service()

        assert service1 is service2
        assert isinstance(service1, RelevanceService)

    def test_filter_trending_empty_list(self):
        """Test filtering with empty card list."""
        from local_deep_research.news.core.relevance_service import (
            RelevanceService,
        )

        service = RelevanceService()

        result = service.filter_trending([], min_impact=7)

        assert len(result) == 0

    def test_filter_trending_no_matching_cards(self):
        """Test filtering when no cards meet minimum impact."""
        from local_deep_research.news.core.relevance_service import (
            RelevanceService,
        )

        service = RelevanceService()

        cards = []
        for impact in [3, 4, 5]:
            card = Mock()
            card.impact_score = impact
            card.interaction = {"views": 10, "votes_up": 1, "votes_down": 0}
            cards.append(card)

        result = service.filter_trending(cards, min_impact=7)

        assert len(result) == 0
