"""
Extended tests for news/core/card_storage.py

Additional comprehensive tests covering:
- SQLCardStorage list() method with all filters
- get_recent() time-based queries
- get_by_user() user filtering
- Version management edge cases
- _card_to_dict() field mappings
- Error handling and edge cases
- Boundary conditions
"""

from unittest.mock import MagicMock, patch
from datetime import datetime, timezone


class TestSQLCardStorageListFilters:
    """Tests for list() method filter handling."""

    def test_list_filters_by_card_type_string(self):
        """Filters by single card_type string."""
        from local_deep_research.news.core.card_storage import SQLCardStorage

        mock_session = MagicMock()
        mock_context = MagicMock()
        mock_session.__enter__ = MagicMock(return_value=mock_context)
        mock_session.__exit__ = MagicMock(return_value=False)

        mock_query = MagicMock()
        mock_context.query.return_value = mock_query
        mock_query.filter_by.return_value = mock_query
        mock_query.filter.return_value = mock_query
        mock_query.order_by.return_value = mock_query
        mock_query.limit.return_value = mock_query
        mock_query.offset.return_value = mock_query
        mock_query.all.return_value = []

        storage = SQLCardStorage(mock_session)
        storage.list(filters={"card_type": "news"})

        mock_query.filter_by.assert_called_with(card_type="news")

    def test_list_filters_by_card_type_list(self):
        """Filters by list of card_types."""
        from local_deep_research.news.core.card_storage import SQLCardStorage

        mock_session = MagicMock()
        mock_context = MagicMock()
        mock_session.__enter__ = MagicMock(return_value=mock_context)
        mock_session.__exit__ = MagicMock(return_value=False)

        mock_query = MagicMock()
        mock_context.query.return_value = mock_query
        mock_query.filter.return_value = mock_query
        mock_query.order_by.return_value = mock_query
        mock_query.limit.return_value = mock_query
        mock_query.offset.return_value = mock_query
        mock_query.all.return_value = []

        storage = SQLCardStorage(mock_session)
        storage.list(filters={"card_type": ["news", "research"]})

        # Should use .filter() for list filtering
        mock_query.filter.assert_called()

    def test_list_filters_by_is_pinned(self):
        """Filters by is_pinned (mapped to is_saved)."""
        from local_deep_research.news.core.card_storage import SQLCardStorage

        mock_session = MagicMock()
        mock_context = MagicMock()
        mock_session.__enter__ = MagicMock(return_value=mock_context)
        mock_session.__exit__ = MagicMock(return_value=False)

        mock_query = MagicMock()
        mock_context.query.return_value = mock_query
        mock_query.filter_by.return_value = mock_query
        mock_query.order_by.return_value = mock_query
        mock_query.limit.return_value = mock_query
        mock_query.offset.return_value = mock_query
        mock_query.all.return_value = []

        storage = SQLCardStorage(mock_session)
        storage.list(filters={"is_pinned": True})

        mock_query.filter_by.assert_called_with(is_saved=True)

    def test_list_filters_by_category(self):
        """Filters by category."""
        from local_deep_research.news.core.card_storage import SQLCardStorage

        mock_session = MagicMock()
        mock_context = MagicMock()
        mock_session.__enter__ = MagicMock(return_value=mock_context)
        mock_session.__exit__ = MagicMock(return_value=False)

        mock_query = MagicMock()
        mock_context.query.return_value = mock_query
        mock_query.filter_by.return_value = mock_query
        mock_query.order_by.return_value = mock_query
        mock_query.limit.return_value = mock_query
        mock_query.offset.return_value = mock_query
        mock_query.all.return_value = []

        storage = SQLCardStorage(mock_session)
        storage.list(filters={"category": "Technology"})

        mock_query.filter_by.assert_called_with(category="Technology")

    def test_list_with_multiple_filters(self):
        """Applies multiple filters correctly."""
        from local_deep_research.news.core.card_storage import SQLCardStorage

        mock_session = MagicMock()
        mock_context = MagicMock()
        mock_session.__enter__ = MagicMock(return_value=mock_context)
        mock_session.__exit__ = MagicMock(return_value=False)

        mock_query = MagicMock()
        mock_context.query.return_value = mock_query
        mock_query.filter_by.return_value = mock_query
        mock_query.order_by.return_value = mock_query
        mock_query.limit.return_value = mock_query
        mock_query.offset.return_value = mock_query
        mock_query.all.return_value = []

        storage = SQLCardStorage(mock_session)
        storage.list(
            filters={
                "card_type": "news",
                "is_pinned": False,
                "category": "Science",
            }
        )

        # Should be called multiple times for different filters
        assert mock_query.filter_by.call_count >= 2

    def test_list_orders_by_discovered_at_descending(self):
        """Orders results by discovered_at descending."""
        from local_deep_research.news.core.card_storage import SQLCardStorage

        mock_session = MagicMock()
        mock_context = MagicMock()
        mock_session.__enter__ = MagicMock(return_value=mock_context)
        mock_session.__exit__ = MagicMock(return_value=False)

        mock_query = MagicMock()
        mock_context.query.return_value = mock_query
        mock_query.order_by.return_value = mock_query
        mock_query.limit.return_value = mock_query
        mock_query.offset.return_value = mock_query
        mock_query.all.return_value = []

        storage = SQLCardStorage(mock_session)
        storage.list()

        mock_query.order_by.assert_called_once()

    def test_list_applies_pagination(self):
        """Applies limit and offset correctly."""
        from local_deep_research.news.core.card_storage import SQLCardStorage

        mock_session = MagicMock()
        mock_context = MagicMock()
        mock_session.__enter__ = MagicMock(return_value=mock_context)
        mock_session.__exit__ = MagicMock(return_value=False)

        mock_query = MagicMock()
        mock_context.query.return_value = mock_query
        mock_query.order_by.return_value = mock_query
        mock_query.limit.return_value = mock_query
        mock_query.offset.return_value = mock_query
        mock_query.all.return_value = []

        storage = SQLCardStorage(mock_session)
        storage.list(limit=25, offset=50)

        mock_query.limit.assert_called_with(25)
        mock_query.offset.assert_called_with(50)


class TestSQLCardStorageGetRecent:
    """Tests for get_recent() method."""

    def test_get_recent_calculates_cutoff(self):
        """Calculates cutoff time correctly."""
        from local_deep_research.news.core.card_storage import SQLCardStorage

        mock_session = MagicMock()
        mock_context = MagicMock()
        mock_session.__enter__ = MagicMock(return_value=mock_context)
        mock_session.__exit__ = MagicMock(return_value=False)

        mock_query = MagicMock()
        mock_context.query.return_value = mock_query
        mock_query.filter.return_value = mock_query
        mock_query.order_by.return_value = mock_query
        mock_query.limit.return_value = mock_query
        mock_query.all.return_value = []

        storage = SQLCardStorage(mock_session)

        datetime.now(timezone.utc)
        storage.get_recent(hours=48)
        datetime.now(timezone.utc)

        # Verify filter was called
        mock_query.filter.assert_called()

    def test_get_recent_filters_by_card_types(self):
        """Filters by card_types when provided."""
        from local_deep_research.news.core.card_storage import SQLCardStorage

        mock_session = MagicMock()
        mock_context = MagicMock()
        mock_session.__enter__ = MagicMock(return_value=mock_context)
        mock_session.__exit__ = MagicMock(return_value=False)

        mock_query = MagicMock()
        mock_context.query.return_value = mock_query
        mock_query.filter.return_value = mock_query
        mock_query.order_by.return_value = mock_query
        mock_query.limit.return_value = mock_query
        mock_query.all.return_value = []

        storage = SQLCardStorage(mock_session)
        storage.get_recent(card_types=["news", "update"])

        # Should have multiple filter calls
        assert mock_query.filter.call_count == 2

    def test_get_recent_without_card_types(self):
        """Works without card_types filter."""
        from local_deep_research.news.core.card_storage import SQLCardStorage

        mock_session = MagicMock()
        mock_context = MagicMock()
        mock_session.__enter__ = MagicMock(return_value=mock_context)
        mock_session.__exit__ = MagicMock(return_value=False)

        mock_query = MagicMock()
        mock_context.query.return_value = mock_query
        mock_query.filter.return_value = mock_query
        mock_query.order_by.return_value = mock_query
        mock_query.limit.return_value = mock_query
        mock_query.all.return_value = []

        storage = SQLCardStorage(mock_session)
        storage.get_recent(card_types=None)

        # Should only have time filter, not card_type filter
        assert mock_query.filter.call_count == 1

    def test_get_recent_applies_limit(self):
        """Applies limit correctly."""
        from local_deep_research.news.core.card_storage import SQLCardStorage

        mock_session = MagicMock()
        mock_context = MagicMock()
        mock_session.__enter__ = MagicMock(return_value=mock_context)
        mock_session.__exit__ = MagicMock(return_value=False)

        mock_query = MagicMock()
        mock_context.query.return_value = mock_query
        mock_query.filter.return_value = mock_query
        mock_query.order_by.return_value = mock_query
        mock_query.limit.return_value = mock_query
        mock_query.all.return_value = []

        storage = SQLCardStorage(mock_session)
        storage.get_recent(limit=25)

        mock_query.limit.assert_called_with(25)


class TestSQLCardStorageGetByUser:
    """Tests for get_by_user() method."""

    def test_get_by_user_filters_by_user_id(self):
        """Filters cards by user_id from extra_data."""
        from local_deep_research.news.core.card_storage import SQLCardStorage

        mock_session = MagicMock()
        mock_context = MagicMock()
        mock_session.__enter__ = MagicMock(return_value=mock_context)
        mock_session.__exit__ = MagicMock(return_value=False)

        # Create mock cards
        mock_card1 = MagicMock()
        mock_card1.id = "card-1"
        mock_card1.title = "Card 1"
        mock_card1.summary = None
        mock_card1.content = None
        mock_card1.url = None
        mock_card1.source_name = None
        mock_card1.source_type = None
        mock_card1.source_id = None
        mock_card1.category = None
        mock_card1.tags = None
        mock_card1.card_type = "news"
        mock_card1.published_at = None
        mock_card1.discovered_at = datetime.now(timezone.utc)
        mock_card1.is_read = False
        mock_card1.read_at = None
        mock_card1.is_saved = False
        mock_card1.saved_at = None
        mock_card1.extra_data = {"user_id": "user-123"}

        mock_card2 = MagicMock()
        mock_card2.id = "card-2"
        mock_card2.title = "Card 2"
        mock_card2.summary = None
        mock_card2.content = None
        mock_card2.url = None
        mock_card2.source_name = None
        mock_card2.source_type = None
        mock_card2.source_id = None
        mock_card2.category = None
        mock_card2.tags = None
        mock_card2.card_type = "news"
        mock_card2.published_at = None
        mock_card2.discovered_at = datetime.now(timezone.utc)
        mock_card2.is_read = False
        mock_card2.read_at = None
        mock_card2.is_saved = False
        mock_card2.saved_at = None
        mock_card2.extra_data = {"user_id": "user-456"}

        mock_query = MagicMock()
        mock_context.query.return_value = mock_query
        mock_query.order_by.return_value = mock_query
        mock_query.limit.return_value = mock_query
        mock_query.offset.return_value = mock_query
        mock_query.all.return_value = [mock_card1, mock_card2]

        storage = SQLCardStorage(mock_session)
        result = storage.get_by_user("user-123")

        # Should only return cards for user-123
        assert len(result) == 1
        assert result[0]["id"] == "card-1"

    def test_get_by_user_excludes_archived(self):
        """Excludes archived cards."""
        from local_deep_research.news.core.card_storage import SQLCardStorage

        mock_session = MagicMock()
        mock_context = MagicMock()
        mock_session.__enter__ = MagicMock(return_value=mock_context)
        mock_session.__exit__ = MagicMock(return_value=False)

        mock_card = MagicMock()
        mock_card.id = "card-1"
        mock_card.title = "Archived Card"
        mock_card.summary = None
        mock_card.content = None
        mock_card.url = None
        mock_card.source_name = None
        mock_card.source_type = None
        mock_card.source_id = None
        mock_card.category = None
        mock_card.tags = None
        mock_card.card_type = "news"
        mock_card.published_at = None
        mock_card.discovered_at = datetime.now(timezone.utc)
        mock_card.is_read = False
        mock_card.read_at = None
        mock_card.is_saved = False
        mock_card.saved_at = None
        mock_card.extra_data = {"user_id": "user-123", "is_archived": True}

        mock_query = MagicMock()
        mock_context.query.return_value = mock_query
        mock_query.order_by.return_value = mock_query
        mock_query.limit.return_value = mock_query
        mock_query.offset.return_value = mock_query
        mock_query.all.return_value = [mock_card]

        storage = SQLCardStorage(mock_session)
        result = storage.get_by_user("user-123")

        # Should exclude archived cards
        assert len(result) == 0

    def test_get_by_user_applies_pagination(self):
        """Applies pagination to filtered results."""
        from local_deep_research.news.core.card_storage import SQLCardStorage

        mock_session = MagicMock()
        mock_context = MagicMock()
        mock_session.__enter__ = MagicMock(return_value=mock_context)
        mock_session.__exit__ = MagicMock(return_value=False)

        # Create multiple cards for the user
        mock_cards = []
        for i in range(10):
            mock_card = MagicMock()
            mock_card.id = f"card-{i}"
            mock_card.title = f"Card {i}"
            mock_card.summary = None
            mock_card.content = None
            mock_card.url = None
            mock_card.source_name = None
            mock_card.source_type = None
            mock_card.source_id = None
            mock_card.category = None
            mock_card.tags = None
            mock_card.card_type = "news"
            mock_card.published_at = None
            mock_card.discovered_at = datetime.now(timezone.utc)
            mock_card.is_read = False
            mock_card.read_at = None
            mock_card.is_saved = False
            mock_card.saved_at = None
            mock_card.extra_data = {"user_id": "user-123"}
            mock_cards.append(mock_card)

        mock_query = MagicMock()
        mock_context.query.return_value = mock_query
        mock_query.order_by.return_value = mock_query
        mock_query.limit.return_value = mock_query
        mock_query.offset.return_value = mock_query
        mock_query.all.return_value = mock_cards

        storage = SQLCardStorage(mock_session)
        result = storage.get_by_user("user-123", limit=5, offset=2)

        # Should return only 5 items starting from offset 2
        assert len(result) == 5
        assert result[0]["id"] == "card-2"


class TestSQLCardStorageGetLatestVersion:
    """Tests for get_latest_version() method."""

    def test_get_latest_version_returns_stored_version(self):
        """Returns latest version from extra_data when available."""
        from local_deep_research.news.core.card_storage import SQLCardStorage

        mock_session = MagicMock()
        mock_context = MagicMock()
        mock_session.__enter__ = MagicMock(return_value=mock_context)
        mock_session.__exit__ = MagicMock(return_value=False)

        mock_card = MagicMock()
        mock_card.id = "card-123"
        mock_card.title = "Test"
        mock_card.summary = "Summary"
        mock_card.content = None
        mock_card.url = None
        mock_card.source_name = None
        mock_card.source_type = None
        mock_card.source_id = None
        mock_card.category = None
        mock_card.tags = None
        mock_card.card_type = "news"
        mock_card.published_at = None
        mock_card.discovered_at = datetime.now(timezone.utc)
        mock_card.is_read = False
        mock_card.read_at = None
        mock_card.is_saved = False
        mock_card.saved_at = None
        mock_card.extra_data = {}

        mock_context.query.return_value.filter_by.return_value.first.return_value = mock_card

        # Override the metadata to have latest_version
        storage = SQLCardStorage(mock_session)

        # Since extra_data is empty, should return card's state as v1
        result = storage.get_latest_version("card-123")

        assert result is not None
        assert result["version_number"] == 1
        assert result["card_id"] == "card-123"

    def test_get_latest_version_nonexistent_card(self):
        """Returns None for nonexistent card."""
        from local_deep_research.news.core.card_storage import SQLCardStorage

        mock_session = MagicMock()
        mock_context = MagicMock()
        mock_session.__enter__ = MagicMock(return_value=mock_context)
        mock_session.__exit__ = MagicMock(return_value=False)

        mock_context.query.return_value.filter_by.return_value.first.return_value = None

        storage = SQLCardStorage(mock_session)
        result = storage.get_latest_version("nonexistent")

        assert result is None


class TestSQLCardStorageAddVersion:
    """Tests for add_version() method."""

    def test_add_version_stores_in_extra_data(self):
        """Stores version data in extra_data."""
        from local_deep_research.news.core.card_storage import SQLCardStorage

        mock_session = MagicMock()
        mock_context = MagicMock()
        mock_session.__enter__ = MagicMock(return_value=mock_context)
        mock_session.__exit__ = MagicMock(return_value=False)

        mock_card = MagicMock()
        mock_card.extra_data = {}
        mock_context.query.return_value.filter_by.return_value.first.return_value = mock_card

        storage = SQLCardStorage(mock_session)
        result = storage.add_version(
            "card-123",
            {
                "id": "v-123",
                "headline": "New Headline",
                "summary": "New summary",
            },
        )

        assert result == "v-123"
        assert len(mock_card.extra_data["versions"]) == 1
        assert mock_card.extra_data["versions"][0]["id"] == "v-123"
        mock_context.commit.assert_called_once()

    def test_add_version_updates_card_title(self):
        """Updates card title with version headline."""
        from local_deep_research.news.core.card_storage import SQLCardStorage

        mock_session = MagicMock()
        mock_context = MagicMock()
        mock_session.__enter__ = MagicMock(return_value=mock_context)
        mock_session.__exit__ = MagicMock(return_value=False)

        mock_card = MagicMock()
        mock_card.title = "Old Title"
        mock_card.extra_data = {}
        mock_context.query.return_value.filter_by.return_value.first.return_value = mock_card

        storage = SQLCardStorage(mock_session)
        storage.add_version("card-123", {"headline": "New Title"})

        assert mock_card.title == "New Title"

    def test_add_version_increments_version_number(self):
        """Increments version number correctly."""
        from local_deep_research.news.core.card_storage import SQLCardStorage

        mock_session = MagicMock()
        mock_context = MagicMock()
        mock_session.__enter__ = MagicMock(return_value=mock_context)
        mock_session.__exit__ = MagicMock(return_value=False)

        mock_card = MagicMock()
        mock_card.extra_data = {
            "versions": [{"version_number": 1}, {"version_number": 2}]
        }
        mock_context.query.return_value.filter_by.return_value.first.return_value = mock_card

        storage = SQLCardStorage(mock_session)
        storage.add_version("card-123", {"headline": "V3"})

        # New version should be version 3
        new_version = mock_card.extra_data["versions"][-1]
        assert new_version["version_number"] == 3


class TestSQLCardStorageCardToDict:
    """Tests for _card_to_dict() helper method."""

    def test_card_to_dict_all_none_dates(self):
        """Handles all None date fields."""
        from local_deep_research.news.core.card_storage import SQLCardStorage

        mock_session = MagicMock()
        storage = SQLCardStorage(mock_session)

        mock_card = MagicMock()
        mock_card.id = "card-123"
        mock_card.title = "Test"
        mock_card.summary = None
        mock_card.content = None
        mock_card.url = None
        mock_card.source_name = None
        mock_card.source_type = None
        mock_card.source_id = None
        mock_card.category = None
        mock_card.tags = None
        mock_card.card_type = "news"
        mock_card.published_at = None
        mock_card.discovered_at = None
        mock_card.is_read = False
        mock_card.read_at = None
        mock_card.is_saved = False
        mock_card.saved_at = None
        mock_card.extra_data = {}

        result = storage._card_to_dict(mock_card)

        assert result["published_at"] is None
        assert result["discovered_at"] is None
        assert result["created_at"] is None
        assert result["read_at"] is None
        assert result["saved_at"] is None

    def test_card_to_dict_formats_all_dates(self):
        """Formats all date fields as ISO strings."""
        from local_deep_research.news.core.card_storage import SQLCardStorage

        mock_session = MagicMock()
        storage = SQLCardStorage(mock_session)

        now = datetime(2024, 1, 15, 12, 0, 0, tzinfo=timezone.utc)

        mock_card = MagicMock()
        mock_card.id = "card-123"
        mock_card.title = "Test"
        mock_card.summary = None
        mock_card.content = None
        mock_card.url = None
        mock_card.source_name = None
        mock_card.source_type = None
        mock_card.source_id = None
        mock_card.category = None
        mock_card.tags = None
        mock_card.card_type = "news"
        mock_card.published_at = now
        mock_card.discovered_at = now
        mock_card.is_read = True
        mock_card.read_at = now
        mock_card.is_saved = True
        mock_card.saved_at = now
        mock_card.extra_data = {}

        result = storage._card_to_dict(mock_card)

        assert result["published_at"] == "2024-01-15T12:00:00+00:00"
        assert result["discovered_at"] == "2024-01-15T12:00:00+00:00"
        assert result["read_at"] == "2024-01-15T12:00:00+00:00"
        assert result["saved_at"] == "2024-01-15T12:00:00+00:00"

    def test_card_to_dict_maps_is_saved_to_is_pinned(self):
        """Maps is_saved to is_pinned for compatibility."""
        from local_deep_research.news.core.card_storage import SQLCardStorage

        mock_session = MagicMock()
        storage = SQLCardStorage(mock_session)

        mock_card = MagicMock()
        mock_card.id = "card-123"
        mock_card.title = "Test"
        mock_card.summary = None
        mock_card.content = None
        mock_card.url = None
        mock_card.source_name = None
        mock_card.source_type = None
        mock_card.source_id = None
        mock_card.category = None
        mock_card.tags = None
        mock_card.card_type = "news"
        mock_card.published_at = None
        mock_card.discovered_at = None
        mock_card.is_read = False
        mock_card.read_at = None
        mock_card.is_saved = True
        mock_card.saved_at = None
        mock_card.extra_data = {}

        result = storage._card_to_dict(mock_card)

        assert result["is_saved"] is True
        assert result["is_pinned"] is True

    def test_card_to_dict_builds_source_object(self):
        """Builds source object from card fields."""
        from local_deep_research.news.core.card_storage import SQLCardStorage

        mock_session = MagicMock()
        storage = SQLCardStorage(mock_session)

        mock_card = MagicMock()
        mock_card.id = "card-123"
        mock_card.title = "Test"
        mock_card.summary = None
        mock_card.content = None
        mock_card.url = None
        mock_card.source_name = None
        mock_card.source_type = "rss"
        mock_card.source_id = "feed-123"
        mock_card.category = None
        mock_card.tags = None
        mock_card.card_type = "news"
        mock_card.published_at = None
        mock_card.discovered_at = None
        mock_card.is_read = False
        mock_card.read_at = None
        mock_card.is_saved = False
        mock_card.saved_at = None
        mock_card.extra_data = {
            "created_from": "Import",
            "metadata": {"key": "val"},
        }

        result = storage._card_to_dict(mock_card)

        assert result["source"]["type"] == "rss"
        assert result["source"]["source_id"] == "feed-123"
        assert result["source"]["created_from"] == "Import"
        assert result["source"]["metadata"] == {"key": "val"}


class TestSQLCardStorageUpdate:
    """Extended tests for update() method."""

    def test_update_sets_saved_at_when_pinning(self):
        """Sets saved_at timestamp when pinning."""
        from local_deep_research.news.core.card_storage import SQLCardStorage

        mock_session = MagicMock()
        mock_context = MagicMock()
        mock_session.__enter__ = MagicMock(return_value=mock_context)
        mock_session.__exit__ = MagicMock(return_value=False)

        mock_card = MagicMock()
        mock_card.is_saved = False
        mock_card.saved_at = None
        mock_card.extra_data = {}
        mock_context.query.return_value.filter_by.return_value.first.return_value = mock_card

        storage = SQLCardStorage(mock_session)
        before = datetime.now(timezone.utc)
        storage.update("card-123", {"is_pinned": True})
        after = datetime.now(timezone.utc)

        assert mock_card.is_saved is True
        assert before <= mock_card.saved_at <= after

    def test_update_sets_read_at_when_last_viewed(self):
        """Sets read_at and is_read when last_viewed is set."""
        from local_deep_research.news.core.card_storage import SQLCardStorage

        mock_session = MagicMock()
        mock_context = MagicMock()
        mock_session.__enter__ = MagicMock(return_value=mock_context)
        mock_session.__exit__ = MagicMock(return_value=False)

        mock_card = MagicMock()
        mock_card.is_read = False
        mock_card.read_at = None
        mock_card.extra_data = {}
        mock_context.query.return_value.filter_by.return_value.first.return_value = mock_card

        viewed_time = datetime(2024, 1, 15, 12, 0, 0, tzinfo=timezone.utc)

        storage = SQLCardStorage(mock_session)
        storage.update("card-123", {"last_viewed": viewed_time})

        assert mock_card.is_read is True
        assert mock_card.read_at == viewed_time

    def test_update_stores_interaction_in_extra_data(self):
        """Stores interaction data in extra_data."""
        from local_deep_research.news.core.card_storage import SQLCardStorage

        mock_session = MagicMock()
        mock_context = MagicMock()
        mock_session.__enter__ = MagicMock(return_value=mock_context)
        mock_session.__exit__ = MagicMock(return_value=False)

        mock_card = MagicMock()
        mock_card.extra_data = {}
        mock_context.query.return_value.filter_by.return_value.first.return_value = mock_card

        storage = SQLCardStorage(mock_session)
        storage.update("card-123", {"interaction": {"views": 5, "votes_up": 2}})

        assert mock_card.extra_data["interaction"] == {
            "views": 5,
            "votes_up": 2,
        }


class TestSQLCardStorageEdgeCases:
    """Edge cases and boundary conditions."""

    def test_create_with_empty_extra_data(self):
        """Handles empty extra_data in create."""
        from local_deep_research.news.core.card_storage import SQLCardStorage

        mock_session = MagicMock()
        mock_context = MagicMock()
        mock_session.__enter__ = MagicMock(return_value=mock_context)
        mock_session.__exit__ = MagicMock(return_value=False)

        storage = SQLCardStorage(mock_session)

        data = {
            "id": "card-123",
            "topic": "Test",
            "extra_data": None,  # Explicitly None
        }

        with patch(
            "local_deep_research.news.core.card_storage.NewsCard"
        ) as MockNewsCard:
            mock_card = MagicMock()
            MockNewsCard.return_value = mock_card

            storage.create(data)

            # Should handle None extra_data gracefully
            call_kwargs = MockNewsCard.call_args[1]
            assert "extra_data" in call_kwargs

    def test_list_with_empty_filters(self):
        """Handles empty filters dict."""
        from local_deep_research.news.core.card_storage import SQLCardStorage

        mock_session = MagicMock()
        mock_context = MagicMock()
        mock_session.__enter__ = MagicMock(return_value=mock_context)
        mock_session.__exit__ = MagicMock(return_value=False)

        mock_query = MagicMock()
        mock_context.query.return_value = mock_query
        mock_query.order_by.return_value = mock_query
        mock_query.limit.return_value = mock_query
        mock_query.offset.return_value = mock_query
        mock_query.all.return_value = []

        storage = SQLCardStorage(mock_session)
        result = storage.list(filters={})

        assert result == []

    def test_get_by_user_empty_results(self):
        """Handles no matching cards for user."""
        from local_deep_research.news.core.card_storage import SQLCardStorage

        mock_session = MagicMock()
        mock_context = MagicMock()
        mock_session.__enter__ = MagicMock(return_value=mock_context)
        mock_session.__exit__ = MagicMock(return_value=False)

        mock_query = MagicMock()
        mock_context.query.return_value = mock_query
        mock_query.order_by.return_value = mock_query
        mock_query.limit.return_value = mock_query
        mock_query.offset.return_value = mock_query
        mock_query.all.return_value = []

        storage = SQLCardStorage(mock_session)
        result = storage.get_by_user("user-123")

        assert result == []
