"""ApprovalNode — human-in-the-loop approval gate for DAG workflows.

When the DAG scheduler encounters an ``ApprovalNode``, it:

1. Saves a checkpoint with all completed nodes so far.
2. Returns a ``PathwayRecord`` with ``status=AWAITING_APPROVAL``.
3. The caller polls or subscribes for approval.
4. On approval, the caller calls ``PathwayVM.resume()`` to continue.
"""

from __future__ import annotations

from typing import TYPE_CHECKING, Literal

from pydantic import Field

from core.pathways.nodes.base import NodeBase

if TYPE_CHECKING:
    from core.pathways.context import Context
    from core.pathways.models.io import NodeInputs, NodeOutputs


class ApprovalRequiredError(Exception):
    """Raised by ApprovalNode to signal the scheduler to pause."""

    def __init__(self, message: str, roles: list[str], timeout_seconds: int) -> None:
        self.approval_message = message
        self.roles = roles
        self.timeout_seconds = timeout_seconds
        super().__init__(f"Approval required: {message}")


class ApprovalNode(NodeBase):
    """Human approval gate — pauses the DAG until approved.

    Attributes
    ----------
    message
        Description of what needs approval (shown to the approver).
    roles
        Roles that can approve (e.g. ``["manager", "admin"]``).
        Empty means anyone can approve.
    timeout_seconds
        How long to wait for approval before timing out.
    """

    type: Literal["approval"] = "approval"
    message: str = "Approval required to continue."
    roles: list[str] = Field(default_factory=list)
    timeout_seconds: int = 3600

    async def compute(self, inputs: NodeInputs, _ctx: Context) -> NodeOutputs:
        """Raise ``ApprovalRequiredError`` to signal the DAG scheduler to pause.

        The scheduler catches this, saves a checkpoint, and returns
        ``AWAITING_APPROVAL`` status.
        """
        template_data = inputs.to_dict()
        formatted_message = self._format_template(self.message, template_data)

        raise ApprovalRequiredError(
            message=formatted_message,
            roles=self.roles,
            timeout_seconds=self.timeout_seconds,
        )


__all__ = ["ApprovalNode", "ApprovalRequiredError"]
