"""
Extended tests for news/api.py

Tests cover:
- get_recommender() singleton
- _notify_scheduler_about_subscription_change() helper
- Exception classes
- Basic API function structure
"""

from unittest.mock import Mock, patch


class TestGetRecommender:
    """Tests for get_recommender() singleton."""

    def test_returns_recommender_instance(self):
        """Returns a recommender instance."""
        with patch("local_deep_research.news.api._recommender", None):
            with patch(
                "local_deep_research.news.api.TopicBasedRecommender"
            ) as mock_recommender:
                mock_recommender.return_value = Mock()

                from local_deep_research.news import api

                api._recommender = None
                result = api.get_recommender()

                assert result is not None

    def test_creates_singleton(self):
        """Creates singleton on first call."""
        with patch("local_deep_research.news.api._recommender", None):
            with patch(
                "local_deep_research.news.api.TopicBasedRecommender"
            ) as mock_recommender:
                mock_instance = Mock()
                mock_recommender.return_value = mock_instance

                from local_deep_research.news import api

                api._recommender = None

                result1 = api.get_recommender()
                result2 = api.get_recommender()

                # Should be same instance
                assert result1 is result2


class TestNotifyScheduler:
    """Tests for _notify_scheduler_about_subscription_change() helper."""

    def test_function_exists(self):
        """_notify_scheduler_about_subscription_change function exists."""
        from local_deep_research.news.api import (
            _notify_scheduler_about_subscription_change,
        )

        assert callable(_notify_scheduler_about_subscription_change)

    def test_accepts_action_parameter(self):
        """Function accepts action parameter without crashing."""
        from local_deep_research.news.api import (
            _notify_scheduler_about_subscription_change,
        )

        # Should not raise even if scheduler is not set up
        try:
            _notify_scheduler_about_subscription_change("created")
        except Exception:
            pass  # May fail if no scheduler, but shouldn't raise unexpected errors


class TestAPIExceptionClasses:
    """Tests for API exception classes."""

    def test_invalid_limit_exception_exists(self):
        """InvalidLimitException is importable."""
        from local_deep_research.news.exceptions import InvalidLimitException

        assert InvalidLimitException is not None

    def test_subscription_not_found_exception_exists(self):
        """SubscriptionNotFoundException is importable."""
        from local_deep_research.news.exceptions import (
            SubscriptionNotFoundException,
        )

        assert SubscriptionNotFoundException is not None

    def test_subscription_creation_exception_exists(self):
        """SubscriptionCreationException is importable."""
        from local_deep_research.news.exceptions import (
            SubscriptionCreationException,
        )

        assert SubscriptionCreationException is not None

    def test_subscription_update_exception_exists(self):
        """SubscriptionUpdateException is importable."""
        from local_deep_research.news.exceptions import (
            SubscriptionUpdateException,
        )

        assert SubscriptionUpdateException is not None

    def test_subscription_deletion_exception_exists(self):
        """SubscriptionDeletionException is importable."""
        from local_deep_research.news.exceptions import (
            SubscriptionDeletionException,
        )

        assert SubscriptionDeletionException is not None

    def test_database_access_exception_exists(self):
        """DatabaseAccessException is importable."""
        from local_deep_research.news.exceptions import DatabaseAccessException

        assert DatabaseAccessException is not None

    def test_news_feed_generation_exception_exists(self):
        """NewsFeedGenerationException is importable."""
        from local_deep_research.news.exceptions import (
            NewsFeedGenerationException,
        )

        assert NewsFeedGenerationException is not None

    def test_not_implemented_exception_exists(self):
        """NotImplementedException is importable."""
        from local_deep_research.news.exceptions import NotImplementedException

        assert NotImplementedException is not None

    def test_news_api_exception_exists(self):
        """NewsAPIException is importable."""
        from local_deep_research.news.exceptions import NewsAPIException

        assert NewsAPIException is not None


class TestAPIFunctionSignatures:
    """Tests for API function signatures."""

    def test_get_news_feed_exists(self):
        """get_news_feed function exists."""
        from local_deep_research.news.api import get_news_feed

        assert callable(get_news_feed)

    def test_create_subscription_exists(self):
        """create_subscription function exists."""
        from local_deep_research.news.api import create_subscription

        assert callable(create_subscription)

    def test_get_subscriptions_exists(self):
        """get_subscriptions function exists."""
        from local_deep_research.news.api import get_subscriptions

        assert callable(get_subscriptions)

    def test_get_subscription_exists(self):
        """get_subscription function exists."""
        from local_deep_research.news.api import get_subscription

        assert callable(get_subscription)

    def test_update_subscription_exists(self):
        """update_subscription function exists."""
        from local_deep_research.news.api import update_subscription

        assert callable(update_subscription)

    def test_delete_subscription_exists(self):
        """delete_subscription function exists."""
        from local_deep_research.news.api import delete_subscription

        assert callable(delete_subscription)


class TestAPIModuleImports:
    """Tests for API module imports."""

    def test_api_module_importable(self):
        """API module is importable."""
        from local_deep_research.news import api

        assert api is not None

    def test_recommender_class_importable(self):
        """TopicBasedRecommender is importable through api module."""
        from local_deep_research.news.recommender.topic_based import (
            TopicBasedRecommender,
        )

        assert TopicBasedRecommender is not None


class TestExceptionInheritance:
    """Tests for exception inheritance."""

    def test_invalid_limit_inherits_from_exception(self):
        """InvalidLimitException inherits from Exception."""
        from local_deep_research.news.exceptions import InvalidLimitException

        exc = InvalidLimitException(-1)
        assert isinstance(exc, Exception)

    def test_subscription_not_found_inherits_from_exception(self):
        """SubscriptionNotFoundException inherits from Exception."""
        from local_deep_research.news.exceptions import (
            SubscriptionNotFoundException,
        )

        exc = SubscriptionNotFoundException("sub-123")
        assert isinstance(exc, Exception)

    def test_database_access_inherits_from_exception(self):
        """DatabaseAccessException inherits from Exception."""
        from local_deep_research.news.exceptions import DatabaseAccessException

        exc = DatabaseAccessException("query", "DB error")
        assert isinstance(exc, Exception)


class TestExceptionMessages:
    """Tests for exception message formatting."""

    def test_invalid_limit_has_limit_value_in_details(self):
        """InvalidLimitException stores limit value in details."""
        from local_deep_research.news.exceptions import InvalidLimitException

        exc = InvalidLimitException(-5)
        assert "provided_limit" in exc.details
        assert exc.details["provided_limit"] == -5

    def test_subscription_not_found_has_id_in_details(self):
        """SubscriptionNotFoundException stores subscription_id in details."""
        from local_deep_research.news.exceptions import (
            SubscriptionNotFoundException,
        )

        exc = SubscriptionNotFoundException("sub-123")
        assert "subscription_id" in exc.details
        assert exc.details["subscription_id"] == "sub-123"

    def test_invalid_limit_message(self):
        """InvalidLimitException has proper message."""
        from local_deep_research.news.exceptions import InvalidLimitException

        exc = InvalidLimitException(-5)
        assert "-5" in str(exc)

    def test_subscription_not_found_message(self):
        """SubscriptionNotFoundException has proper message."""
        from local_deep_research.news.exceptions import (
            SubscriptionNotFoundException,
        )

        exc = SubscriptionNotFoundException("sub-123")
        assert "sub-123" in str(exc)

    def test_database_access_exception_has_operation_in_details(self):
        """DatabaseAccessException stores operation in details."""
        from local_deep_research.news.exceptions import DatabaseAccessException

        exc = DatabaseAccessException("query", "Connection failed")
        assert "operation" in exc.details
        assert exc.details["operation"] == "query"

    def test_exception_to_dict_includes_error(self):
        """Exception to_dict includes error message."""
        from local_deep_research.news.exceptions import InvalidLimitException

        exc = InvalidLimitException(-5)
        result = exc.to_dict()

        assert "error" in result
        assert "-5" in result["error"]

    def test_exception_to_dict_includes_status_code(self):
        """Exception to_dict includes status_code."""
        from local_deep_research.news.exceptions import (
            SubscriptionNotFoundException,
        )

        exc = SubscriptionNotFoundException("sub-123")
        result = exc.to_dict()

        assert "status_code" in result
        assert result["status_code"] == 404

    def test_exception_to_dict_includes_error_code(self):
        """Exception to_dict includes error_code."""
        from local_deep_research.news.exceptions import InvalidLimitException

        exc = InvalidLimitException(-5)
        result = exc.to_dict()

        assert "error_code" in result
        assert result["error_code"] == "INVALID_LIMIT"
