"""
History query service implementation.

Provides high-level queries on top of the history backend.
"""

from pathlib import Path
from typing import List, Optional, Dict, Callable

from .interfaces import IHistoryBackend, IHistoryQuery
from .models import HistoryEntry, DiffResult, Commit


class HistoryQueryService(IHistoryQuery):
    """
    Service for querying history information.

    Provides convenience methods on top of the raw history backend.
    """

    def __init__(
        self,
        backend: IHistoryBackend,
        directory_resolver: Callable[[str], Optional[Path]],
    ):
        """
        Initialize the query service.

        Args:
            backend: History backend to query
            directory_resolver: Function that resolves space_id to directory path
        """
        self._backend = backend
        self._directory_resolver = directory_resolver

    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."""
        directory = self._directory_resolver(space_id)
        if not directory or not self._backend.is_initialized(directory):
            return []

        commits = self._backend.get_log(directory, limit, offset, path)
        return [self._commit_to_entry(commit, directory) for commit in commits]

    def _commit_to_entry(self, commit: Commit, directory: Path) -> HistoryEntry:
        """Convert a commit to a history entry with summary."""
        # Count file changes by type
        files_added = 0
        files_modified = 0
        files_deleted = 0

        # Get diff to determine change types
        if commit.parent_ids:
            parent = commit.parent_ids[0]
            diffs = self._backend.get_diff(directory, parent, commit.id)
            for diff in diffs:
                if diff.additions > 0 and diff.deletions == 0:
                    files_added += 1
                elif diff.additions == 0 and diff.deletions > 0:
                    files_deleted += 1
                else:
                    files_modified += 1
        else:
            # First commit - all files are additions
            files_added = len(commit.files_changed)

        # Build summary
        parts = []
        if files_added:
            parts.append(f"+{files_added}")
        if files_modified:
            parts.append(f"~{files_modified}")
        if files_deleted:
            parts.append(f"-{files_deleted}")

        summary = ", ".join(parts) if parts else "No changes"

        return HistoryEntry(
            commit=commit,
            summary=summary,
            files_added=files_added,
            files_modified=files_modified,
            files_deleted=files_deleted,
        )

    def get_version_content(
        self,
        space_id: str,
        document_path: str,
        version: str,
    ) -> Optional[str]:
        """Get document content at a specific version."""
        directory = self._directory_resolver(space_id)
        if not directory or not self._backend.is_initialized(directory):
            return None

        # Normalize path (remove leading /)
        if document_path.startswith("/"):
            document_path = document_path[1:]

        return self._backend.get_file_at_version(directory, document_path, version)

    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."""
        directory = self._directory_resolver(space_id)
        if not directory or not self._backend.is_initialized(directory):
            return None

        # Normalize path
        if document_path.startswith("/"):
            document_path = document_path[1:]

        diffs = self._backend.get_diff(directory, old_version, new_version, document_path)
        return diffs[0] if diffs else None

    def search_history(
        self,
        space_id: str,
        query: str,
        limit: int = 20,
    ) -> List[HistoryEntry]:
        """Search commit messages."""
        directory = self._directory_resolver(space_id)
        if not directory or not self._backend.is_initialized(directory):
            return []

        # Get all recent commits
        commits = self._backend.get_log(directory, limit=limit * 2)

        # Filter by query in message
        matching_commits = [
            c for c in commits
            if query.lower() in c.message.lower()
        ][:limit]

        return [self._commit_to_entry(commit, directory) for commit in matching_commits]

    def restore_version(
        self,
        space_id: str,
        document_path: str,
        version: str,
    ) -> bool:
        """Restore a document to a specific version."""
        directory = self._directory_resolver(space_id)
        if not directory or not self._backend.is_initialized(directory):
            return False

        # Normalize path
        if document_path.startswith("/"):
            document_path = document_path[1:]

        # Get content at version
        content = self._backend.get_file_at_version(directory, document_path, version)
        if content is None:
            return False

        # Write to working directory
        file_path = directory / document_path
        file_path.parent.mkdir(parents=True, exist_ok=True)
        file_path.write_text(content)
        return True

    def get_latest_commit(self, space_id: str) -> Optional[Commit]:
        """Get the latest commit for a space."""
        directory = self._directory_resolver(space_id)
        if not directory or not self._backend.is_initialized(directory):
            return None

        commits = self._backend.get_log(directory, limit=1)
        return commits[0] if commits else None

    def get_commit_by_id(self, space_id: str, commit_id: str) -> Optional[Commit]:
        """Get a specific commit by ID."""
        directory = self._directory_resolver(space_id)
        if not directory:
            return None

        return self._backend.get_commit(directory, commit_id)

    def get_file_history(
        self,
        space_id: str,
        document_path: str,
        limit: int = 20,
    ) -> List[HistoryEntry]:
        """Get history for a specific file."""
        # Normalize path
        if document_path.startswith("/"):
            document_path = document_path[1:]

        return self.get_history(space_id, limit=limit, path=document_path)

    def has_history(self, space_id: str) -> bool:
        """Check if a space has history tracking enabled."""
        directory = self._directory_resolver(space_id)
        if not directory:
            return False
        return self._backend.is_initialized(directory)

    def get_branches(self, space_id: str) -> List[str]:
        """Get list of branch names for a space."""
        directory = self._directory_resolver(space_id)
        if not directory or not self._backend.is_initialized(directory):
            return []

        branches = self._backend.list_branches(directory)
        return [b.name for b in branches]

    def get_current_branch(self, space_id: str) -> Optional[str]:
        """Get the current branch name."""
        directory = self._directory_resolver(space_id)
        if not directory:
            return None
        return self._backend.get_current_branch(directory)

    def create_branch(
        self,
        space_id: str,
        name: str,
        start_point: Optional[str] = None,
    ) -> bool:
        """Create a new branch."""
        directory = self._directory_resolver(space_id)
        if not directory or not self._backend.is_initialized(directory):
            return False

        try:
            self._backend.create_branch(directory, name, start_point)
            return True
        except Exception:
            return False

    def switch_branch(self, space_id: str, name: str) -> bool:
        """Switch to a different branch."""
        directory = self._directory_resolver(space_id)
        if not directory:
            return False
        return self._backend.switch_branch(directory, name)

    def has_uncommitted_changes(self, space_id: str) -> bool:
        """Check if space has uncommitted changes."""
        directory = self._directory_resolver(space_id)
        if not directory:
            return False
        return self._backend.has_changes(directory)
