"""
Event handlers for history tracking.

Subscribes to space events and triggers commits automatically.
"""

from pathlib import Path
from typing import Dict, Any, Optional, Callable
import logging

from ..events import SpaceEvent, SpaceEventType, EventBus
from .interfaces import IHistoryBackend
from .models import HistoryConfig, Commit

logger = logging.getLogger(__name__)


class GitHistoryEventHandler:
    """
    Event handler that commits changes to git on document events.

    Subscribes to document events and automatically creates commits
    when documents are added, removed, or modified.
    """

    def __init__(
        self,
        backend: IHistoryBackend,
        config: HistoryConfig,
        directory_resolver: Callable[[str], Optional[Path]],
    ):
        """
        Initialize the event handler.

        Args:
            backend: History backend to use
            config: History configuration
            directory_resolver: Function that resolves space_id to directory path
        """
        self._backend = backend
        self._config = config
        self._directory_resolver = directory_resolver
        self._event_bus: Optional[EventBus] = None
        self._handler_ids: Dict[SpaceEventType, str] = {}

    def register(self, event_bus: EventBus) -> None:
        """
        Register handlers with an event bus.

        Args:
            event_bus: Event bus to register with
        """
        self._event_bus = event_bus

        # Map event type names to SpaceEventType
        event_type_map = {
            "DOCUMENT_ADDED": SpaceEventType.DOCUMENT_ADDED,
            "DOCUMENT_REMOVED": SpaceEventType.DOCUMENT_REMOVED,
            "DOCUMENT_CONTENT_CHANGED": SpaceEventType.DOCUMENT_CONTENT_CHANGED,
            "DOCUMENT_MOVED": SpaceEventType.DOCUMENT_MOVED,
        }

        for event_name in self._config.commit_on_events:
            event_type = event_type_map.get(event_name)
            if event_type:
                handler_id = event_bus.subscribe(event_type, self._handle_event)
                self._handler_ids[event_type] = handler_id

    def unregister(self) -> None:
        """Unregister handlers from the event bus."""
        if self._event_bus:
            for event_type, handler_id in self._handler_ids.items():
                self._event_bus.unsubscribe(event_type, handler_id)
            self._handler_ids.clear()
            self._event_bus = None

    def _handle_event(self, event: SpaceEvent) -> None:
        """
        Handle a space event.

        Args:
            event: The event to handle
        """
        if not self._config.enabled or not self._config.auto_commit:
            return

        space_id = event.space_id
        if not space_id:
            return

        directory = self._directory_resolver(space_id)
        if not directory:
            logger.warning(f"No directory found for space {space_id}")
            return

        try:
            self._commit_for_event(event, directory)
        except Exception as e:
            logger.error(f"Failed to commit for event {event.event_type}: {e}")

    def _commit_for_event(self, event: SpaceEvent, directory: Path) -> Optional[Commit]:
        """
        Create a commit for an event.

        Args:
            event: The triggering event
            directory: Space directory

        Returns:
            The created commit, or None if no commit was made
        """
        # Ensure history is initialized
        if not self._backend.is_initialized(directory):
            self._backend.initialize(directory, self._config)

        # Check for changes
        if not self._backend.has_changes(directory):
            return None

        # Build commit message
        message = self._build_commit_message(event)

        # Build metadata
        metadata = {
            "space_id": event.space_id,
            "event_type": event.event_type.value,
            "event_id": event.event_id,
        }

        # Get specific files to commit if available
        files = None
        if event.payload:
            doc_path = event.payload.get("space_path") or event.payload.get("path")
            if doc_path:
                # Normalize path
                if doc_path.startswith("/"):
                    doc_path = doc_path[1:]
                files = [doc_path]

        # Create commit
        author = f"{self._config.author_name} <{self._config.author_email}>"
        return self._backend.commit(
            directory=directory,
            message=message,
            files=files,
            author=author,
            metadata=metadata,
        )

    def _build_commit_message(self, event: SpaceEvent) -> str:
        """
        Build a commit message for an event.

        Args:
            event: The event

        Returns:
            Commit message string
        """
        payload = event.payload or {}

        if event.event_type == SpaceEventType.DOCUMENT_ADDED:
            path = payload.get("space_path", "document")
            return f"Add document: {path}"

        elif event.event_type == SpaceEventType.DOCUMENT_REMOVED:
            path = payload.get("space_path", "document")
            return f"Remove document: {path}"

        elif event.event_type == SpaceEventType.DOCUMENT_CONTENT_CHANGED:
            doc_id = payload.get("document_id", "document")
            return f"Update document: {doc_id}"

        elif event.event_type == SpaceEventType.DOCUMENT_MOVED:
            old_path = payload.get("old_path", "old")
            new_path = payload.get("new_path", "new")
            return f"Move document: {old_path} -> {new_path}"

        else:
            return f"Auto-commit: {event.event_type.value}"

    def commit_changes(
        self,
        space_id: str,
        message: str,
        files: Optional[list] = None,
    ) -> Optional[Commit]:
        """
        Manually trigger a commit.

        Args:
            space_id: Space identifier
            message: Commit message
            files: Specific files to commit

        Returns:
            The created commit, or None if failed
        """
        directory = self._directory_resolver(space_id)
        if not directory:
            return None

        if not self._backend.is_initialized(directory):
            self._backend.initialize(directory, self._config)

        if not self._backend.has_changes(directory):
            return None

        author = f"{self._config.author_name} <{self._config.author_email}>"
        return self._backend.commit(
            directory=directory,
            message=message,
            files=files,
            author=author,
            metadata={"space_id": space_id, "manual": True},
        )


class HistoryEventCoordinator:
    """
    Coordinates history tracking across multiple spaces.

    Manages event handlers for spaces with history enabled.
    """

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

        Args:
            backend: History backend
            event_bus: Event bus for subscriptions
            directory_resolver: Function to resolve space_id to directory
        """
        self._backend = backend
        self._event_bus = event_bus
        self._directory_resolver = directory_resolver
        self._handlers: Dict[str, GitHistoryEventHandler] = {}

    def enable_history(self, space_id: str, config: HistoryConfig) -> None:
        """
        Enable history tracking for a space.

        Args:
            space_id: Space identifier
            config: History configuration
        """
        if space_id in self._handlers:
            return

        handler = GitHistoryEventHandler(
            backend=self._backend,
            config=config,
            directory_resolver=self._directory_resolver,
        )
        handler.register(self._event_bus)
        self._handlers[space_id] = handler

        # Initialize git in the space directory
        directory = self._directory_resolver(space_id)
        if directory:
            self._backend.initialize(directory, config)

    def disable_history(self, space_id: str) -> None:
        """
        Disable history tracking for a space.

        Args:
            space_id: Space identifier
        """
        handler = self._handlers.pop(space_id, None)
        if handler:
            handler.unregister()

    def is_enabled(self, space_id: str) -> bool:
        """Check if history is enabled for a space."""
        return space_id in self._handlers

    def get_handler(self, space_id: str) -> Optional[GitHistoryEventHandler]:
        """Get the event handler for a space."""
        return self._handlers.get(space_id)
