"""
Deep behavioral tests for storage interfaces and base storage patterns.
Tests BaseStorage, CardStorage, RatingStorage,
and PreferenceStorage interfaces.
"""

import uuid

import pytest

from local_deep_research.news.core.storage import (
    BaseStorage,
    CardStorage,
    RatingStorage,
    PreferenceStorage,
)


# --- Concrete implementations for testing ---


class ConcreteBaseStorage(BaseStorage):
    """Concrete implementation of BaseStorage for testing."""

    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].update(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]


# --- BaseStorage.generate_id ---


class TestBaseStorageGenerateId:
    """Tests for BaseStorage.generate_id."""

    def test_returns_string(self):
        storage = ConcreteBaseStorage()
        result = storage.generate_id()
        assert isinstance(result, str)

    def test_is_valid_uuid(self):
        storage = ConcreteBaseStorage()
        result = storage.generate_id()
        # Should not raise
        uuid.UUID(result)

    def test_unique_ids(self):
        storage = ConcreteBaseStorage()
        ids = {storage.generate_id() for _ in range(100)}
        assert len(ids) == 100

    def test_uuid4_format(self):
        storage = ConcreteBaseStorage()
        result = storage.generate_id()
        parsed = uuid.UUID(result)
        assert parsed.version == 4

    def test_non_empty(self):
        storage = ConcreteBaseStorage()
        result = storage.generate_id()
        assert len(result) > 0

    def test_contains_hyphens(self):
        storage = ConcreteBaseStorage()
        result = storage.generate_id()
        assert "-" in result


# --- Concrete BaseStorage CRUD ---


class TestConcreteStorageCrud:
    """Tests for the concrete storage implementation CRUD."""

    def test_create_returns_id(self):
        storage = ConcreteBaseStorage()
        id = storage.create({"name": "test"})
        assert isinstance(id, str)

    def test_get_after_create(self):
        storage = ConcreteBaseStorage()
        id = storage.create({"name": "test"})
        result = storage.get(id)
        assert result["name"] == "test"

    def test_get_nonexistent_returns_none(self):
        storage = ConcreteBaseStorage()
        assert storage.get("nonexistent") is None

    def test_update_returns_true(self):
        storage = ConcreteBaseStorage()
        id = storage.create({"name": "test"})
        assert storage.update(id, {"name": "updated"}) is True

    def test_update_nonexistent_returns_false(self):
        storage = ConcreteBaseStorage()
        assert storage.update("nonexistent", {"name": "test"}) is False

    def test_update_modifies_data(self):
        storage = ConcreteBaseStorage()
        id = storage.create({"name": "test"})
        storage.update(id, {"name": "updated"})
        assert storage.get(id)["name"] == "updated"

    def test_delete_returns_true(self):
        storage = ConcreteBaseStorage()
        id = storage.create({"name": "test"})
        assert storage.delete(id) is True

    def test_delete_nonexistent_returns_false(self):
        storage = ConcreteBaseStorage()
        assert storage.delete("nonexistent") is False

    def test_delete_removes_data(self):
        storage = ConcreteBaseStorage()
        id = storage.create({"name": "test"})
        storage.delete(id)
        assert storage.get(id) is None

    def test_list_empty(self):
        storage = ConcreteBaseStorage()
        assert storage.list() == []

    def test_list_after_create(self):
        storage = ConcreteBaseStorage()
        storage.create({"name": "a"})
        storage.create({"name": "b"})
        result = storage.list()
        assert len(result) == 2

    def test_list_with_limit(self):
        storage = ConcreteBaseStorage()
        for i in range(10):
            storage.create({"name": f"item{i}"})
        result = storage.list(limit=5)
        assert len(result) == 5

    def test_list_with_offset(self):
        storage = ConcreteBaseStorage()
        for i in range(10):
            storage.create({"name": f"item{i}"})
        offset_items = storage.list(offset=5)
        assert len(offset_items) == 5


# --- Abstract interface enforcement ---


class TestBaseStorageAbstract:
    """Tests that BaseStorage cannot be instantiated directly."""

    def test_cannot_instantiate(self):
        with pytest.raises(TypeError):
            BaseStorage()


class TestCardStorageAbstract:
    """Tests that CardStorage enforces abstract methods."""

    def test_cannot_instantiate(self):
        with pytest.raises(TypeError):
            CardStorage()

    def test_required_methods(self):
        """CardStorage requires get_by_user, get_latest_version, etc."""
        # These should be in the abstract methods
        abstract_methods = CardStorage.__abstractmethods__
        assert "get_by_user" in abstract_methods
        assert "get_latest_version" in abstract_methods
        assert "add_version" in abstract_methods
        assert "update_latest_info" in abstract_methods
        assert "archive_card" in abstract_methods
        assert "pin_card" in abstract_methods


class TestRatingStorageAbstract:
    """Tests that RatingStorage enforces abstract methods."""

    def test_cannot_instantiate(self):
        with pytest.raises(TypeError):
            RatingStorage()

    def test_required_methods(self):
        abstract_methods = RatingStorage.__abstractmethods__
        assert "get_user_rating" in abstract_methods
        assert "upsert_rating" in abstract_methods
        assert "get_ratings_summary" in abstract_methods
        assert "get_user_ratings" in abstract_methods


class TestPreferenceStorageAbstract:
    """Tests that PreferenceStorage enforces abstract methods."""

    def test_cannot_instantiate(self):
        with pytest.raises(TypeError):
            PreferenceStorage()

    def test_required_methods(self):
        abstract_methods = PreferenceStorage.__abstractmethods__
        assert "get_user_preferences" in abstract_methods
        assert "upsert_preferences" in abstract_methods
        assert "add_liked_item" in abstract_methods
        assert "add_disliked_item" in abstract_methods
        assert "update_preference_embedding" in abstract_methods


# --- Interface inheritance chain ---


class TestStorageInheritance:
    """Tests for the storage interface inheritance chain."""

    def test_card_storage_extends_base(self):
        assert issubclass(CardStorage, BaseStorage)

    def test_rating_storage_extends_base(self):
        assert issubclass(RatingStorage, BaseStorage)

    def test_preference_storage_extends_base(self):
        assert issubclass(PreferenceStorage, BaseStorage)

    def test_all_share_generate_id(self):
        """All storage interfaces should inherit generate_id from BaseStorage."""
        assert hasattr(CardStorage, "generate_id")
        assert hasattr(RatingStorage, "generate_id")
        assert hasattr(PreferenceStorage, "generate_id")

    def test_base_storage_is_abc(self):
        """BaseStorage should be an ABC."""
        from abc import ABC

        assert issubclass(BaseStorage, ABC)


# --- Default parameter values ---


class TestStorageDefaultParameters:
    """Tests for default parameter values in storage interfaces."""

    def test_list_default_limit_100(self):
        """list() should default to limit=100."""
        import inspect

        sig = inspect.signature(BaseStorage.list)
        assert sig.parameters["limit"].default == 100

    def test_list_default_offset_0(self):
        """list() should default to offset=0."""
        import inspect

        sig = inspect.signature(BaseStorage.list)
        assert sig.parameters["offset"].default == 0

    def test_list_default_filters_none(self):
        """list() should default to filters=None."""
        import inspect

        sig = inspect.signature(BaseStorage.list)
        assert sig.parameters["filters"].default is None

    def test_get_by_user_default_limit_50(self):
        import inspect

        sig = inspect.signature(CardStorage.get_by_user)
        assert sig.parameters["limit"].default == 50

    def test_get_by_user_default_offset_0(self):
        import inspect

        sig = inspect.signature(CardStorage.get_by_user)
        assert sig.parameters["offset"].default == 0

    def test_pin_card_default_pinned_true(self):
        import inspect

        sig = inspect.signature(CardStorage.pin_card)
        assert sig.parameters["pinned"].default is True

    def test_upsert_rating_default_item_type(self):
        import inspect

        sig = inspect.signature(RatingStorage.upsert_rating)
        assert sig.parameters["item_type"].default == "card"

    def test_get_ratings_summary_default_item_type(self):
        import inspect

        sig = inspect.signature(RatingStorage.get_ratings_summary)
        assert sig.parameters["item_type"].default == "card"

    def test_get_user_ratings_default_limit(self):
        import inspect

        sig = inspect.signature(RatingStorage.get_user_ratings)
        assert sig.parameters["limit"].default == 100
