<?php

declare(strict_types=1);

namespace Drupal\Tests\flowdrop_node_processor\Unit\Plugin\FlowDropNodeProcessor;

use Drupal\flowdrop\DTO\ParameterBag;
use Drupal\flowdrop\Exception\WorkflowStopException;
use Drupal\flowdrop\Plugin\FlowDropNodeProcessor\StartNodeProcessorInterface;
use Drupal\flowdrop\Plugin\FlowDropNodeProcessor\TerminalNodeProcessorInterface;
use Drupal\flowdrop_node_processor\Plugin\FlowDropNodeProcessor\Stop;
use PHPUnit\Framework\TestCase;

/**
 * Tests the Stop terminal node processor.
 *
 * @coversDefaultClass \Drupal\flowdrop_node_processor\Plugin\FlowDropNodeProcessor\Stop
 * @group flowdrop_node_processor
 */
class StopTest extends TestCase {

  /**
   * Creates a Stop processor instance.
   *
   * @return \Drupal\flowdrop_node_processor\Plugin\FlowDropNodeProcessor\Stop
   *   The processor instance.
   */
  private function createProcessor(): Stop {
    return new Stop([], 'stop', ['id' => 'stop']);
  }

  /**
   * The Stop node is a terminal node, and NOT a start node.
   *
   * It is reached via the normal flow (so it keeps its trigger input), unlike
   * a start node.
   */
  public function testMarkerInterfaces(): void {
    $processor = $this->createProcessor();
    $this->assertInstanceOf(TerminalNodeProcessorInterface::class, $processor);
    $this->assertNotInstanceOf(StartNodeProcessorInterface::class, $processor);
  }

  /**
   * A bare Stop node throws a clean stop with no payload.
   *
   * @covers ::process
   */
  public function testProcessThrowsCleanStop(): void {
    $processor = $this->createProcessor();
    try {
      $processor->process(new ParameterBag([]));
      $this->fail('Stop::process() must throw WorkflowStopException.');
    }
    catch (WorkflowStopException $e) {
      $this->assertSame([], $e->getResult());
      $this->assertNull($e->getStopReason());
    }
  }

  /**
   * A wired array result and a configured reason are carried on the exception.
   *
   * @covers ::process
   */
  public function testProcessCarriesResultAndReason(): void {
    $processor = $this->createProcessor();
    try {
      $processor->process(new ParameterBag([
        'result' => ['answer' => 42],
        'reason' => 'budget reached',
      ]));
      $this->fail('Stop::process() must throw WorkflowStopException.');
    }
    catch (WorkflowStopException $e) {
      $this->assertSame(['answer' => 42], $e->getResult());
      $this->assertSame('budget reached', $e->getStopReason());
      $this->assertSame('budget reached', $e->getMessage());
    }
  }

  /**
   * A scalar wired result is normalized into the typed array payload.
   *
   * @covers ::process
   */
  public function testProcessWrapsScalarResult(): void {
    $processor = $this->createProcessor();
    try {
      $processor->process(new ParameterBag(['result' => 'done']));
      $this->fail('Stop::process() must throw WorkflowStopException.');
    }
    catch (WorkflowStopException $e) {
      $this->assertSame(['value' => 'done'], $e->getResult());
      $this->assertNull($e->getStopReason());
    }
  }

}
