"""
Abstract interfaces for history tracking.

Defines the contract for history backends and query services.
"""

from abc import ABC, abstractmethod
from pathlib import Path
from typing import List, Optional, Dict, Any

from .models import Commit, Branch, DiffResult, HistoryEntry, HistoryConfig


class IHistoryBackend(ABC):
    """
    Abstract interface for history storage backends.

    Implementations can use git, SQLite, or other storage mechanisms.
    """

    @abstractmethod
    def initialize(self, directory: Path, config: HistoryConfig) -> None:
        """
        Initialize the history backend.

        Args:
            directory: Root directory for the space
            config: History configuration
        """
        pass

    @abstractmethod
    def is_initialized(self, directory: Path) -> bool:
        """
        Check if history is initialized for a directory.

        Args:
            directory: Directory to check

        Returns:
            True if history tracking is initialized
        """
        pass

    @abstractmethod
    def commit(
        self,
        directory: Path,
        message: str,
        files: Optional[List[str]] = None,
        author: Optional[str] = None,
        metadata: Optional[Dict[str, Any]] = None,
    ) -> Commit:
        """
        Create a new commit.

        Args:
            directory: Root directory
            message: Commit message
            files: Specific files to commit (None for all changes)
            author: Author string (name <email>)
            metadata: Additional metadata to store

        Returns:
            The created commit
        """
        pass

    @abstractmethod
    def get_commit(self, directory: Path, commit_id: str) -> Optional[Commit]:
        """
        Get a commit by ID.

        Args:
            directory: Root directory
            commit_id: Commit identifier

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

    @abstractmethod
    def get_log(
        self,
        directory: Path,
        limit: int = 50,
        offset: int = 0,
        path: Optional[str] = None,
    ) -> List[Commit]:
        """
        Get commit history.

        Args:
            directory: Root directory
            limit: Maximum number of commits
            offset: Number of commits to skip
            path: Filter by file path

        Returns:
            List of commits, newest first
        """
        pass

    @abstractmethod
    def get_diff(
        self,
        directory: Path,
        old_version: Optional[str] = None,
        new_version: Optional[str] = None,
        path: Optional[str] = None,
    ) -> List[DiffResult]:
        """
        Get diff between versions.

        Args:
            directory: Root directory
            old_version: Old commit ID (None for working directory)
            new_version: New commit ID (None for working directory)
            path: Filter by file path

        Returns:
            List of diff results
        """
        pass

    @abstractmethod
    def checkout(
        self,
        directory: Path,
        version: str,
        path: Optional[str] = None,
    ) -> bool:
        """
        Checkout a specific version.

        Args:
            directory: Root directory
            version: Commit ID or ref to checkout
            path: Specific file to checkout (None for all)

        Returns:
            True if successful
        """
        pass

    @abstractmethod
    def get_file_at_version(
        self,
        directory: Path,
        path: str,
        version: str,
    ) -> Optional[str]:
        """
        Get file content at a specific version.

        Args:
            directory: Root directory
            path: File path relative to directory
            version: Commit ID or ref

        Returns:
            File content as string, or None if not found
        """
        pass

    @abstractmethod
    def list_branches(self, directory: Path) -> List[Branch]:
        """
        List all branches.

        Args:
            directory: Root directory

        Returns:
            List of branches
        """
        pass

    @abstractmethod
    def create_branch(
        self,
        directory: Path,
        name: str,
        start_point: Optional[str] = None,
    ) -> Branch:
        """
        Create a new branch.

        Args:
            directory: Root directory
            name: Branch name
            start_point: Starting commit/ref (None for HEAD)

        Returns:
            The created branch
        """
        pass

    @abstractmethod
    def switch_branch(self, directory: Path, name: str) -> bool:
        """
        Switch to a branch.

        Args:
            directory: Root directory
            name: Branch name

        Returns:
            True if successful
        """
        pass

    @abstractmethod
    def get_current_branch(self, directory: Path) -> Optional[str]:
        """
        Get the current branch name.

        Args:
            directory: Root directory

        Returns:
            Branch name or None if detached
        """
        pass

    @abstractmethod
    def has_changes(self, directory: Path) -> bool:
        """
        Check if there are uncommitted changes.

        Args:
            directory: Root directory

        Returns:
            True if there are uncommitted changes
        """
        pass


class IHistoryQuery(ABC):
    """
    Abstract interface for history queries.

    Provides higher-level query operations on top of the backend.
    """

    @abstractmethod
    def get_history(
        self,
        space_id: str,
        limit: int = 50,
        offset: int = 0,
        path: Optional[str] = None,
    ) -> List[HistoryEntry]:
        """
        Get history entries for a space.

        Args:
            space_id: Space identifier
            limit: Maximum entries
            offset: Skip count
            path: Filter by document path

        Returns:
            List of history entries
        """
        pass

    @abstractmethod
    def get_version_content(
        self,
        space_id: str,
        document_path: str,
        version: str,
    ) -> Optional[str]:
        """
        Get document content at a specific version.

        Args:
            space_id: Space identifier
            document_path: Path to document
            version: Version identifier

        Returns:
            Document content or None
        """
        pass

    @abstractmethod
    def compare_versions(
        self,
        space_id: str,
        document_path: str,
        old_version: str,
        new_version: Optional[str] = None,
    ) -> Optional[DiffResult]:
        """
        Compare two versions of a document.

        Args:
            space_id: Space identifier
            document_path: Path to document
            old_version: Old version ID
            new_version: New version ID (None for current)

        Returns:
            Diff result or None
        """
        pass

    @abstractmethod
    def search_history(
        self,
        space_id: str,
        query: str,
        limit: int = 20,
    ) -> List[HistoryEntry]:
        """
        Search commit messages.

        Args:
            space_id: Space identifier
            query: Search query
            limit: Maximum results

        Returns:
            Matching history entries
        """
        pass

    @abstractmethod
    def restore_version(
        self,
        space_id: str,
        document_path: str,
        version: str,
    ) -> bool:
        """
        Restore a document to a specific version.

        Args:
            space_id: Space identifier
            document_path: Path to document
            version: Version to restore

        Returns:
            True if successful
        """
        pass
