"""Builder for constructing FlowResult objects from execution outcomes.

This module isolates purely transformational logic for converting internal
execution state into public-facing FlowResult objects. It strictly follows
SRP by handling only data transformation, leaving side effects (logging,
streaming) to the executor.
"""

from datetime import UTC, datetime
from typing import Any

from agent_flows.core.flow_engine import ExecutionResult
from agent_flows.models.execution import (
    ExecutionError,
    FlowResult,
    FlowResultData,
    FlowResultType,
)
from agent_flows.models.flow import FlowConfig
from agent_flows.models.test import TestManifest


class FlowResultBuilder:
    """Builder for constructing FlowResult objects.

    Design Principles:
        - Pure Transformations: Methods only operate on inputs
        - No Side Effects: Does NOT log errors or emit streaming events
        - Stateless: No instance state beyond initialization
    """

    def build_final_result(
        self,
        execution_result: ExecutionResult,
        test_manifest: TestManifest | None,
        flow_id: str,
    ) -> FlowResult:
        """Convert ExecutionResult to FlowResult.

        Args:
            execution_result: Result from FlowOrchestrator execution
            test_manifest: Optional test manifest for test mode
            flow_id: Flow identifier

        Returns:
            FlowResult with execution results
        """
        # Determine result type and data based on execution outcome
        target_step_id = None
        target_step_type = None

        if not execution_result.success:
            result_type = FlowResultType.ERROR
            primary_error = execution_result.errors[0] if execution_result.errors else None
            result_data = self._format_execution_error(primary_error)
        elif execution_result.target_step_result is not None:
            result_type = FlowResultType.HALTED_BY_TARGET
            result_data = execution_result.target_step_result.data
            # Populate target fields when test mode halted at target
            if test_manifest:
                target_step_id = execution_result.target_step_result.step_id
                target_step_type = execution_result.target_step_result.step_type
        elif execution_result.direct_output is not None:
            result_type = FlowResultType.DIRECT_OUTPUT
            result_data = execution_result.direct_output
        else:
            result_type = FlowResultType.COMPLETED
            result_data = execution_result.final_variables

        # Extract flow_as_output from finish metadata if present
        flow_as_output = False

        if execution_result.finish_metadata:
            flow_as_output = execution_result.finish_metadata.get("flow_as_output", False)

        return FlowResult(
            flow_id=flow_id,
            success=execution_result.success,
            execution_time=execution_result.execution_time,
            result=FlowResultData(
                type=result_type,
                data=result_data,
                target_step_id=target_step_id,
                target_step_type=target_step_type,
            ),
            errors=execution_result.errors,
            trace=execution_result.trace,
            flow_as_output=flow_as_output,
        )

    def build_error_result(
        self,
        error: Exception,
        flow_source: FlowConfig | str,
        variables: dict[str, Any] | None,
    ) -> FlowResult:
        """Build error result for failed execution.

        Args:
            error: The exception that occurred
            flow_source: Original flow source for fallback ID
            variables: Original variables for error context

        Returns:
            FlowResult with error information
        """
        # Determine flow ID for error reporting
        if isinstance(flow_source, FlowConfig):
            flow_id = flow_source.uuid
        elif isinstance(flow_source, str):
            flow_id = flow_source
        else:
            flow_id = "unknown"

        # Create error result
        execution_errors = [
            ExecutionError(
                message=str(error),
                step_id=None,
                step_index=None,
                step_type=None,
                error_type=type(error).__name__,
                context_variables=variables,
                timestamp=datetime.now(UTC),
            )
        ]

        return FlowResult(
            flow_id=flow_id,
            success=False,
            execution_time=0.0,
            result=FlowResultData(
                type=FlowResultType.ERROR, data=self._format_execution_error(execution_errors[0])
            ),
            errors=execution_errors,
            trace=None,
        )

    @staticmethod
    def _format_execution_error(error: ExecutionError | None) -> dict[str, Any]:
        """Create a frontend-friendly error payload from an ExecutionError."""
        if error is None:
            return {"message": "Flow execution failed", "step_id": ""}

        payload: dict[str, Any] = {
            "message": error.message,
            "step_id": error.step_id or "",
        }

        return payload
