<?php

declare(strict_types=1);

namespace Drupal\flowdrop\Exception;

/**
 * Signals that a workflow run should stop cleanly at a terminal node.
 *
 * Thrown by a terminal node processor (TerminalNodeProcessorInterface) to end
 * the run as COMPLETED — not failed — abandoning any nodes downstream of the
 * terminal node.
 *
 * Extends \Exception DIRECTLY — not FlowDropException, which extends
 * \RuntimeException — so that node-level `catch (\RuntimeException)`
 * error-routing blocks never swallow the stop signal and turn it into an error
 * Output. This mirrors FlowDropInterruptException, which extends \Exception for
 * the same reason. Orchestrators catch this type explicitly (before their
 * generic `catch (\Exception)`), map it to a completed run, and may fold
 * getResult() into the run's results.
 *
 * @see \Drupal\flowdrop\Plugin\FlowDropNodeProcessor\TerminalNodeProcessorInterface
 *
 * @api
 */
class WorkflowStopException extends \Exception {

  /**
   * Constructs a WorkflowStopException.
   *
   * @param array<string, mixed> $result
   *   The terminal node's final payload, offered as the run's designated
   *   result value.
   * @param string|null $stopReason
   *   Optional human-readable reason for the stop, for observability.
   * @param string $message
   *   The exception message.
   */
  public function __construct(
    protected readonly array $result = [],
    protected readonly ?string $stopReason = NULL,
    string $message = 'Workflow stopped by a terminal node',
  ) {
    parent::__construct($message);
  }

  /**
   * Gets the terminal node's final payload.
   *
   * @return array<string, mixed>
   *   The designated result value, or an empty array when none was provided.
   */
  public function getResult(): array {
    return $this->result;
  }

  /**
   * Gets the human-readable stop reason.
   *
   * @return string|null
   *   The reason, or NULL when none was provided.
   */
  public function getStopReason(): ?string {
    return $this->stopReason;
  }

}
