"""
Repository interfaces for Information Spaces.

This module defines abstract base classes for space data access,
following the repository pattern for clean separation of concerns.
"""

from abc import ABC, abstractmethod
from typing import List, Optional, Dict, Any
from ..models import (
    InformationSpace,
    SpaceDocument,
    SpaceVariable,
    TransclusionReference,
)


class ISpaceRepository(ABC):
    """
    Abstract repository interface for InformationSpace persistence.

    Implementations should handle CRUD operations for spaces,
    including proper transaction management and error handling.
    """

    @abstractmethod
    def create(self, space: InformationSpace) -> InformationSpace:
        """
        Create a new space in the repository.

        Args:
            space: The space to create

        Returns:
            The created space with any generated fields populated

        Raises:
            ValueError: If space with same name already exists
        """
        pass

    @abstractmethod
    def get_by_id(self, space_id: str) -> Optional[InformationSpace]:
        """
        Retrieve a space by its ID.

        Args:
            space_id: The unique space identifier

        Returns:
            The space if found, None otherwise
        """
        pass

    @abstractmethod
    def get_by_name(self, name: str) -> Optional[InformationSpace]:
        """
        Retrieve a space by its unique name.

        Args:
            name: The space name

        Returns:
            The space if found, None otherwise
        """
        pass

    @abstractmethod
    def list_all(self, include_archived: bool = False) -> List[InformationSpace]:
        """
        List all spaces in the repository.

        Args:
            include_archived: Whether to include archived spaces

        Returns:
            List of all spaces
        """
        pass

    @abstractmethod
    def update(self, space: InformationSpace) -> InformationSpace:
        """
        Update an existing space.

        Args:
            space: The space with updated values

        Returns:
            The updated space

        Raises:
            ValueError: If space does not exist
        """
        pass

    @abstractmethod
    def delete(self, space_id: str) -> bool:
        """
        Delete a space by ID.

        Args:
            space_id: The space ID to delete

        Returns:
            True if deleted, False if not found
        """
        pass

    @abstractmethod
    def exists(self, space_id: str) -> bool:
        """
        Check if a space exists.

        Args:
            space_id: The space ID to check

        Returns:
            True if exists, False otherwise
        """
        pass

    @abstractmethod
    def get_children(self, parent_space_id: str) -> List[InformationSpace]:
        """
        Get all child spaces of a parent space.

        Args:
            parent_space_id: The parent space ID

        Returns:
            List of child spaces
        """
        pass


class IDocumentAssociationRepository(ABC):
    """
    Abstract repository interface for SpaceDocument associations.

    Manages the relationship between documents and spaces.
    """

    @abstractmethod
    def add_document(self, document: SpaceDocument) -> SpaceDocument:
        """
        Add a document to a space.

        Args:
            document: The document association to create

        Returns:
            The created document association

        Raises:
            ValueError: If document path already exists in space
        """
        pass

    @abstractmethod
    def get_document(self, document_id: str) -> Optional[SpaceDocument]:
        """
        Get a document association by ID.

        Args:
            document_id: The document association ID

        Returns:
            The document if found, None otherwise
        """
        pass

    @abstractmethod
    def get_by_space_path(self, space_id: str, space_path: str) -> Optional[SpaceDocument]:
        """
        Get a document by its path within a space.

        Args:
            space_id: The space ID
            space_path: The path within the space (e.g., "/intro.md")

        Returns:
            The document if found, None otherwise
        """
        pass

    @abstractmethod
    def list_by_space(self, space_id: str) -> List[SpaceDocument]:
        """
        List all documents in a space.

        Args:
            space_id: The space ID

        Returns:
            List of documents in the space, ordered by order_index
        """
        pass

    @abstractmethod
    def update_document(self, document: SpaceDocument) -> SpaceDocument:
        """
        Update a document association.

        Args:
            document: The document with updated values

        Returns:
            The updated document

        Raises:
            ValueError: If document does not exist
        """
        pass

    @abstractmethod
    def remove_document(self, document_id: str) -> bool:
        """
        Remove a document from a space.

        Args:
            document_id: The document association ID

        Returns:
            True if removed, False if not found
        """
        pass

    @abstractmethod
    def move_document(self, document_id: str, new_space_path: str) -> SpaceDocument:
        """
        Move a document to a new path within the space.

        Args:
            document_id: The document association ID
            new_space_path: The new path within the space

        Returns:
            The updated document

        Raises:
            ValueError: If new path already exists
        """
        pass

    @abstractmethod
    def reorder_documents(self, space_id: str, document_ids: List[str]) -> None:
        """
        Reorder documents within a space.

        Args:
            space_id: The space ID
            document_ids: Ordered list of document IDs
        """
        pass

    @abstractmethod
    def update_content_hash(self, document_id: str, content_hash: str) -> None:
        """
        Update the content hash for change detection.

        Args:
            document_id: The document association ID
            content_hash: New content hash
        """
        pass


class IVariableRepository(ABC):
    """
    Abstract repository interface for SpaceVariable storage.

    Manages space-level variables for transclusion context.
    """

    @abstractmethod
    def set_variable(self, variable: SpaceVariable) -> SpaceVariable:
        """
        Set a variable value.

        Args:
            variable: The variable to set

        Returns:
            The saved variable
        """
        pass

    @abstractmethod
    def get_variable(self, space_id: str, name: str) -> Optional[SpaceVariable]:
        """
        Get a variable by name.

        Args:
            space_id: The space ID
            name: Variable name

        Returns:
            The variable if found, None otherwise
        """
        pass

    @abstractmethod
    def list_variables(self, space_id: str, scope: Optional[str] = None) -> List[SpaceVariable]:
        """
        List all variables in a space.

        Args:
            space_id: The space ID
            scope: Optional scope filter

        Returns:
            List of variables
        """
        pass

    @abstractmethod
    def delete_variable(self, space_id: str, name: str) -> bool:
        """
        Delete a variable.

        Args:
            space_id: The space ID
            name: Variable name

        Returns:
            True if deleted, False if not found
        """
        pass


class IReferenceRepository(ABC):
    """
    Abstract repository interface for TransclusionReference tracking.

    Manages the dependency graph for cache invalidation.
    """

    @abstractmethod
    def add_reference(self, reference: TransclusionReference) -> TransclusionReference:
        """
        Add a transclusion reference.

        Args:
            reference: The reference to add

        Returns:
            The saved reference
        """
        pass

    @abstractmethod
    def get_references_from(self, source_doc_id: str, space_id: str) -> List[TransclusionReference]:
        """
        Get all references from a source document.

        Args:
            source_doc_id: The source document ID
            space_id: The space ID

        Returns:
            List of references from this document
        """
        pass

    @abstractmethod
    def get_references_to(self, target_doc_id: str, space_id: str) -> List[TransclusionReference]:
        """
        Get all references to a target document.

        Args:
            target_doc_id: The target document ID
            space_id: The space ID

        Returns:
            List of references to this document
        """
        pass

    @abstractmethod
    def clear_references_from(self, source_doc_id: str, space_id: str) -> int:
        """
        Clear all references from a source document.

        Args:
            source_doc_id: The source document ID
            space_id: The space ID

        Returns:
            Number of references deleted
        """
        pass

    @abstractmethod
    def get_dependents(self, document_id: str, space_id: str) -> List[str]:
        """
        Get all documents that depend on a given document.

        Used for cache invalidation - returns documents that need
        to be re-rendered when the target document changes.

        Args:
            document_id: The document ID
            space_id: The space ID

        Returns:
            List of dependent document IDs
        """
        pass
