"""
Tests for news/core/storage.py

Tests cover:
- BaseStorage abstract interface
- CardStorage abstract interface
- SubscriptionStorage abstract interface
- RatingStorage abstract interface
- PreferenceStorage abstract interface
- SearchHistoryStorage abstract interface
- NewsItemStorage abstract interface
- generate_id() utility method
"""

import pytest
from abc import ABC


class TestBaseStorage:
    """Tests for BaseStorage abstract interface."""

    def test_cannot_instantiate_directly(self):
        """Test that BaseStorage cannot be instantiated directly."""
        from local_deep_research.news.core.storage import BaseStorage

        with pytest.raises(TypeError):
            BaseStorage()

    def test_is_abstract_base_class(self):
        """Test that BaseStorage is an ABC."""
        from local_deep_research.news.core.storage import BaseStorage

        assert issubclass(BaseStorage, ABC)

    def test_defines_create_abstract_method(self):
        """Test that create is an abstract method."""
        from local_deep_research.news.core.storage import BaseStorage

        assert hasattr(BaseStorage, "create")
        assert (
            getattr(BaseStorage.create, "__isabstractmethod__", False) is True
        )

    def test_defines_get_abstract_method(self):
        """Test that get is an abstract method."""
        from local_deep_research.news.core.storage import BaseStorage

        assert hasattr(BaseStorage, "get")
        assert getattr(BaseStorage.get, "__isabstractmethod__", False) is True

    def test_defines_update_abstract_method(self):
        """Test that update is an abstract method."""
        from local_deep_research.news.core.storage import BaseStorage

        assert hasattr(BaseStorage, "update")
        assert (
            getattr(BaseStorage.update, "__isabstractmethod__", False) is True
        )

    def test_defines_delete_abstract_method(self):
        """Test that delete is an abstract method."""
        from local_deep_research.news.core.storage import BaseStorage

        assert hasattr(BaseStorage, "delete")
        assert (
            getattr(BaseStorage.delete, "__isabstractmethod__", False) is True
        )

    def test_defines_list_abstract_method(self):
        """Test that list is an abstract method."""
        from local_deep_research.news.core.storage import BaseStorage

        assert hasattr(BaseStorage, "list")
        assert getattr(BaseStorage.list, "__isabstractmethod__", False) is True

    def test_generate_id_is_concrete(self):
        """Test that generate_id is a concrete method."""
        from local_deep_research.news.core.storage import BaseStorage

        assert hasattr(BaseStorage, "generate_id")
        assert (
            getattr(BaseStorage.generate_id, "__isabstractmethod__", False)
            is False
        )

    def test_generate_id_returns_uuid_string(self):
        """Test that generate_id returns a valid UUID string."""
        from local_deep_research.news.core.storage import BaseStorage

        # Create a concrete implementation to test generate_id
        class ConcreteStorage(BaseStorage):
            def create(self, data):
                return "id"

            def get(self, id):
                return None

            def update(self, id, data):
                return True

            def delete(self, id):
                return True

            def list(self, filters=None, limit=100, offset=0):
                return []

        storage = ConcreteStorage()
        result = storage.generate_id()

        assert isinstance(result, str)
        assert len(result) == 36  # UUID format: 8-4-4-4-12 with dashes
        assert result.count("-") == 4

    def test_generate_id_returns_unique_values(self):
        """Test that generate_id returns unique values."""
        from local_deep_research.news.core.storage import BaseStorage

        class ConcreteStorage(BaseStorage):
            def create(self, data):
                return "id"

            def get(self, id):
                return None

            def update(self, id, data):
                return True

            def delete(self, id):
                return True

            def list(self, filters=None, limit=100, offset=0):
                return []

        storage = ConcreteStorage()
        ids = [storage.generate_id() for _ in range(100)]

        # All IDs should be unique
        assert len(set(ids)) == 100


class TestCardStorage:
    """Tests for CardStorage abstract interface."""

    def test_cannot_instantiate_directly(self):
        """Test that CardStorage cannot be instantiated directly."""
        from local_deep_research.news.core.storage import CardStorage

        with pytest.raises(TypeError):
            CardStorage()

    def test_inherits_from_base_storage(self):
        """Test that CardStorage inherits from BaseStorage."""
        from local_deep_research.news.core.storage import (
            CardStorage,
            BaseStorage,
        )

        assert issubclass(CardStorage, BaseStorage)

    def test_defines_get_by_user_abstract_method(self):
        """Test that get_by_user is an abstract method."""
        from local_deep_research.news.core.storage import CardStorage

        assert hasattr(CardStorage, "get_by_user")
        assert (
            getattr(CardStorage.get_by_user, "__isabstractmethod__", False)
            is True
        )

    def test_defines_get_latest_version_abstract_method(self):
        """Test that get_latest_version is an abstract method."""
        from local_deep_research.news.core.storage import CardStorage

        assert hasattr(CardStorage, "get_latest_version")
        assert (
            getattr(
                CardStorage.get_latest_version, "__isabstractmethod__", False
            )
            is True
        )

    def test_defines_add_version_abstract_method(self):
        """Test that add_version is an abstract method."""
        from local_deep_research.news.core.storage import CardStorage

        assert hasattr(CardStorage, "add_version")
        assert (
            getattr(CardStorage.add_version, "__isabstractmethod__", False)
            is True
        )

    def test_defines_update_latest_info_abstract_method(self):
        """Test that update_latest_info is an abstract method."""
        from local_deep_research.news.core.storage import CardStorage

        assert hasattr(CardStorage, "update_latest_info")
        assert (
            getattr(
                CardStorage.update_latest_info, "__isabstractmethod__", False
            )
            is True
        )

    def test_defines_archive_card_abstract_method(self):
        """Test that archive_card is an abstract method."""
        from local_deep_research.news.core.storage import CardStorage

        assert hasattr(CardStorage, "archive_card")
        assert (
            getattr(CardStorage.archive_card, "__isabstractmethod__", False)
            is True
        )

    def test_defines_pin_card_abstract_method(self):
        """Test that pin_card is an abstract method."""
        from local_deep_research.news.core.storage import CardStorage

        assert hasattr(CardStorage, "pin_card")
        assert (
            getattr(CardStorage.pin_card, "__isabstractmethod__", False) is True
        )


class TestSubscriptionStorage:
    """Tests for SubscriptionStorage abstract interface."""

    def test_cannot_instantiate_directly(self):
        """Test that SubscriptionStorage cannot be instantiated directly."""
        from local_deep_research.news.core.storage import SubscriptionStorage

        with pytest.raises(TypeError):
            SubscriptionStorage()

    def test_inherits_from_base_storage(self):
        """Test that SubscriptionStorage inherits from BaseStorage."""
        from local_deep_research.news.core.storage import (
            SubscriptionStorage,
            BaseStorage,
        )

        assert issubclass(SubscriptionStorage, BaseStorage)

    def test_defines_get_active_subscriptions_abstract_method(self):
        """Test that get_active_subscriptions is an abstract method."""
        from local_deep_research.news.core.storage import SubscriptionStorage

        assert hasattr(SubscriptionStorage, "get_active_subscriptions")
        assert (
            getattr(
                SubscriptionStorage.get_active_subscriptions,
                "__isabstractmethod__",
                False,
            )
            is True
        )

    def test_defines_get_due_subscriptions_abstract_method(self):
        """Test that get_due_subscriptions is an abstract method."""
        from local_deep_research.news.core.storage import SubscriptionStorage

        assert hasattr(SubscriptionStorage, "get_due_subscriptions")
        assert (
            getattr(
                SubscriptionStorage.get_due_subscriptions,
                "__isabstractmethod__",
                False,
            )
            is True
        )

    def test_defines_update_refresh_time_abstract_method(self):
        """Test that update_refresh_time is an abstract method."""
        from local_deep_research.news.core.storage import SubscriptionStorage

        assert hasattr(SubscriptionStorage, "update_refresh_time")
        assert (
            getattr(
                SubscriptionStorage.update_refresh_time,
                "__isabstractmethod__",
                False,
            )
            is True
        )

    def test_defines_increment_stats_abstract_method(self):
        """Test that increment_stats is an abstract method."""
        from local_deep_research.news.core.storage import SubscriptionStorage

        assert hasattr(SubscriptionStorage, "increment_stats")
        assert (
            getattr(
                SubscriptionStorage.increment_stats,
                "__isabstractmethod__",
                False,
            )
            is True
        )

    def test_defines_pause_subscription_abstract_method(self):
        """Test that pause_subscription is an abstract method."""
        from local_deep_research.news.core.storage import SubscriptionStorage

        assert hasattr(SubscriptionStorage, "pause_subscription")
        assert (
            getattr(
                SubscriptionStorage.pause_subscription,
                "__isabstractmethod__",
                False,
            )
            is True
        )

    def test_defines_resume_subscription_abstract_method(self):
        """Test that resume_subscription is an abstract method."""
        from local_deep_research.news.core.storage import SubscriptionStorage

        assert hasattr(SubscriptionStorage, "resume_subscription")
        assert (
            getattr(
                SubscriptionStorage.resume_subscription,
                "__isabstractmethod__",
                False,
            )
            is True
        )

    def test_defines_expire_subscription_abstract_method(self):
        """Test that expire_subscription is an abstract method."""
        from local_deep_research.news.core.storage import SubscriptionStorage

        assert hasattr(SubscriptionStorage, "expire_subscription")
        assert (
            getattr(
                SubscriptionStorage.expire_subscription,
                "__isabstractmethod__",
                False,
            )
            is True
        )


class TestRatingStorage:
    """Tests for RatingStorage abstract interface."""

    def test_cannot_instantiate_directly(self):
        """Test that RatingStorage cannot be instantiated directly."""
        from local_deep_research.news.core.storage import RatingStorage

        with pytest.raises(TypeError):
            RatingStorage()

    def test_inherits_from_base_storage(self):
        """Test that RatingStorage inherits from BaseStorage."""
        from local_deep_research.news.core.storage import (
            RatingStorage,
            BaseStorage,
        )

        assert issubclass(RatingStorage, BaseStorage)

    def test_defines_get_user_rating_abstract_method(self):
        """Test that get_user_rating is an abstract method."""
        from local_deep_research.news.core.storage import RatingStorage

        assert hasattr(RatingStorage, "get_user_rating")
        assert (
            getattr(
                RatingStorage.get_user_rating, "__isabstractmethod__", False
            )
            is True
        )

    def test_defines_upsert_rating_abstract_method(self):
        """Test that upsert_rating is an abstract method."""
        from local_deep_research.news.core.storage import RatingStorage

        assert hasattr(RatingStorage, "upsert_rating")
        assert (
            getattr(RatingStorage.upsert_rating, "__isabstractmethod__", False)
            is True
        )

    def test_defines_get_ratings_summary_abstract_method(self):
        """Test that get_ratings_summary is an abstract method."""
        from local_deep_research.news.core.storage import RatingStorage

        assert hasattr(RatingStorage, "get_ratings_summary")
        assert (
            getattr(
                RatingStorage.get_ratings_summary, "__isabstractmethod__", False
            )
            is True
        )

    def test_defines_get_user_ratings_abstract_method(self):
        """Test that get_user_ratings is an abstract method."""
        from local_deep_research.news.core.storage import RatingStorage

        assert hasattr(RatingStorage, "get_user_ratings")
        assert (
            getattr(
                RatingStorage.get_user_ratings, "__isabstractmethod__", False
            )
            is True
        )


class TestPreferenceStorage:
    """Tests for PreferenceStorage abstract interface."""

    def test_cannot_instantiate_directly(self):
        """Test that PreferenceStorage cannot be instantiated directly."""
        from local_deep_research.news.core.storage import PreferenceStorage

        with pytest.raises(TypeError):
            PreferenceStorage()

    def test_inherits_from_base_storage(self):
        """Test that PreferenceStorage inherits from BaseStorage."""
        from local_deep_research.news.core.storage import (
            PreferenceStorage,
            BaseStorage,
        )

        assert issubclass(PreferenceStorage, BaseStorage)

    def test_defines_get_user_preferences_abstract_method(self):
        """Test that get_user_preferences is an abstract method."""
        from local_deep_research.news.core.storage import PreferenceStorage

        assert hasattr(PreferenceStorage, "get_user_preferences")
        assert (
            getattr(
                PreferenceStorage.get_user_preferences,
                "__isabstractmethod__",
                False,
            )
            is True
        )

    def test_defines_upsert_preferences_abstract_method(self):
        """Test that upsert_preferences is an abstract method."""
        from local_deep_research.news.core.storage import PreferenceStorage

        assert hasattr(PreferenceStorage, "upsert_preferences")
        assert (
            getattr(
                PreferenceStorage.upsert_preferences,
                "__isabstractmethod__",
                False,
            )
            is True
        )

    def test_defines_add_liked_item_abstract_method(self):
        """Test that add_liked_item is an abstract method."""
        from local_deep_research.news.core.storage import PreferenceStorage

        assert hasattr(PreferenceStorage, "add_liked_item")
        assert (
            getattr(
                PreferenceStorage.add_liked_item, "__isabstractmethod__", False
            )
            is True
        )

    def test_defines_add_disliked_item_abstract_method(self):
        """Test that add_disliked_item is an abstract method."""
        from local_deep_research.news.core.storage import PreferenceStorage

        assert hasattr(PreferenceStorage, "add_disliked_item")
        assert (
            getattr(
                PreferenceStorage.add_disliked_item,
                "__isabstractmethod__",
                False,
            )
            is True
        )

    def test_defines_update_preference_embedding_abstract_method(self):
        """Test that update_preference_embedding is an abstract method."""
        from local_deep_research.news.core.storage import PreferenceStorage

        assert hasattr(PreferenceStorage, "update_preference_embedding")
        assert (
            getattr(
                PreferenceStorage.update_preference_embedding,
                "__isabstractmethod__",
                False,
            )
            is True
        )


class TestSearchHistoryStorage:
    """Tests for SearchHistoryStorage abstract interface."""

    def test_cannot_instantiate_directly(self):
        """Test that SearchHistoryStorage cannot be instantiated directly."""
        from local_deep_research.news.core.storage import SearchHistoryStorage

        with pytest.raises(TypeError):
            SearchHistoryStorage()

    def test_inherits_from_base_storage(self):
        """Test that SearchHistoryStorage inherits from BaseStorage."""
        from local_deep_research.news.core.storage import (
            SearchHistoryStorage,
            BaseStorage,
        )

        assert issubclass(SearchHistoryStorage, BaseStorage)

    def test_defines_record_search_abstract_method(self):
        """Test that record_search is an abstract method."""
        from local_deep_research.news.core.storage import SearchHistoryStorage

        assert hasattr(SearchHistoryStorage, "record_search")
        assert (
            getattr(
                SearchHistoryStorage.record_search,
                "__isabstractmethod__",
                False,
            )
            is True
        )

    def test_defines_get_recent_searches_abstract_method(self):
        """Test that get_recent_searches is an abstract method."""
        from local_deep_research.news.core.storage import SearchHistoryStorage

        assert hasattr(SearchHistoryStorage, "get_recent_searches")
        assert (
            getattr(
                SearchHistoryStorage.get_recent_searches,
                "__isabstractmethod__",
                False,
            )
            is True
        )

    def test_defines_link_to_subscription_abstract_method(self):
        """Test that link_to_subscription is an abstract method."""
        from local_deep_research.news.core.storage import SearchHistoryStorage

        assert hasattr(SearchHistoryStorage, "link_to_subscription")
        assert (
            getattr(
                SearchHistoryStorage.link_to_subscription,
                "__isabstractmethod__",
                False,
            )
            is True
        )

    def test_defines_get_popular_searches_abstract_method(self):
        """Test that get_popular_searches is an abstract method."""
        from local_deep_research.news.core.storage import SearchHistoryStorage

        assert hasattr(SearchHistoryStorage, "get_popular_searches")
        assert (
            getattr(
                SearchHistoryStorage.get_popular_searches,
                "__isabstractmethod__",
                False,
            )
            is True
        )


class TestNewsItemStorage:
    """Tests for NewsItemStorage abstract interface."""

    def test_cannot_instantiate_directly(self):
        """Test that NewsItemStorage cannot be instantiated directly."""
        from local_deep_research.news.core.storage import NewsItemStorage

        with pytest.raises(TypeError):
            NewsItemStorage()

    def test_inherits_from_base_storage(self):
        """Test that NewsItemStorage inherits from BaseStorage."""
        from local_deep_research.news.core.storage import (
            NewsItemStorage,
            BaseStorage,
        )

        assert issubclass(NewsItemStorage, BaseStorage)

    def test_defines_get_recent_abstract_method(self):
        """Test that get_recent is an abstract method."""
        from local_deep_research.news.core.storage import NewsItemStorage

        assert hasattr(NewsItemStorage, "get_recent")
        assert (
            getattr(NewsItemStorage.get_recent, "__isabstractmethod__", False)
            is True
        )

    def test_defines_store_batch_abstract_method(self):
        """Test that store_batch is an abstract method."""
        from local_deep_research.news.core.storage import NewsItemStorage

        assert hasattr(NewsItemStorage, "store_batch")
        assert (
            getattr(NewsItemStorage.store_batch, "__isabstractmethod__", False)
            is True
        )

    def test_defines_update_votes_abstract_method(self):
        """Test that update_votes is an abstract method."""
        from local_deep_research.news.core.storage import NewsItemStorage

        assert hasattr(NewsItemStorage, "update_votes")
        assert (
            getattr(NewsItemStorage.update_votes, "__isabstractmethod__", False)
            is True
        )

    def test_defines_get_by_category_abstract_method(self):
        """Test that get_by_category is an abstract method."""
        from local_deep_research.news.core.storage import NewsItemStorage

        assert hasattr(NewsItemStorage, "get_by_category")
        assert (
            getattr(
                NewsItemStorage.get_by_category, "__isabstractmethod__", False
            )
            is True
        )

    def test_defines_cleanup_old_items_abstract_method(self):
        """Test that cleanup_old_items is an abstract method."""
        from local_deep_research.news.core.storage import NewsItemStorage

        assert hasattr(NewsItemStorage, "cleanup_old_items")
        assert (
            getattr(
                NewsItemStorage.cleanup_old_items, "__isabstractmethod__", False
            )
            is True
        )


class TestConcreteImplementation:
    """Tests for concrete implementation of BaseStorage."""

    def test_can_create_concrete_subclass(self):
        """Test that we can create a concrete subclass of BaseStorage."""
        from local_deep_research.news.core.storage import BaseStorage

        class ConcreteStorage(BaseStorage):
            def create(self, data):
                return self.generate_id()

            def get(self, id):
                return {"id": id}

            def update(self, id, data):
                return True

            def delete(self, id):
                return True

            def list(self, filters=None, limit=100, offset=0):
                return []

        storage = ConcreteStorage()

        # Should be able to call methods
        created_id = storage.create({})
        assert isinstance(created_id, str)

        retrieved = storage.get("test-id")
        assert retrieved == {"id": "test-id"}

        updated = storage.update("test-id", {"name": "Test"})
        assert updated is True

        deleted = storage.delete("test-id")
        assert deleted is True

        listed = storage.list()
        assert listed == []

    def test_concrete_subclass_must_implement_all_methods(self):
        """Test that concrete subclass must implement all abstract methods."""
        from local_deep_research.news.core.storage import BaseStorage

        # Missing some methods
        class IncompleteStorage(BaseStorage):
            def create(self, data):
                return "id"

            def get(self, id):
                return None

            # Missing update, delete, list

        with pytest.raises(TypeError):
            IncompleteStorage()


class TestModuleImports:
    """Tests for module imports."""

    def test_all_classes_importable(self):
        """Test that all storage classes can be imported."""
        from local_deep_research.news.core.storage import (
            BaseStorage,
            CardStorage,
            SubscriptionStorage,
            RatingStorage,
            PreferenceStorage,
            SearchHistoryStorage,
            NewsItemStorage,
        )

        assert BaseStorage is not None
        assert CardStorage is not None
        assert SubscriptionStorage is not None
        assert RatingStorage is not None
        assert PreferenceStorage is not None
        assert SearchHistoryStorage is not None
        assert NewsItemStorage is not None
