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

Tests cover:
- BaseStorage abstract interface
- CardStorage abstract interface
- SubscriptionStorage abstract interface
- RatingStorage abstract interface
- PreferenceStorage 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 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,
        )

        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
