"""
Tests for news/recommender/base_recommender.py

Tests cover:
- BaseRecommender initialization
- Progress callback handling
- User preference access
- Abstract method requirements
"""

import pytest
from unittest.mock import Mock
from abc import ABC


class TestBaseRecommenderInit:
    """Tests for BaseRecommender initialization."""

    def test_base_recommender_is_abstract(self):
        """BaseRecommender is an abstract class."""
        from local_deep_research.news.recommender.base_recommender import (
            BaseRecommender,
        )

        assert issubclass(BaseRecommender, ABC)

    def test_base_recommender_has_abstract_method(self):
        """BaseRecommender requires generate_recommendations."""
        from local_deep_research.news.recommender.base_recommender import (
            BaseRecommender,
        )

        assert hasattr(BaseRecommender, "generate_recommendations")


class TestConcreteRecommender:
    """Tests using a concrete implementation of BaseRecommender."""

    @pytest.fixture
    def mock_preference_manager(self):
        """Create mock preference manager."""
        mock = Mock()
        mock.get_preferences.return_value = {"topic": "test"}
        return mock

    @pytest.fixture
    def mock_rating_system(self):
        """Create mock rating system."""
        mock = Mock()
        mock.get_user_ratings.return_value = []
        return mock

    @pytest.fixture
    def concrete_recommender(self):
        """Create a concrete recommender class."""
        from local_deep_research.news.recommender.base_recommender import (
            BaseRecommender,
        )

        class TestRecommender(BaseRecommender):
            def generate_recommendations(self, user_id, context=None):
                return []

        return TestRecommender

    def test_recommender_initialization_with_defaults(
        self, concrete_recommender
    ):
        """Recommender initializes with default None values."""
        recommender = concrete_recommender()

        assert recommender.preference_manager is None
        assert recommender.rating_system is None
        assert recommender.topic_registry is None
        assert recommender.search_system is None
        assert recommender.progress_callback is None

    def test_recommender_initialization_with_dependencies(
        self, concrete_recommender, mock_preference_manager, mock_rating_system
    ):
        """Recommender initializes with provided dependencies."""
        recommender = concrete_recommender(
            preference_manager=mock_preference_manager,
            rating_system=mock_rating_system,
        )

        assert recommender.preference_manager is mock_preference_manager
        assert recommender.rating_system is mock_rating_system

    def test_strategy_name_is_class_name(self, concrete_recommender):
        """Strategy name is set to class name."""
        recommender = concrete_recommender()

        assert recommender.strategy_name == "TestRecommender"


class TestProgressCallback:
    """Tests for progress callback functionality."""

    @pytest.fixture
    def concrete_recommender(self):
        """Create a concrete recommender class."""
        from local_deep_research.news.recommender.base_recommender import (
            BaseRecommender,
        )

        class TestRecommender(BaseRecommender):
            def generate_recommendations(self, user_id, context=None):
                self._update_progress("Processing", 50, {"step": 1})
                return []

        return TestRecommender

    def test_set_progress_callback(self, concrete_recommender):
        """Progress callback can be set."""
        recommender = concrete_recommender()
        callback = Mock()

        recommender.set_progress_callback(callback)

        assert recommender.progress_callback is callback

    def test_update_progress_calls_callback(self, concrete_recommender):
        """_update_progress calls the callback when set."""
        recommender = concrete_recommender()
        callback = Mock()
        recommender.set_progress_callback(callback)

        recommender._update_progress("Test message", 50, {"key": "value"})

        callback.assert_called_once_with("Test message", 50, {"key": "value"})

    def test_update_progress_does_nothing_without_callback(
        self, concrete_recommender
    ):
        """_update_progress doesn't fail without callback."""
        recommender = concrete_recommender()

        # Should not raise
        recommender._update_progress("Test message", 50, {})

    def test_update_progress_default_metadata(self, concrete_recommender):
        """_update_progress uses empty dict for default metadata."""
        recommender = concrete_recommender()
        callback = Mock()
        recommender.set_progress_callback(callback)

        recommender._update_progress("Test message", 50)

        callback.assert_called_once_with("Test message", 50, {})


class TestUserPreferences:
    """Tests for user preference handling."""

    @pytest.fixture
    def concrete_recommender(self):
        """Create a concrete recommender class."""
        from local_deep_research.news.recommender.base_recommender import (
            BaseRecommender,
        )

        class TestRecommender(BaseRecommender):
            def generate_recommendations(self, user_id, context=None):
                return []

            def get_prefs(self, user_id):
                return self._get_user_preferences(user_id)

        return TestRecommender

    def test_get_user_preferences_with_manager(self, concrete_recommender):
        """_get_user_preferences returns preferences when manager available."""
        mock_manager = Mock()
        mock_manager.get_preferences.return_value = {"topic": "test"}

        recommender = concrete_recommender(preference_manager=mock_manager)
        prefs = recommender.get_prefs("user123")

        assert prefs == {"topic": "test"}
        mock_manager.get_preferences.assert_called_once_with("user123")

    def test_get_user_preferences_without_manager(self, concrete_recommender):
        """_get_user_preferences returns empty dict without manager."""
        recommender = concrete_recommender()
        prefs = recommender.get_prefs("user123")

        assert prefs == {}


class TestGenerateRecommendations:
    """Tests for the generate_recommendations abstract method."""

    @pytest.fixture
    def concrete_recommender(self):
        """Create a concrete recommender class."""
        from local_deep_research.news.recommender.base_recommender import (
            BaseRecommender,
        )

        class TestRecommender(BaseRecommender):
            def generate_recommendations(self, user_id, context=None):
                return [{"id": 1, "topic": "test"}]

        return TestRecommender

    def test_generate_recommendations_returns_list(self, concrete_recommender):
        """generate_recommendations returns a list."""
        recommender = concrete_recommender()
        result = recommender.generate_recommendations("user123")

        assert isinstance(result, list)

    def test_generate_recommendations_accepts_context(
        self, concrete_recommender
    ):
        """generate_recommendations accepts optional context."""
        recommender = concrete_recommender()

        # Should not raise
        result = recommender.generate_recommendations(
            "user123", context={"page": "home"}
        )

        assert isinstance(result, list)
