<?php

declare(strict_types=1);

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

use Drupal\flowdrop\Constants\ReservedName;
use Drupal\flowdrop\DTO\ParameterBagInterface;
use Drupal\flowdrop\Plugin\FlowDropNodeProcessor\ResumableNodeProcessorInterface;
use Drupal\flowdrop_interrupt\Exception\InterruptRequiredException;
use Drupal\flowdrop_interrupt\Message\HitlInterruptMessage;
use Drupal\flowdrop_interrupt\Plugin\FlowDropNodeProcessor\ConfirmationNode;
use Drupal\flowdrop_orchestration\DTO\ResolvedInterruptInterface;
use Drupal\Tests\UnitTestCase;
use Prophecy\PhpUnit\ProphecyTrait;

/**
 * Tests the ConfirmationNode resume() hook.
 *
 * @coversDefaultClass \Drupal\flowdrop_interrupt\Plugin\FlowDropNodeProcessor\ConfirmationNode
 * @group flowdrop_interrupt
 */
class ConfirmationNodeResumeTest extends UnitTestCase {

  use ProphecyTrait;

  /**
   * The node opts into the Phase 2 resume hook.
   */
  public function testImplementsResumableInterface(): void {
    $node = new ConfirmationNode([], 'confirmation', []);
    $this->assertInstanceOf(ResumableNodeProcessorInterface::class, $node);
  }

  /**
   * The process() method throws a descriptor exception with the schema.
   *
   * No __interrupt_id__ check inside process() — that logic moved to
   * resume(). The runtime drives the dispatch.
   */
  public function testProcessThrowsDescriptor(): void {
    $node = new ConfirmationNode([], 'confirmation', []);
    $params = $this->prophesize(ParameterBagInterface::class);
    $params->getString('message', 'Do you want to continue?')->willReturn('Approve publish?');
    $params->getString('confirm_label', 'Yes')->willReturn('Approve');
    $params->getString('cancel_label', 'No')->willReturn('Reject');

    try {
      $node->process($params->reveal());
      $this->fail('Expected InterruptRequiredException');
    }
    catch (InterruptRequiredException $e) {
      $message = $e->getInterruptMessage();
      $this->assertInstanceOf(HitlInterruptMessage::class, $message);
      $this->assertSame('Approve publish?', $message->getMessage());
      $this->assertSame([
        'type' => 'boolean',
        'presentation' => 'confirmation',
        'confirmLabel' => 'Approve',
        'declineLabel' => 'Reject',
      ], $message->schema);
    }
  }

  /**
   * The resume() method reads response_data and emits confirmed output.
   *
   * Boolean true → confirmed=true and active_branches="true".
   */
  public function testResumeWithBooleanTrueResponse(): void {
    $node = new ConfirmationNode([], 'confirmation', []);
    $params = $this->prophesize(ParameterBagInterface::class);

    $interrupt = $this->prophesize(ResolvedInterruptInterface::class);
    $interrupt->getResponseData()->willReturn(TRUE);
    $interrupt->getResolvedAt()->willReturn(1700000000);
    $interrupt->getResolvedBy()->willReturn(42);
    $interrupt->getInterruptId()->willReturn('uuid-1');

    $output = $node->resume($interrupt->reveal(), $params->reveal());

    $this->assertTrue($output['confirmed']);
    $this->assertSame(1700000000, $output['response_time']);
    $this->assertSame(42, $output['user_id']);
    $this->assertSame('uuid-1', $output['interrupt_id']);
    $this->assertSame('true', $output[ReservedName::ACTIVE_BRANCHES]);
  }

  /**
   * The resume() method handles string "false" — confirmed=false.
   */
  public function testResumeWithStringFalse(): void {
    $node = new ConfirmationNode([], 'confirmation', []);
    $params = $this->prophesize(ParameterBagInterface::class);

    $interrupt = $this->prophesize(ResolvedInterruptInterface::class);
    $interrupt->getResponseData()->willReturn('false');
    $interrupt->getResolvedAt()->willReturn(NULL);
    $interrupt->getResolvedBy()->willReturn(NULL);
    $interrupt->getInterruptId()->willReturn('uuid-2');

    $output = $node->resume($interrupt->reveal(), $params->reveal());

    $this->assertFalse($output['confirmed']);
    $this->assertSame('false', $output[ReservedName::ACTIVE_BRANCHES]);
  }

  /**
   * The resume() method handles wrapped `{value: true}` payloads.
   */
  public function testResumeWithWrappedValue(): void {
    $node = new ConfirmationNode([], 'confirmation', []);
    $params = $this->prophesize(ParameterBagInterface::class);

    $interrupt = $this->prophesize(ResolvedInterruptInterface::class);
    $interrupt->getResponseData()->willReturn(['value' => 'true']);
    $interrupt->getResolvedAt()->willReturn(1700000000);
    $interrupt->getResolvedBy()->willReturn(NULL);
    $interrupt->getInterruptId()->willReturn('uuid-3');

    $output = $node->resume($interrupt->reveal(), $params->reveal());

    $this->assertTrue($output['confirmed']);
  }

}
