# EXECUTOR-COUPLED: This node is tightly coupled to the workflow executor
# in flowire-app. The executor detects this node in the flow definition,
# redirects errors to it, and populates its inputs directly. The execute()
# method simply passes inputs through as output. Changes here must be
# coordinated with workflow_executor.py in flowire-app.

"""Catch Error node for flow-level error handling."""

from typing import Any

from flowire_sdk import BaseNode, BaseNodeOutput, HandleConfig, NodeExecutionContext, NodeHandles, NodeMetadata
from pydantic import BaseModel, Field


class CatchErrorErrorDetail(BaseModel):
    """Details about a single error."""

    error_message: str = Field(..., description="The error message")
    failed_node_id: str = Field(..., description="ID of the node that errored")
    failed_node_label: str = Field(..., description="Human-readable label of the failed node")
    failed_node_type: str = Field(..., description="Registry key of the failed node (e.g., http_request)")


class CatchErrorInput(BaseModel):
    """Input schema for Catch Error node.

    These fields are populated by the executor, not by the user.
    """

    error_message: str = Field(
        default="", description="Error message from the first failed node", json_schema_extra={"x-hidden": True}
    )
    failed_node_id: str = Field(
        default="", description="ID of the first node that errored", json_schema_extra={"x-hidden": True}
    )
    failed_node_label: str = Field(
        default="", description="Label of the first failed node", json_schema_extra={"x-hidden": True}
    )
    failed_node_type: str = Field(
        default="", description="Type of the first failed node", json_schema_extra={"x-hidden": True}
    )
    errors: list[CatchErrorErrorDetail] = Field(
        default_factory=list,
        description="All errors (relevant when multiple nodes fail in parallel)",
        json_schema_extra={"x-hidden": True},
    )


class CatchErrorOutput(BaseNodeOutput):
    """Output schema for Catch Error node."""

    error_message: str = Field(..., description="Error message from the first failed node")
    failed_node_id: str = Field(..., description="ID of the first node that errored")
    failed_node_label: str = Field(..., description="Label of the first failed node")
    failed_node_type: str = Field(..., description="Type of the first failed node")
    errors: list[CatchErrorErrorDetail] = Field(
        ...,
        description="All errors (relevant when multiple nodes fail in parallel)",
    )


class CatchErrorNode(BaseNode):
    """Catch errors in a workflow and allow custom error handling.

    This is a flow-level error handler. When any node in the flow errors,
    instead of the flow failing immediately, execution jumps to this node.
    Downstream nodes can then log the error, send notifications, retry, etc.

    Limited to one per flow. No input connections — it activates automatically.

    After the catch chain completes:
    - Without a Throw Error node: execution status is "completed" (recovered)
    - With a Throw Error node: execution status is "failed" (re-thrown)
    """

    input_schema = CatchErrorInput
    output_schema = CatchErrorOutput

    metadata = NodeMetadata(
        name="Catch Error",
        description="Catch workflow errors and handle them with custom logic",
        category="logic",
        icon="🛡️",
        color="#FF9800",
        handles=NodeHandles(
            inputs=[],  # No input connections — activated by the executor on error
            outputs=[HandleConfig(id="default", label="Error", position="right")],
        ),
    )

    async def execute_logic(
        self,
        validated_inputs: dict[str, Any],
        context: NodeExecutionContext,
    ) -> CatchErrorOutput:
        """Pass through error details populated by the executor."""
        return CatchErrorOutput(
            error_message=validated_inputs["error_message"],
            failed_node_id=validated_inputs["failed_node_id"],
            failed_node_label=validated_inputs["failed_node_label"],
            failed_node_type=validated_inputs["failed_node_type"],
            errors=validated_inputs["errors"],
        )
