"""Unified task management interface.

Provides abstract base class for all task runners (schedulers and queues)
and a TaskManager facade for unified lifecycle management.
"""

from abc import ABC, abstractmethod
from dataclasses import dataclass


@dataclass(frozen=True)
class TaskStatus:
    """Status of a task runner.

    Attributes:
        name: Unique identifier for the runner.
        is_running: Whether the runner is currently active.
        pending_count: Number of tasks waiting to be processed.
        processing_count: Number of tasks currently being processed.
        error: Optional error message if runner is in error state.
    """

    name: str
    is_running: bool
    pending_count: int
    processing_count: int
    error: str | None = None


class TaskRunner(ABC):
    """Abstract base class for all task runners.

    All schedulers and task queues should implement this interface
    to enable unified lifecycle management and monitoring.

    Note:
        Implementations may have different `start()` signatures based on
        their initialization needs (e.g., handler functions, services).
        Use the implementation-specific start methods for initialization,
        and use TaskManager for unified status monitoring.
    """

    @property
    @abstractmethod
    def name(self) -> str:
        """Unique identifier for this runner."""
        pass

    @abstractmethod
    def start(self, *args, **kwargs) -> None:
        """Start the task runner.

        Should be idempotent - calling start on an already running
        runner should have no effect.

        Note:
            Implementations may require additional parameters (handlers,
            services). See implementation-specific documentation.
        """
        pass

    @abstractmethod
    def stop(self, timeout: float | None = None) -> None:
        """Stop the task runner gracefully.

        Args:
            timeout: Maximum time to wait for graceful shutdown in seconds.
                    If None, use implementation default.
        """
        pass

    @property
    @abstractmethod
    def is_running(self) -> bool:
        """Check if the runner is currently active."""
        pass

    @abstractmethod
    def get_status(self) -> TaskStatus:
        """Get current status of the runner.

        Returns:
            TaskStatus with current state information.
        """
        pass


class TaskManager:
    """Unified manager for all task runners.

    Provides centralized lifecycle management and monitoring
    for all registered task runners.
    """

    def __init__(self) -> None:
        """Initialize the task manager."""
        self._runners: dict[str, TaskRunner] = {}

    def register(self, runner: TaskRunner) -> None:
        """Register a task runner.

        Args:
            runner: TaskRunner instance to register.
        """
        self._runners[runner.name] = runner

    def get_runner(self, name: str) -> TaskRunner | None:
        """Get a specific runner by name.

        Args:
            name: Name of the runner to retrieve.

        Returns:
            TaskRunner if found, None otherwise.
        """
        return self._runners.get(name)

    @property
    def runner_names(self) -> list[str]:
        """Get names of all registered runners.

        Returns:
            List of runner names.
        """
        return list(self._runners.keys())

    def start_all(self) -> None:
        """Start all registered runners that support no-argument start.

        Note:
            Runners requiring initialization parameters (like AsyncTaskQueue
            or BatchTaskQueue) should be started individually before
            registering with TaskManager.
        """
        for runner in self._runners.values():
            try:
                runner.start()
            except TypeError:
                # Runner requires arguments - skip (should be started individually)
                pass

    def stop_all(self, timeout: float = 30.0) -> None:
        """Stop all registered runners gracefully.

        Args:
            timeout: Total timeout to distribute across all runners.
        """
        if not self._runners:
            return

        per_runner_timeout = timeout / len(self._runners)
        for runner in self._runners.values():
            runner.stop(timeout=per_runner_timeout)

    def get_all_status(self) -> dict[str, TaskStatus]:
        """Get status of all registered runners.

        Returns:
            Dict mapping runner names to their status.
        """
        return {name: runner.get_status() for name, runner in self._runners.items()}
