"""
Unit tests for space repositories.

Tests the SQLite implementations of:
- ISpaceRepository (SqliteSpaceRepository)
- IDocumentAssociationRepository (SqliteDocumentRepository)
- IVariableRepository (SqliteVariableRepository)
- IReferenceRepository (SqliteReferenceRepository)
"""

import pytest
import tempfile
import os
from datetime import datetime

from markitect.spaces.models import (
    InformationSpace,
    SpaceDocument,
    SpaceVariable,
    TransclusionReference,
    SpaceStatus,
    SpaceConfig,
    SpaceMetadata,
)
from markitect.spaces.repositories.sqlite import (
    SqliteSpaceRepository,
    SqliteDocumentRepository,
    SqliteVariableRepository,
    SqliteReferenceRepository,
    initialize_space_tables,
)


@pytest.fixture
def temp_db():
    """Create a temporary database file for testing."""
    fd, path = tempfile.mkstemp(suffix=".db")
    os.close(fd)
    yield path
    if os.path.exists(path):
        os.unlink(path)


@pytest.fixture
def space_repo(temp_db):
    """Create a SqliteSpaceRepository for testing."""
    return SqliteSpaceRepository(temp_db)


@pytest.fixture
def doc_repo(temp_db):
    """Create a SqliteDocumentRepository for testing."""
    return SqliteDocumentRepository(temp_db)


@pytest.fixture
def var_repo(temp_db):
    """Create a SqliteVariableRepository for testing."""
    return SqliteVariableRepository(temp_db)


@pytest.fixture
def ref_repo(temp_db):
    """Create a SqliteReferenceRepository for testing."""
    return SqliteReferenceRepository(temp_db)


class TestInitializeSpaceTables:
    """Tests for initialize_space_tables function."""

    def test_creates_tables(self, temp_db):
        """Test that initialize_space_tables creates all required tables."""
        import sqlite3

        initialize_space_tables(temp_db)

        conn = sqlite3.connect(temp_db)
        cursor = conn.cursor()

        # Check that all tables exist
        cursor.execute("SELECT name FROM sqlite_master WHERE type='table'")
        tables = {row[0] for row in cursor.fetchall()}

        assert "spaces" in tables
        assert "space_documents" in tables
        assert "space_variables" in tables
        assert "transclusion_references" in tables

        conn.close()

    def test_idempotent(self, temp_db):
        """Test that initialize_space_tables can be called multiple times."""
        initialize_space_tables(temp_db)
        initialize_space_tables(temp_db)  # Should not raise

    def test_creates_parent_directory(self):
        """Test that initialize_space_tables creates parent directories."""
        with tempfile.TemporaryDirectory() as tmpdir:
            db_path = os.path.join(tmpdir, "subdir", "nested", "test.db")
            initialize_space_tables(db_path)
            assert os.path.exists(db_path)


class TestSqliteSpaceRepository:
    """Tests for SqliteSpaceRepository."""

    def test_create_space(self, space_repo):
        """Test creating a new space."""
        space = InformationSpace(name="test-space", description="A test space")
        created = space_repo.create(space)

        assert created.id == space.id
        assert created.name == "test-space"
        assert created.description == "A test space"

    def test_create_space_duplicate_name_raises(self, space_repo):
        """Test that creating a space with duplicate name raises ValueError."""
        space1 = InformationSpace(name="duplicate")
        space_repo.create(space1)

        space2 = InformationSpace(name="duplicate")
        with pytest.raises(ValueError, match="already exists"):
            space_repo.create(space2)

    def test_get_by_id(self, space_repo):
        """Test retrieving a space by ID."""
        space = InformationSpace(name="test-space")
        space_repo.create(space)

        retrieved = space_repo.get_by_id(space.id)
        assert retrieved is not None
        assert retrieved.id == space.id
        assert retrieved.name == "test-space"

    def test_get_by_id_not_found(self, space_repo):
        """Test that get_by_id returns None for non-existent space."""
        result = space_repo.get_by_id("non-existent-id")
        assert result is None

    def test_get_by_name(self, space_repo):
        """Test retrieving a space by name."""
        space = InformationSpace(name="named-space")
        space_repo.create(space)

        retrieved = space_repo.get_by_name("named-space")
        assert retrieved is not None
        assert retrieved.name == "named-space"

    def test_get_by_name_not_found(self, space_repo):
        """Test that get_by_name returns None for non-existent space."""
        result = space_repo.get_by_name("non-existent")
        assert result is None

    def test_list_all_empty(self, space_repo):
        """Test listing spaces when none exist."""
        spaces = space_repo.list_all()
        assert spaces == []

    def test_list_all(self, space_repo):
        """Test listing all spaces."""
        space1 = InformationSpace(name="alpha")
        space2 = InformationSpace(name="beta")
        space_repo.create(space1)
        space_repo.create(space2)

        spaces = space_repo.list_all()
        assert len(spaces) == 2
        names = [s.name for s in spaces]
        assert "alpha" in names
        assert "beta" in names

    def test_list_all_excludes_archived_by_default(self, space_repo):
        """Test that list_all excludes archived spaces by default."""
        space1 = InformationSpace(name="active-space")
        space2 = InformationSpace(name="archived-space")
        space2.archive()

        space_repo.create(space1)
        space_repo.create(space2)

        spaces = space_repo.list_all()
        assert len(spaces) == 1
        assert spaces[0].name == "active-space"

    def test_list_all_includes_archived_when_requested(self, space_repo):
        """Test that list_all includes archived spaces when requested."""
        space1 = InformationSpace(name="active-space")
        space2 = InformationSpace(name="archived-space")
        space2.archive()

        space_repo.create(space1)
        space_repo.create(space2)

        spaces = space_repo.list_all(include_archived=True)
        assert len(spaces) == 2

    def test_update_space(self, space_repo):
        """Test updating a space."""
        space = InformationSpace(name="original")
        space_repo.create(space)

        space.description = "Updated description"
        updated = space_repo.update(space)

        assert updated.description == "Updated description"

        # Verify persisted
        retrieved = space_repo.get_by_id(space.id)
        assert retrieved.description == "Updated description"

    def test_update_nonexistent_raises(self, space_repo):
        """Test that updating a non-existent space raises ValueError."""
        space = InformationSpace(name="non-existent")
        with pytest.raises(ValueError, match="does not exist"):
            space_repo.update(space)

    def test_delete_space(self, space_repo):
        """Test deleting a space."""
        space = InformationSpace(name="to-delete")
        space_repo.create(space)

        result = space_repo.delete(space.id)
        assert result is True

        # Verify deleted
        retrieved = space_repo.get_by_id(space.id)
        assert retrieved is None

    def test_delete_nonexistent(self, space_repo):
        """Test that deleting a non-existent space returns False."""
        result = space_repo.delete("non-existent-id")
        assert result is False

    def test_exists(self, space_repo):
        """Test checking if a space exists."""
        space = InformationSpace(name="existing")
        space_repo.create(space)

        assert space_repo.exists(space.id) is True
        assert space_repo.exists("non-existent") is False

    def test_get_children(self, space_repo):
        """Test getting child spaces."""
        parent = InformationSpace(name="parent")
        space_repo.create(parent)

        child1 = InformationSpace(name="child1", parent_space_id=parent.id)
        child2 = InformationSpace(name="child2", parent_space_id=parent.id)
        space_repo.create(child1)
        space_repo.create(child2)

        children = space_repo.get_children(parent.id)
        assert len(children) == 2
        names = [c.name for c in children]
        assert "child1" in names
        assert "child2" in names

    def test_get_children_empty(self, space_repo):
        """Test getting children when none exist."""
        parent = InformationSpace(name="lonely-parent")
        space_repo.create(parent)

        children = space_repo.get_children(parent.id)
        assert children == []

    def test_space_with_config_and_metadata(self, space_repo):
        """Test creating and retrieving a space with config and metadata."""
        config = SpaceConfig(theme="dark", history_enabled=True)
        metadata = SpaceMetadata(tags=["api", "docs"], author="tester")
        space = InformationSpace(
            name="configured-space",
            config=config,
            metadata=metadata,
        )
        space_repo.create(space)

        retrieved = space_repo.get_by_id(space.id)
        assert retrieved.config.theme == "dark"
        assert retrieved.config.history_enabled is True
        assert retrieved.metadata.tags == ["api", "docs"]
        assert retrieved.metadata.author == "tester"

    def test_space_status_persistence(self, space_repo):
        """Test that space status is persisted correctly."""
        space = InformationSpace(name="lifecycle-test")
        space.activate()
        space_repo.create(space)

        retrieved = space_repo.get_by_id(space.id)
        assert retrieved.status == SpaceStatus.ACTIVE


class TestSqliteDocumentRepository:
    """Tests for SqliteDocumentRepository."""

    def test_add_document(self, doc_repo, space_repo):
        """Test adding a document to a space."""
        # First create a space
        space = InformationSpace(name="test-space")
        space_repo.create(space)

        doc = SpaceDocument(
            space_id=space.id,
            document_id="doc-123",
            space_path="/intro.md",
        )
        added = doc_repo.add_document(doc)

        assert added.id == doc.id
        assert added.space_path == "/intro.md"

    def test_add_document_duplicate_path_raises(self, doc_repo, space_repo):
        """Test that adding a document with duplicate path raises ValueError."""
        space = InformationSpace(name="test-space")
        space_repo.create(space)

        doc1 = SpaceDocument(space_id=space.id, space_path="/same.md")
        doc_repo.add_document(doc1)

        doc2 = SpaceDocument(space_id=space.id, space_path="/same.md")
        with pytest.raises(ValueError, match="already exists"):
            doc_repo.add_document(doc2)

    def test_get_document(self, doc_repo, space_repo):
        """Test getting a document by ID."""
        space = InformationSpace(name="test-space")
        space_repo.create(space)

        doc = SpaceDocument(space_id=space.id, space_path="/test.md")
        doc_repo.add_document(doc)

        retrieved = doc_repo.get_document(doc.id)
        assert retrieved is not None
        assert retrieved.space_path == "/test.md"

    def test_get_document_not_found(self, doc_repo):
        """Test that get_document returns None for non-existent document."""
        result = doc_repo.get_document("non-existent")
        assert result is None

    def test_get_by_space_path(self, doc_repo, space_repo):
        """Test getting a document by space path."""
        space = InformationSpace(name="test-space")
        space_repo.create(space)

        doc = SpaceDocument(space_id=space.id, space_path="/api/docs.md")
        doc_repo.add_document(doc)

        retrieved = doc_repo.get_by_space_path(space.id, "/api/docs.md")
        assert retrieved is not None
        assert retrieved.id == doc.id

    def test_get_by_space_path_not_found(self, doc_repo, space_repo):
        """Test that get_by_space_path returns None for non-existent path."""
        space = InformationSpace(name="test-space")
        space_repo.create(space)

        result = doc_repo.get_by_space_path(space.id, "/non-existent.md")
        assert result is None

    def test_list_by_space(self, doc_repo, space_repo):
        """Test listing documents in a space."""
        space = InformationSpace(name="test-space")
        space_repo.create(space)

        doc1 = SpaceDocument(space_id=space.id, space_path="/first.md", order_index=0)
        doc2 = SpaceDocument(space_id=space.id, space_path="/second.md", order_index=1)
        doc_repo.add_document(doc1)
        doc_repo.add_document(doc2)

        docs = doc_repo.list_by_space(space.id)
        assert len(docs) == 2
        assert docs[0].space_path == "/first.md"
        assert docs[1].space_path == "/second.md"

    def test_list_by_space_empty(self, doc_repo, space_repo):
        """Test listing documents when none exist."""
        space = InformationSpace(name="empty-space")
        space_repo.create(space)

        docs = doc_repo.list_by_space(space.id)
        assert docs == []

    def test_update_document(self, doc_repo, space_repo):
        """Test updating a document."""
        space = InformationSpace(name="test-space")
        space_repo.create(space)

        doc = SpaceDocument(space_id=space.id, space_path="/old.md")
        doc_repo.add_document(doc)

        doc.content_hash = "newhash123"
        updated = doc_repo.update_document(doc)
        assert updated.content_hash == "newhash123"

        # Verify persisted
        retrieved = doc_repo.get_document(doc.id)
        assert retrieved.content_hash == "newhash123"

    def test_update_nonexistent_raises(self, doc_repo):
        """Test that updating a non-existent document raises ValueError."""
        doc = SpaceDocument(space_path="/non-existent.md")
        with pytest.raises(ValueError, match="does not exist"):
            doc_repo.update_document(doc)

    def test_remove_document(self, doc_repo, space_repo):
        """Test removing a document."""
        space = InformationSpace(name="test-space")
        space_repo.create(space)

        doc = SpaceDocument(space_id=space.id, space_path="/to-remove.md")
        doc_repo.add_document(doc)

        result = doc_repo.remove_document(doc.id)
        assert result is True

        # Verify removed
        retrieved = doc_repo.get_document(doc.id)
        assert retrieved is None

    def test_remove_nonexistent(self, doc_repo):
        """Test that removing a non-existent document returns False."""
        result = doc_repo.remove_document("non-existent")
        assert result is False

    def test_move_document(self, doc_repo, space_repo):
        """Test moving a document to a new path."""
        space = InformationSpace(name="test-space")
        space_repo.create(space)

        doc = SpaceDocument(space_id=space.id, space_path="/old-path.md")
        doc_repo.add_document(doc)

        moved = doc_repo.move_document(doc.id, "/new-path.md")
        assert moved.space_path == "/new-path.md"

        # Verify old path no longer works
        old_result = doc_repo.get_by_space_path(space.id, "/old-path.md")
        assert old_result is None

        # Verify new path works
        new_result = doc_repo.get_by_space_path(space.id, "/new-path.md")
        assert new_result is not None

    def test_move_document_to_existing_path_raises(self, doc_repo, space_repo):
        """Test that moving to an existing path raises ValueError."""
        space = InformationSpace(name="test-space")
        space_repo.create(space)

        doc1 = SpaceDocument(space_id=space.id, space_path="/first.md")
        doc2 = SpaceDocument(space_id=space.id, space_path="/second.md")
        doc_repo.add_document(doc1)
        doc_repo.add_document(doc2)

        with pytest.raises(ValueError, match="already exists"):
            doc_repo.move_document(doc1.id, "/second.md")

    def test_move_nonexistent_raises(self, doc_repo):
        """Test that moving a non-existent document raises ValueError."""
        with pytest.raises(ValueError, match="does not exist"):
            doc_repo.move_document("non-existent", "/new-path.md")

    def test_reorder_documents(self, doc_repo, space_repo):
        """Test reordering documents within a space."""
        space = InformationSpace(name="test-space")
        space_repo.create(space)

        doc1 = SpaceDocument(space_id=space.id, space_path="/a.md", order_index=0)
        doc2 = SpaceDocument(space_id=space.id, space_path="/b.md", order_index=1)
        doc3 = SpaceDocument(space_id=space.id, space_path="/c.md", order_index=2)
        doc_repo.add_document(doc1)
        doc_repo.add_document(doc2)
        doc_repo.add_document(doc3)

        # Reorder: c, a, b
        doc_repo.reorder_documents(space.id, [doc3.id, doc1.id, doc2.id])

        docs = doc_repo.list_by_space(space.id)
        assert docs[0].id == doc3.id
        assert docs[1].id == doc1.id
        assert docs[2].id == doc2.id

    def test_update_content_hash(self, doc_repo, space_repo):
        """Test updating content hash."""
        space = InformationSpace(name="test-space")
        space_repo.create(space)

        doc = SpaceDocument(space_id=space.id, space_path="/test.md")
        doc_repo.add_document(doc)

        doc_repo.update_content_hash(doc.id, "newhash456")

        retrieved = doc_repo.get_document(doc.id)
        assert retrieved.content_hash == "newhash456"

    def test_document_with_metadata(self, doc_repo, space_repo):
        """Test document with custom metadata."""
        space = InformationSpace(name="test-space")
        space_repo.create(space)

        doc = SpaceDocument(
            space_id=space.id,
            space_path="/with-meta.md",
            metadata={"title": "Test Document", "version": "1.0"},
        )
        doc_repo.add_document(doc)

        retrieved = doc_repo.get_document(doc.id)
        assert retrieved.metadata["title"] == "Test Document"
        assert retrieved.metadata["version"] == "1.0"


class TestSqliteVariableRepository:
    """Tests for SqliteVariableRepository."""

    def test_set_variable(self, var_repo, space_repo):
        """Test setting a variable."""
        space = InformationSpace(name="test-space")
        space_repo.create(space)

        var = SpaceVariable(
            space_id=space.id,
            name="version",
            value="1.0.0",
        )
        result = var_repo.set_variable(var)
        assert result.name == "version"
        assert result.value == "1.0.0"

    def test_set_variable_overwrites(self, var_repo, space_repo):
        """Test that setting a variable with same name overwrites."""
        space = InformationSpace(name="test-space")
        space_repo.create(space)

        var1 = SpaceVariable(space_id=space.id, name="config", value="old")
        var_repo.set_variable(var1)

        var2 = SpaceVariable(space_id=space.id, name="config", value="new")
        var_repo.set_variable(var2)

        retrieved = var_repo.get_variable(space.id, "config")
        assert retrieved.value == "new"

    def test_get_variable(self, var_repo, space_repo):
        """Test getting a variable."""
        space = InformationSpace(name="test-space")
        space_repo.create(space)

        var = SpaceVariable(space_id=space.id, name="api_key", value="secret123")
        var_repo.set_variable(var)

        retrieved = var_repo.get_variable(space.id, "api_key")
        assert retrieved is not None
        assert retrieved.value == "secret123"

    def test_get_variable_not_found(self, var_repo, space_repo):
        """Test that get_variable returns None for non-existent variable."""
        space = InformationSpace(name="test-space")
        space_repo.create(space)

        result = var_repo.get_variable(space.id, "non-existent")
        assert result is None

    def test_list_variables(self, var_repo, space_repo):
        """Test listing variables in a space."""
        space = InformationSpace(name="test-space")
        space_repo.create(space)

        var1 = SpaceVariable(space_id=space.id, name="var1", value="a")
        var2 = SpaceVariable(space_id=space.id, name="var2", value="b")
        var_repo.set_variable(var1)
        var_repo.set_variable(var2)

        variables = var_repo.list_variables(space.id)
        assert len(variables) == 2
        names = [v.name for v in variables]
        assert "var1" in names
        assert "var2" in names

    def test_list_variables_empty(self, var_repo, space_repo):
        """Test listing variables when none exist."""
        space = InformationSpace(name="empty-space")
        space_repo.create(space)

        variables = var_repo.list_variables(space.id)
        assert variables == []

    def test_list_variables_with_scope_filter(self, var_repo, space_repo):
        """Test listing variables filtered by scope."""
        space = InformationSpace(name="test-space")
        space_repo.create(space)

        var1 = SpaceVariable(space_id=space.id, name="global", value="x", scope="space")
        var2 = SpaceVariable(space_id=space.id, name="local", value="y", scope="document")
        var_repo.set_variable(var1)
        var_repo.set_variable(var2)

        space_vars = var_repo.list_variables(space.id, scope="space")
        assert len(space_vars) == 1
        assert space_vars[0].name == "global"

        doc_vars = var_repo.list_variables(space.id, scope="document")
        assert len(doc_vars) == 1
        assert doc_vars[0].name == "local"

    def test_delete_variable(self, var_repo, space_repo):
        """Test deleting a variable."""
        space = InformationSpace(name="test-space")
        space_repo.create(space)

        var = SpaceVariable(space_id=space.id, name="to-delete", value="bye")
        var_repo.set_variable(var)

        result = var_repo.delete_variable(space.id, "to-delete")
        assert result is True

        # Verify deleted
        retrieved = var_repo.get_variable(space.id, "to-delete")
        assert retrieved is None

    def test_delete_nonexistent(self, var_repo, space_repo):
        """Test that deleting a non-existent variable returns False."""
        space = InformationSpace(name="test-space")
        space_repo.create(space)

        result = var_repo.delete_variable(space.id, "non-existent")
        assert result is False

    def test_variable_with_complex_value(self, var_repo, space_repo):
        """Test variable with complex JSON value."""
        space = InformationSpace(name="test-space")
        space_repo.create(space)

        complex_value = {
            "endpoints": [
                {"url": "/api/v1", "methods": ["GET", "POST"]},
                {"url": "/api/v2", "methods": ["GET"]},
            ],
            "config": {"timeout": 30, "retries": 3},
        }
        var = SpaceVariable(space_id=space.id, name="api_config", value=complex_value)
        var_repo.set_variable(var)

        retrieved = var_repo.get_variable(space.id, "api_config")
        assert retrieved.value == complex_value
        assert retrieved.value["endpoints"][0]["url"] == "/api/v1"


class TestSqliteReferenceRepository:
    """Tests for SqliteReferenceRepository."""

    def test_add_reference(self, ref_repo, space_repo):
        """Test adding a transclusion reference."""
        space = InformationSpace(name="test-space")
        space_repo.create(space)

        ref = TransclusionReference(
            source_doc_id="doc-1",
            target_doc_id="doc-2",
            space_id=space.id,
        )
        result = ref_repo.add_reference(ref)
        assert result.source_doc_id == "doc-1"
        assert result.target_doc_id == "doc-2"

    def test_add_reference_overwrites(self, ref_repo, space_repo):
        """Test that adding same reference overwrites (no duplicates)."""
        space = InformationSpace(name="test-space")
        space_repo.create(space)

        ref1 = TransclusionReference(
            source_doc_id="doc-1",
            target_doc_id="doc-2",
            space_id=space.id,
        )
        ref_repo.add_reference(ref1)

        # Add same reference again (should not raise)
        ref2 = TransclusionReference(
            source_doc_id="doc-1",
            target_doc_id="doc-2",
            space_id=space.id,
        )
        ref_repo.add_reference(ref2)

        # Should still only have one reference
        refs = ref_repo.get_references_from("doc-1", space.id)
        assert len(refs) == 1

    def test_get_references_from(self, ref_repo, space_repo):
        """Test getting references from a source document."""
        space = InformationSpace(name="test-space")
        space_repo.create(space)

        ref1 = TransclusionReference(
            source_doc_id="doc-1",
            target_doc_id="doc-2",
            space_id=space.id,
        )
        ref2 = TransclusionReference(
            source_doc_id="doc-1",
            target_doc_id="doc-3",
            space_id=space.id,
        )
        ref_repo.add_reference(ref1)
        ref_repo.add_reference(ref2)

        refs = ref_repo.get_references_from("doc-1", space.id)
        assert len(refs) == 2
        targets = [r.target_doc_id for r in refs]
        assert "doc-2" in targets
        assert "doc-3" in targets

    def test_get_references_from_empty(self, ref_repo, space_repo):
        """Test getting references when none exist."""
        space = InformationSpace(name="test-space")
        space_repo.create(space)

        refs = ref_repo.get_references_from("non-existent", space.id)
        assert refs == []

    def test_get_references_to(self, ref_repo, space_repo):
        """Test getting references to a target document."""
        space = InformationSpace(name="test-space")
        space_repo.create(space)

        ref1 = TransclusionReference(
            source_doc_id="doc-1",
            target_doc_id="shared-doc",
            space_id=space.id,
        )
        ref2 = TransclusionReference(
            source_doc_id="doc-2",
            target_doc_id="shared-doc",
            space_id=space.id,
        )
        ref_repo.add_reference(ref1)
        ref_repo.add_reference(ref2)

        refs = ref_repo.get_references_to("shared-doc", space.id)
        assert len(refs) == 2
        sources = [r.source_doc_id for r in refs]
        assert "doc-1" in sources
        assert "doc-2" in sources

    def test_clear_references_from(self, ref_repo, space_repo):
        """Test clearing references from a source document."""
        space = InformationSpace(name="test-space")
        space_repo.create(space)

        ref1 = TransclusionReference(
            source_doc_id="doc-1",
            target_doc_id="doc-2",
            space_id=space.id,
        )
        ref2 = TransclusionReference(
            source_doc_id="doc-1",
            target_doc_id="doc-3",
            space_id=space.id,
        )
        ref_repo.add_reference(ref1)
        ref_repo.add_reference(ref2)

        count = ref_repo.clear_references_from("doc-1", space.id)
        assert count == 2

        # Verify cleared
        refs = ref_repo.get_references_from("doc-1", space.id)
        assert refs == []

    def test_clear_references_from_empty(self, ref_repo, space_repo):
        """Test clearing references when none exist."""
        space = InformationSpace(name="test-space")
        space_repo.create(space)

        count = ref_repo.clear_references_from("non-existent", space.id)
        assert count == 0

    def test_get_dependents(self, ref_repo, space_repo):
        """Test getting dependent documents."""
        space = InformationSpace(name="test-space")
        space_repo.create(space)

        # doc-1 and doc-2 both reference shared-component
        ref1 = TransclusionReference(
            source_doc_id="doc-1",
            target_doc_id="shared-component",
            space_id=space.id,
        )
        ref2 = TransclusionReference(
            source_doc_id="doc-2",
            target_doc_id="shared-component",
            space_id=space.id,
        )
        ref_repo.add_reference(ref1)
        ref_repo.add_reference(ref2)

        dependents = ref_repo.get_dependents("shared-component", space.id)
        assert len(dependents) == 2
        assert "doc-1" in dependents
        assert "doc-2" in dependents

    def test_get_dependents_empty(self, ref_repo, space_repo):
        """Test getting dependents when none exist."""
        space = InformationSpace(name="test-space")
        space_repo.create(space)

        dependents = ref_repo.get_dependents("orphan-doc", space.id)
        assert dependents == []

    def test_references_isolated_by_space(self, ref_repo, space_repo):
        """Test that references are isolated by space."""
        space1 = InformationSpace(name="space-1")
        space2 = InformationSpace(name="space-2")
        space_repo.create(space1)
        space_repo.create(space2)

        # Same source/target IDs in different spaces
        ref1 = TransclusionReference(
            source_doc_id="doc-1",
            target_doc_id="doc-2",
            space_id=space1.id,
        )
        ref2 = TransclusionReference(
            source_doc_id="doc-1",
            target_doc_id="doc-2",
            space_id=space2.id,
        )
        ref_repo.add_reference(ref1)
        ref_repo.add_reference(ref2)

        # Each space should have its own reference
        refs1 = ref_repo.get_references_from("doc-1", space1.id)
        refs2 = ref_repo.get_references_from("doc-1", space2.id)

        assert len(refs1) == 1
        assert len(refs2) == 1
        assert refs1[0].space_id == space1.id
        assert refs2[0].space_id == space2.id


class TestCascadeDelete:
    """Test cascade delete behavior."""

    def test_deleting_space_cascades_to_documents(self, temp_db):
        """Test that deleting a space also deletes its documents."""
        space_repo = SqliteSpaceRepository(temp_db)
        doc_repo = SqliteDocumentRepository(temp_db)

        space = InformationSpace(name="test-space")
        space_repo.create(space)

        doc = SpaceDocument(space_id=space.id, space_path="/test.md")
        doc_repo.add_document(doc)

        # Delete space
        space_repo.delete(space.id)

        # Document should also be gone
        retrieved = doc_repo.get_document(doc.id)
        assert retrieved is None

    def test_deleting_space_cascades_to_variables(self, temp_db):
        """Test that deleting a space also deletes its variables."""
        space_repo = SqliteSpaceRepository(temp_db)
        var_repo = SqliteVariableRepository(temp_db)

        space = InformationSpace(name="test-space")
        space_repo.create(space)

        var = SpaceVariable(space_id=space.id, name="var", value="val")
        var_repo.set_variable(var)

        # Delete space
        space_repo.delete(space.id)

        # Variable should also be gone
        retrieved = var_repo.get_variable(space.id, "var")
        assert retrieved is None

    def test_deleting_space_cascades_to_references(self, temp_db):
        """Test that deleting a space also deletes its references."""
        space_repo = SqliteSpaceRepository(temp_db)
        ref_repo = SqliteReferenceRepository(temp_db)

        space = InformationSpace(name="test-space")
        space_repo.create(space)

        ref = TransclusionReference(
            source_doc_id="doc-1",
            target_doc_id="doc-2",
            space_id=space.id,
        )
        ref_repo.add_reference(ref)

        # Delete space
        space_repo.delete(space.id)

        # Reference should also be gone
        refs = ref_repo.get_references_from("doc-1", space.id)
        assert refs == []
