"""Coverage tests for storage/base.py targeting ~5 missing statements.

Uncovered functions/branches:
- ReportStorage abstract methods (save_report, get_report, etc.)
- Verifying ABC contract
- Partial implementation raises TypeError
- Abstractmethods set matches exactly 5 methods
- save_report metadata=None default handling
- report_exists user isolation
"""

import pytest

from local_deep_research.storage.base import ReportStorage

MODULE = "local_deep_research.storage.base"


class ConcreteStorage(ReportStorage):
    """Minimal concrete implementation for testing ABC contract."""

    def __init__(self):
        self._store = {}

    def save_report(self, research_id, content, metadata=None, username=None):
        self._store[research_id] = {
            "content": content,
            "metadata": metadata or {},
        }
        return True

    def get_report(self, research_id, username=None):
        entry = self._store.get(research_id)
        return entry["content"] if entry else None

    def get_report_with_metadata(self, research_id, username=None):
        return self._store.get(research_id)

    def delete_report(self, research_id, username=None):
        if research_id in self._store:
            del self._store[research_id]
            return True
        return False

    def list_reports(self, username=None):
        return []

    def report_exists(self, research_id, username=None):
        return research_id in self._store


class TestReportStorageABC:
    """Tests for the abstract base class contract."""

    def test_cannot_instantiate_abc_directly(self):
        """ReportStorage cannot be instantiated without implementing all methods."""
        with pytest.raises(TypeError):
            ReportStorage()

    def test_concrete_storage_works(self):
        """Concrete implementation round-trips data."""
        s = ConcreteStorage()
        assert s.save_report("r1", "# Report") is True
        assert s.get_report("r1") == "# Report"
        assert s.report_exists("r1") is True

    def test_get_report_missing(self):
        """get_report returns None for missing report."""
        s = ConcreteStorage()
        assert s.get_report("missing") is None

    def test_delete_report(self):
        """delete_report removes the report."""
        s = ConcreteStorage()
        s.save_report("r2", "content")
        assert s.delete_report("r2") is True
        assert s.report_exists("r2") is False

    def test_delete_nonexistent(self):
        """delete_report returns False for missing report."""
        s = ConcreteStorage()
        assert s.delete_report("nope") is False


# ---------------------------------------------------------------------------
# Additional deep-coverage tests
# ---------------------------------------------------------------------------


from abc import ABC  # noqa: E402


class TestAbstractMethodsSetExact:
    """Verify the exact set of abstract methods declared on ReportStorage."""

    def test_abstract_methods_match_documented_interface(self):
        expected = {
            "save_report",
            "get_report",
            "get_report_with_metadata",
            "delete_report",
            "list_reports",
            "report_exists",
        }
        assert ReportStorage.__abstractmethods__ == expected

    def test_report_storage_is_abc_subclass(self):
        assert issubclass(ReportStorage, ABC)


class TestPartialImplementationRaisesTypeError:
    """Any missing abstract method must prevent instantiation."""

    def test_missing_save_report_raises(self):
        class Incomplete(ReportStorage):
            def get_report(self, rid, username=None):
                return None

            def get_report_with_metadata(self, rid, username=None):
                return None

            def delete_report(self, rid, username=None):
                return False

            def list_reports(self, username=None):
                return []

            def report_exists(self, rid, username=None):
                return False

        with pytest.raises(TypeError):
            Incomplete()

    def test_missing_report_exists_raises(self):
        class Incomplete(ReportStorage):
            def save_report(self, rid, content, metadata=None, username=None):
                return True

            def get_report(self, rid, username=None):
                return None

            def get_report_with_metadata(self, rid, username=None):
                return None

            def delete_report(self, rid, username=None):
                return False

            def list_reports(self, username=None):
                return []

        with pytest.raises(TypeError):
            Incomplete()


class TestConcreteStorageEdgeCases:
    """Edge cases not covered in the base tests."""

    def test_metadata_none_stored_as_empty_dict(self):
        s = ConcreteStorage()
        s.save_report("r1", "text", metadata=None)
        result = s.get_report_with_metadata("r1")
        assert result is not None
        assert result.get("metadata") == {}

    def test_delete_twice_second_returns_false(self):
        s = ConcreteStorage()
        s.save_report("r1", "data")
        assert s.delete_report("r1") is True
        assert s.delete_report("r1") is False

    def test_get_report_with_metadata_returns_none_for_missing(self):
        s = ConcreteStorage()
        assert s.get_report_with_metadata("no-such") is None

    def test_save_overwrite_replaces_content(self):
        s = ConcreteStorage()
        s.save_report("r1", "v1")
        s.save_report("r1", "v2")
        assert s.get_report("r1") == "v2"
