"""
Test that API functions raise appropriate exceptions instead of returning error dicts.
"""

import pytest
from unittest.mock import patch, MagicMock

from local_deep_research.news import api as news_api
from local_deep_research.news.exceptions import (
    InvalidLimitException,
    SubscriptionNotFoundException,
    SubscriptionCreationException,
    DatabaseAccessException,
    NotImplementedException,
)


class TestGetNewsFeedExceptions:
    """Test exception handling in get_news_feed function."""

    def test_invalid_limit_raises_exception(self):
        """Test that invalid limit raises InvalidLimitException."""
        with pytest.raises(InvalidLimitException) as exc_info:
            news_api.get_news_feed(limit=0)

        assert exc_info.value.status_code == 400
        assert exc_info.value.details["provided_limit"] == 0

    def test_negative_limit_raises_exception(self):
        """Test that negative limit raises InvalidLimitException."""
        with pytest.raises(InvalidLimitException) as exc_info:
            news_api.get_news_feed(limit=-10)

        assert exc_info.value.details["provided_limit"] == -10

    @patch("local_deep_research.database.session_context.get_user_db_session")
    def test_database_error_raises_exception(self, mock_db_session):
        """Test that database errors raise DatabaseAccessException."""
        mock_db_session.side_effect = Exception("Connection failed")

        with pytest.raises(DatabaseAccessException) as exc_info:
            news_api.get_news_feed(user_id="test_user")

        assert exc_info.value.status_code == 500
        assert "research_history_query" in exc_info.value.details["operation"]
        assert "Connection failed" in exc_info.value.message


class TestSubscriptionExceptions:
    """Test exception handling in subscription functions."""

    @patch("local_deep_research.database.session_context.get_user_db_session")
    def test_get_subscription_not_found(self, mock_db_session):
        """Test that missing subscription raises SubscriptionNotFoundException."""
        mock_session = MagicMock()
        mock_db_session.return_value.__enter__.return_value = mock_session
        mock_session.query().filter_by().first.return_value = None

        with pytest.raises(SubscriptionNotFoundException) as exc_info:
            news_api.get_subscription("non-existent-id")

        assert exc_info.value.status_code == 404
        assert exc_info.value.details["subscription_id"] == "non-existent-id"

    @patch("local_deep_research.database.session_context.get_user_db_session")
    def test_get_subscription_database_error(self, mock_db_session):
        """Test that database errors in get_subscription raise DatabaseAccessException."""
        mock_db_session.side_effect = Exception("Database locked")

        with pytest.raises(DatabaseAccessException) as exc_info:
            news_api.get_subscription("sub-123")

        assert exc_info.value.status_code == 500
        assert "get_subscription" in exc_info.value.details["operation"]

    @patch("local_deep_research.database.session_context.get_user_db_session")
    def test_update_subscription_not_found(self, mock_db_session):
        """Test that updating non-existent subscription raises exception."""
        mock_session = MagicMock()
        mock_db_session.return_value.__enter__.return_value = mock_session
        mock_session.query().filter_by().first.return_value = None

        with pytest.raises(SubscriptionNotFoundException) as exc_info:
            news_api.update_subscription("non-existent", {"name": "New Name"})

        assert exc_info.value.details["subscription_id"] == "non-existent"

    @patch("local_deep_research.database.session_context.get_user_db_session")
    def test_delete_subscription_not_found(self, mock_db_session):
        """Test that deleting non-existent subscription raises exception."""
        mock_session = MagicMock()
        mock_db_session.return_value.__enter__.return_value = mock_session
        mock_session.query().filter_by().first.return_value = None

        with pytest.raises(SubscriptionNotFoundException) as exc_info:
            news_api.delete_subscription("non-existent")

        assert exc_info.value.details["subscription_id"] == "non-existent"

    @patch("local_deep_research.database.session_context.get_user_db_session")
    def test_create_subscription_database_error(self, mock_db_session):
        """Test that database error during creation raises exception."""
        mock_db_session.side_effect = Exception("Constraint violation")

        with pytest.raises(SubscriptionCreationException) as exc_info:
            news_api.create_subscription(
                user_id="test_user",
                query="test query",
                subscription_type="search",
            )

        assert exc_info.value.status_code == 500
        assert "Constraint violation" in exc_info.value.message
        assert exc_info.value.details["query"] == "test query"


class TestNotImplementedExceptions:
    """Test that unimplemented features raise NotImplementedException."""

    def test_research_news_item_not_implemented(self):
        """Test that research_news_item raises NotImplementedException."""
        with pytest.raises(NotImplementedException) as exc_info:
            news_api.research_news_item("card-123", "detailed")

        assert exc_info.value.details["feature"] == "research_news_item"

    def test_save_preferences_not_implemented(self):
        """Test that save_news_preferences raises NotImplementedException."""
        with pytest.raises(NotImplementedException) as exc_info:
            news_api.save_news_preferences("user", {"theme": "dark"})

        assert exc_info.value.details["feature"] == "save_news_preferences"

    def test_get_categories_not_implemented(self):
        """Test that get_news_categories raises NotImplementedException."""
        with pytest.raises(NotImplementedException) as exc_info:
            news_api.get_news_categories()

        assert exc_info.value.details["feature"] == "get_news_categories"


class TestSubscriptionHistoryExceptions:
    """Test exception handling in subscription history functions."""

    @patch("local_deep_research.database.session_context.get_user_db_session")
    def test_subscription_history_not_found(self, mock_db_session):
        """Test that history for non-existent subscription raises exception."""
        mock_session = MagicMock()
        mock_db_session.return_value.__enter__.return_value = mock_session

        # First call for subscription lookup returns None
        mock_session.query().filter_by().first.return_value = None

        with pytest.raises(SubscriptionNotFoundException) as exc_info:
            news_api.get_subscription_history("non-existent")

        assert exc_info.value.details["subscription_id"] == "non-existent"

    @patch("local_deep_research.database.session_context.get_user_db_session")
    def test_subscription_history_database_error(self, mock_db_session):
        """Test that database error in history raises exception."""
        mock_db_session.side_effect = Exception("Query timeout")

        with pytest.raises(DatabaseAccessException) as exc_info:
            news_api.get_subscription_history("sub-123")

        assert "get_subscription_history" in exc_info.value.details["operation"]
        assert "Query timeout" in exc_info.value.message
