"""Execution strategies for building flow execution pipelines."""

from __future__ import annotations

from typing import TYPE_CHECKING, Protocol

from agent_flows.core.flow_engine.pipeline import ExecutionPipeline
from agent_flows.core.flow_engine.stages import (
    CredentialResolutionStage,
    ExecutorStage,
    InterpolationStage,
    PinCheckStage,
    TargetMarkingStage,
    TargetModeStage,
    TargetPropagationStage,
    TracingStage,
    VariableAssignmentStage,
)
from agent_flows.executors.registry import ExecutorRegistry

if TYPE_CHECKING:
    from agent_flows.models.test import TestManifest
    from agent_flows.services.realtimex import CredentialResolver


class ExecutionStrategy(Protocol):
    """Protocol for strategies that construct execution pipelines."""

    def build_pipeline(
        self,
        executor_registry: ExecutorRegistry,
        credential_resolver: CredentialResolver | None = None,
    ) -> ExecutionPipeline:
        """Build execution pipeline for the strategy."""


class ProductionStrategy:
    """Strategy for standard production execution."""

    def build_pipeline(
        self,
        executor_registry: ExecutorRegistry,
        credential_resolver: CredentialResolver | None = None,
    ) -> ExecutionPipeline:
        """Build production pipeline without test-specific stages."""
        pipeline = ExecutionPipeline(
            stages=[
                # Pre-execution stages
                InterpolationStage(),
                CredentialResolutionStage(credential_resolver),
                # Execution stage
                ExecutorStage(executor_registry),
                # Post-execution stages
                TracingStage(),
                VariableAssignmentStage(),
            ]
        )

        # Wire pipeline to registry so composite executors can execute nested blocks.
        # This resolves the circular dependency: Pipeline needs ExecutorStage which
        # needs Registry, but Registry needs Pipeline for composite executors.
        executor_registry.set_pipeline(pipeline)

        return pipeline


class TestStrategy:
    """Strategy for test execution supporting pins and target detection."""

    def __init__(self, test_manifest: TestManifest) -> None:
        self.test_manifest = test_manifest

    def build_pipeline(
        self,
        executor_registry: ExecutorRegistry,
        credential_resolver: CredentialResolver | None = None,
    ) -> ExecutionPipeline:
        """Build test pipeline with pin and target handling stages."""
        pipeline = ExecutionPipeline(
            stages=[
                # Pre-execution stages
                PinCheckStage(self.test_manifest),
                InterpolationStage(),
                CredentialResolutionStage(credential_resolver),
                TargetModeStage(self.test_manifest),
                # Execution stage
                ExecutorStage(executor_registry),
                # Post-execution stages
                TracingStage(),
                TargetMarkingStage(self.test_manifest),
                TargetPropagationStage(),
                VariableAssignmentStage(),
            ]
        )

        # Wire pipeline to registry so composite executors can execute nested blocks.
        # This resolves the circular dependency: Pipeline needs ExecutorStage which
        # needs Registry, but Registry needs Pipeline for composite executors.
        executor_registry.set_pipeline(pipeline)

        return pipeline
