<?php

declare(strict_types=1);

namespace Drupal\flowdrop_stategraph\Exception;

use Drupal\flowdrop_orchestration\Exception\FlowDropInterruptException;
use Drupal\flowdrop_stategraph\DTO\Checkpoint;

/**
 * Exception thrown when human approval is required to continue.
 *
 * This exception is thrown by approval gate nodes to pause execution
 * and wait for human review. It includes the current checkpoint so
 * the state can be inspected before approval.
 *
 * @internal This class is not part of the public API and may change without notice.
 */
class ApprovalRequiredException extends FlowDropInterruptException {

  /**
   * Constructs a new ApprovalRequiredException.
   *
   * @param string $nodeId
   *   The approval gate node ID.
   * @param \Drupal\flowdrop_stategraph\DTO\Checkpoint $checkpoint
   *   The checkpoint at the approval gate.
   * @param string $message
   *   Optional custom message.
   * @param \Throwable|null $previous
   *   Previous exception for chaining.
   */
  public function __construct(
    public readonly string $nodeId,
    public readonly Checkpoint $checkpoint,
    string $message = "",
    ?\Throwable $previous = NULL,
  ) {
    if ($message === "") {
      $message = sprintf(
        "Approval required at node '%s'. Checkpoint ID: %s. " .
        "Review the current state and approve or reject to continue.",
        $nodeId,
        $checkpoint->id
      );
    }

    parent::__construct($message, 0, $previous);
  }

  /**
   * {@inheritdoc}
   */
  public function getInterruptId(): string {
    return $this->checkpoint->id;
  }

  /**
   * {@inheritdoc}
   */
  public function getInterruptType(): string {
    return "approval";
  }

  /**
   * {@inheritdoc}
   */
  public function getExecutionContext(): array {
    return [
      "node_id" => $this->nodeId,
      "thread_id" => $this->checkpoint->threadId,
      "checkpoint_id" => $this->checkpoint->id,
      "state" => $this->checkpoint->state->toArray(),
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function toArray(): array {
    return [
      "interrupt_id" => $this->getInterruptId(),
      "interrupt_type" => $this->getInterruptType(),
      "node_id" => $this->nodeId,
      "thread_id" => $this->checkpoint->threadId,
      "checkpoint_id" => $this->checkpoint->id,
      "message" => $this->getMessage(),
      "context" => $this->getExecutionContext(),
    ];
  }

}
