# Copyright 2026 Flyto2. Licensed under Apache-2.0. See LICENSE.

"""
Hook Base Classes

Abstract base class and null implementation for executor hooks.
"""

from abc import ABC
from typing import Optional, Callable, Any

from .models import HookContext, HookResult


class ExecutorHooks(ABC):
    """
    Abstract base class for executor hooks.

    Provides extension points for:
    - Module missing handling
    - Pre/post execution
    - Error handling
    - Workflow lifecycle

    All methods have default implementations that do nothing,
    allowing subclasses to override only what they need.
    """

    def on_workflow_start(self, context: HookContext) -> HookResult:
        """
        Called when workflow execution begins.

        Args:
            context: Execution context with workflow info

        Returns:
            HookResult indicating how to proceed
        """
        return HookResult.continue_execution()

    def on_workflow_complete(self, context: HookContext) -> None:
        """
        Called when workflow execution completes successfully.

        Args:
            context: Execution context with final state
        """
        pass

    def on_workflow_failed(self, context: HookContext) -> None:
        """
        Called when workflow execution fails.

        Args:
            context: Execution context with error info
        """
        pass

    def on_module_missing(self, context: HookContext) -> HookResult:
        """
        Called when a module is not found.

        This is a key extension point for:
        - Auto-installation of modules
        - Module substitution
        - Graceful degradation

        Args:
            context: Execution context with module_id

        Returns:
            HookResult (SKIP, SUBSTITUTE, or ABORT)
        """
        return HookResult.abort_execution(f"Module not found: {context.module_id}")

    def on_pre_execute(self, context: HookContext) -> HookResult:
        """
        Called before each step execution.

        Allows:
        - Parameter modification via metadata
        - Step skipping
        - Execution blocking

        Args:
            context: Execution context with step info

        Returns:
            HookResult indicating how to proceed
        """
        return HookResult.continue_execution()

    def on_post_execute(self, context: HookContext) -> HookResult:
        """
        Called after each step execution (success or failure).

        Allows:
        - Result transformation
        - Metric collection
        - Conditional retry

        Args:
            context: Execution context with result/error

        Returns:
            HookResult indicating how to proceed
        """
        return HookResult.continue_execution()

    def on_error(self, context: HookContext) -> HookResult:
        """
        Called when a step execution fails.

        Allows:
        - Error recovery
        - Retry logic
        - Error transformation

        Args:
            context: Execution context with error info

        Returns:
            HookResult (RETRY, SKIP, SUBSTITUTE, or ABORT)
        """
        return HookResult.continue_execution()

    def on_retry(self, context: HookContext) -> HookResult:
        """
        Called before a retry attempt.

        Args:
            context: Execution context with retry info

        Returns:
            HookResult indicating whether to proceed with retry
        """
        return HookResult.continue_execution()

    def create_step_notifier(self, step_id: str) -> Optional[Callable]:
        """
        Create a real-time notification callback for a step.

        Used by agent modules to stream intermediate events (tool calls,
        iterations) during execution. Override in subclasses to broadcast
        events via WebSocket, SSE, or other transports.

        Args:
            step_id: The step ID requesting a notifier

        Returns:
            An async callable(event_type: str, data: dict) or None
        """
        return None


class NullHooks(ExecutorHooks):
    """
    No-op hooks implementation.

    Used as default when no hooks are configured.
    All methods return continue/do nothing.
    """
    pass
