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

Tests cover:
- BaseStorage abstract class and methods
- CardStorage interface
- RatingStorage interface
- PreferenceStorage interface
- ID generation
"""

import pytest
from abc import ABC


class TestBaseStorageAbstract:
    """Tests for BaseStorage abstract class."""

    def test_is_abstract_class(self):
        """BaseStorage is an abstract class."""
        from local_deep_research.news.core.storage import BaseStorage

        assert issubclass(BaseStorage, ABC)

    def test_cannot_instantiate_directly(self):
        """Cannot directly instantiate BaseStorage."""
        from local_deep_research.news.core.storage import BaseStorage

        with pytest.raises(TypeError):
            BaseStorage()

    def test_has_create_abstract_method(self):
        """Has abstract create method."""
        from local_deep_research.news.core.storage import BaseStorage

        assert hasattr(BaseStorage, "create")

    def test_has_get_abstract_method(self):
        """Has abstract get method."""
        from local_deep_research.news.core.storage import BaseStorage

        assert hasattr(BaseStorage, "get")

    def test_has_update_abstract_method(self):
        """Has abstract update method."""
        from local_deep_research.news.core.storage import BaseStorage

        assert hasattr(BaseStorage, "update")

    def test_has_delete_abstract_method(self):
        """Has abstract delete method."""
        from local_deep_research.news.core.storage import BaseStorage

        assert hasattr(BaseStorage, "delete")

    def test_has_list_abstract_method(self):
        """Has abstract list method."""
        from local_deep_research.news.core.storage import BaseStorage

        assert hasattr(BaseStorage, "list")


class ConcreteBaseStorage:
    """Factory for creating concrete base storage for testing."""

    @staticmethod
    def create():
        """Create a concrete base storage for testing."""
        from local_deep_research.news.core.storage import BaseStorage

        class TestStorage(BaseStorage):
            def __init__(self):
                self.data = {}

            def create(self, data):
                id = self.generate_id()
                self.data[id] = data
                return id

            def get(self, id):
                return self.data.get(id)

            def update(self, id, data):
                if id in self.data:
                    self.data[id] = data
                    return True
                return False

            def delete(self, id):
                if id in self.data:
                    del self.data[id]
                    return True
                return False

            def list(self, filters=None, limit=100, offset=0):
                items = list(self.data.values())
                return items[offset : offset + limit]

        return TestStorage()


class TestBaseStorageGenerateId:
    """Tests for BaseStorage.generate_id() method."""

    def test_returns_string(self):
        """generate_id returns a string."""
        storage = ConcreteBaseStorage.create()

        result = storage.generate_id()

        assert isinstance(result, str)

    def test_returns_uuid_format(self):
        """generate_id returns UUID format (36 chars with dashes)."""
        storage = ConcreteBaseStorage.create()

        result = storage.generate_id()

        assert len(result) == 36
        assert result.count("-") == 4

    def test_generates_unique_ids(self):
        """generate_id generates unique IDs."""
        storage = ConcreteBaseStorage.create()

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

        assert len(set(ids)) == 100  # All unique


class TestBaseStorageCRUD:
    """Tests for basic CRUD operations."""

    def test_create_returns_id(self):
        """create returns the generated ID."""
        storage = ConcreteBaseStorage.create()

        result = storage.create({"name": "test"})

        assert isinstance(result, str)
        assert len(result) == 36

    def test_get_returns_data(self):
        """get returns the stored data."""
        storage = ConcreteBaseStorage.create()
        id = storage.create({"name": "test"})

        result = storage.get(id)

        assert result == {"name": "test"}

    def test_get_returns_none_for_nonexistent(self):
        """get returns None for nonexistent ID."""
        storage = ConcreteBaseStorage.create()

        result = storage.get("nonexistent-id")

        assert result is None

    def test_update_returns_true_on_success(self):
        """update returns True on success."""
        storage = ConcreteBaseStorage.create()
        id = storage.create({"name": "old"})

        result = storage.update(id, {"name": "new"})

        assert result is True
        assert storage.get(id) == {"name": "new"}

    def test_update_returns_false_for_nonexistent(self):
        """update returns False for nonexistent ID."""
        storage = ConcreteBaseStorage.create()

        result = storage.update("nonexistent-id", {"name": "new"})

        assert result is False

    def test_delete_returns_true_on_success(self):
        """delete returns True on success."""
        storage = ConcreteBaseStorage.create()
        id = storage.create({"name": "test"})

        result = storage.delete(id)

        assert result is True
        assert storage.get(id) is None

    def test_delete_returns_false_for_nonexistent(self):
        """delete returns False for nonexistent ID."""
        storage = ConcreteBaseStorage.create()

        result = storage.delete("nonexistent-id")

        assert result is False

    def test_list_returns_all_items(self):
        """list returns all stored items."""
        storage = ConcreteBaseStorage.create()
        storage.create({"name": "item1"})
        storage.create({"name": "item2"})

        result = storage.list()

        assert len(result) == 2

    def test_list_respects_limit(self):
        """list respects limit parameter."""
        storage = ConcreteBaseStorage.create()
        for i in range(10):
            storage.create({"name": f"item{i}"})

        result = storage.list(limit=3)

        assert len(result) == 3

    def test_list_respects_offset(self):
        """list respects offset parameter."""
        storage = ConcreteBaseStorage.create()
        for i in range(10):
            storage.create({"name": f"item{i}"})

        result = storage.list(offset=5, limit=100)

        assert len(result) == 5


class TestCardStorageInterface:
    """Tests for CardStorage interface."""

    def test_is_abstract_class(self):
        """CardStorage is an abstract class."""
        from local_deep_research.news.core.storage import CardStorage

        assert issubclass(CardStorage, ABC)

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

        assert issubclass(CardStorage, BaseStorage)

    def test_has_get_by_user_method(self):
        """Has get_by_user abstract method."""
        from local_deep_research.news.core.storage import CardStorage

        assert hasattr(CardStorage, "get_by_user")

    def test_has_get_latest_version_method(self):
        """Has get_latest_version abstract method."""
        from local_deep_research.news.core.storage import CardStorage

        assert hasattr(CardStorage, "get_latest_version")

    def test_has_add_version_method(self):
        """Has add_version abstract method."""
        from local_deep_research.news.core.storage import CardStorage

        assert hasattr(CardStorage, "add_version")

    def test_has_update_latest_info_method(self):
        """Has update_latest_info abstract method."""
        from local_deep_research.news.core.storage import CardStorage

        assert hasattr(CardStorage, "update_latest_info")

    def test_has_archive_card_method(self):
        """Has archive_card abstract method."""
        from local_deep_research.news.core.storage import CardStorage

        assert hasattr(CardStorage, "archive_card")

    def test_has_pin_card_method(self):
        """Has pin_card abstract method."""
        from local_deep_research.news.core.storage import CardStorage

        assert hasattr(CardStorage, "pin_card")


class TestRatingStorageInterface:
    """Tests for RatingStorage interface."""

    def test_is_abstract_class(self):
        """RatingStorage is an abstract class."""
        from local_deep_research.news.core.storage import RatingStorage

        assert issubclass(RatingStorage, ABC)

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

        assert issubclass(RatingStorage, BaseStorage)

    def test_has_get_user_rating_method(self):
        """Has get_user_rating abstract method."""
        from local_deep_research.news.core.storage import RatingStorage

        assert hasattr(RatingStorage, "get_user_rating")

    def test_has_upsert_rating_method(self):
        """Has upsert_rating abstract method."""
        from local_deep_research.news.core.storage import RatingStorage

        assert hasattr(RatingStorage, "upsert_rating")

    def test_has_get_ratings_summary_method(self):
        """Has get_ratings_summary abstract method."""
        from local_deep_research.news.core.storage import RatingStorage

        assert hasattr(RatingStorage, "get_ratings_summary")

    def test_has_get_user_ratings_method(self):
        """Has get_user_ratings abstract method."""
        from local_deep_research.news.core.storage import RatingStorage

        assert hasattr(RatingStorage, "get_user_ratings")


class TestPreferenceStorageInterface:
    """Tests for PreferenceStorage interface."""

    def test_is_abstract_class(self):
        """PreferenceStorage is an abstract class."""
        from local_deep_research.news.core.storage import PreferenceStorage

        assert issubclass(PreferenceStorage, ABC)

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

        assert issubclass(PreferenceStorage, BaseStorage)

    def test_has_get_user_preferences_method(self):
        """Has get_user_preferences abstract method."""
        from local_deep_research.news.core.storage import PreferenceStorage

        assert hasattr(PreferenceStorage, "get_user_preferences")

    def test_has_upsert_preferences_method(self):
        """Has upsert_preferences abstract method."""
        from local_deep_research.news.core.storage import PreferenceStorage

        assert hasattr(PreferenceStorage, "upsert_preferences")

    def test_has_add_liked_item_method(self):
        """Has add_liked_item abstract method."""
        from local_deep_research.news.core.storage import PreferenceStorage

        assert hasattr(PreferenceStorage, "add_liked_item")

    def test_has_add_disliked_item_method(self):
        """Has add_disliked_item abstract method."""
        from local_deep_research.news.core.storage import PreferenceStorage

        assert hasattr(PreferenceStorage, "add_disliked_item")

    def test_has_update_preference_embedding_method(self):
        """Has update_preference_embedding abstract method."""
        from local_deep_research.news.core.storage import PreferenceStorage

        assert hasattr(PreferenceStorage, "update_preference_embedding")


class TestStorageInheritanceChain:
    """Tests for proper inheritance chain."""

    def test_all_storages_inherit_from_base(self):
        """All storage classes inherit from BaseStorage."""
        from local_deep_research.news.core.storage import (
            BaseStorage,
            CardStorage,
            RatingStorage,
            PreferenceStorage,
        )

        storage_classes = [
            CardStorage,
            RatingStorage,
            PreferenceStorage,
        ]

        for storage_class in storage_classes:
            assert issubclass(storage_class, BaseStorage)

    def test_all_storages_have_generate_id(self):
        """All storage classes inherit generate_id from BaseStorage."""
        from local_deep_research.news.core.storage import (
            CardStorage,
            RatingStorage,
            PreferenceStorage,
        )

        storage_classes = [
            CardStorage,
            RatingStorage,
            PreferenceStorage,
        ]

        for storage_class in storage_classes:
            assert hasattr(storage_class, "generate_id")


class TestConcreteImplementationRequirements:
    """Tests verifying that concrete implementations must implement all methods."""

    def test_missing_create_raises_error(self):
        """Concrete class missing create raises TypeError."""
        from local_deep_research.news.core.storage import BaseStorage

        class IncompleteStorage(BaseStorage):
            def get(self, id):
                pass

            def update(self, id, data):
                pass

            def delete(self, id):
                pass

            def list(self, filters=None, limit=100, offset=0):
                pass

        with pytest.raises(TypeError):
            IncompleteStorage()

    def test_missing_get_raises_error(self):
        """Concrete class missing get raises TypeError."""
        from local_deep_research.news.core.storage import BaseStorage

        class IncompleteStorage(BaseStorage):
            def create(self, data):
                pass

            def update(self, id, data):
                pass

            def delete(self, id):
                pass

            def list(self, filters=None, limit=100, offset=0):
                pass

        with pytest.raises(TypeError):
            IncompleteStorage()

    def test_card_storage_requires_all_methods(self):
        """CardStorage requires all abstract methods to be implemented."""
        from local_deep_research.news.core.storage import CardStorage

        class IncompleteCardStorage(CardStorage):
            # Only implementing base methods, not card-specific ones
            def create(self, data):
                pass

            def get(self, id):
                pass

            def update(self, id, data):
                pass

            def delete(self, id):
                pass

            def list(self, filters=None, limit=100, offset=0):
                pass

        with pytest.raises(TypeError):
            IncompleteCardStorage()


class TestStorageEdgeCases:
    """Edge case tests for storage operations."""

    def test_empty_data_create(self):
        """Can create with empty data."""
        storage = ConcreteBaseStorage.create()

        id = storage.create({})

        assert storage.get(id) == {}

    def test_none_data_create(self):
        """Can create with None-like values in data."""
        storage = ConcreteBaseStorage.create()

        id = storage.create({"value": None, "empty": ""})

        result = storage.get(id)
        assert result["value"] is None
        assert result["empty"] == ""

    def test_unicode_in_data(self):
        """Handles unicode in data."""
        storage = ConcreteBaseStorage.create()

        id = storage.create({"name": "日本語テスト"})

        result = storage.get(id)
        assert result["name"] == "日本語テスト"

    def test_nested_data(self):
        """Handles nested data structures."""
        storage = ConcreteBaseStorage.create()
        nested_data = {
            "level1": {"level2": {"level3": "deep_value"}},
            "array": [1, 2, {"nested": True}],
        }

        id = storage.create(nested_data)

        result = storage.get(id)
        assert result["level1"]["level2"]["level3"] == "deep_value"

    def test_list_with_zero_limit(self):
        """list with zero limit returns empty."""
        storage = ConcreteBaseStorage.create()
        storage.create({"name": "item"})

        result = storage.list(limit=0)

        assert len(result) == 0

    def test_list_with_large_offset(self):
        """list with large offset returns empty."""
        storage = ConcreteBaseStorage.create()
        storage.create({"name": "item"})

        result = storage.list(offset=1000)

        assert len(result) == 0
