<?php

declare(strict_types=1);

namespace Drupal\Tests\flowdrop_interrupt\Unit\Exception;

use Drupal\flowdrop_interrupt\Entity\FlowDropInterruptInterface;
use Drupal\flowdrop_interrupt\Enum\InterruptType;
use Drupal\flowdrop_interrupt\Exception\InterruptRequiredException;
use Drupal\flowdrop_interrupt\Message\HitlInterruptMessage;
use Drupal\Tests\UnitTestCase;
use Prophecy\PhpUnit\ProphecyTrait;

/**
 * Tests InterruptRequiredException's dual payload shapes.
 *
 * @coversDefaultClass \Drupal\flowdrop_interrupt\Exception\InterruptRequiredException
 * @group flowdrop_interrupt
 */
class InterruptRequiredExceptionTest extends UnitTestCase {

  use ProphecyTrait;

  /**
   * Entity shape: constructor accepts a pre-persisted entity.
   *
   * GetInterrupt() returns the entity, getInterruptMessage() returns NULL.
   * getInterruptId() and getInterruptType() read from the entity directly.
   */
  public function testEntityShape(): void {
    $entity = $this->prophesize(FlowDropInterruptInterface::class);
    $entity->uuid()->willReturn('uuid-entity');
    $entity->getType()->willReturn(InterruptType::Confirmation);

    $exception = new InterruptRequiredException($entity->reveal());

    $this->assertSame($entity->reveal(), $exception->getInterrupt());
    $this->assertNull($exception->getInterruptMessage());
    $this->assertSame('uuid-entity', $exception->getInterruptId());
    $this->assertSame('confirmation', $exception->getInterruptType());
  }

  /**
   * Message shape: constructor accepts an InterruptMessage.
   *
   * GetInterrupt() returns NULL (subscriber attaches to event, not here).
   * getInterruptMessage() returns the message. getInterruptType() falls back
   * to the message's declared type. getInterruptId() throws.
   */
  public function testMessageShape(): void {
    $message = new HitlInterruptMessage(
      type: InterruptType::SchemaForm,
      message: 'Please confirm',
      schema: ['type' => 'boolean'],
    );
    $exception = new InterruptRequiredException($message);

    $this->assertNull($exception->getInterrupt());
    $this->assertSame($message, $exception->getInterruptMessage());
    $this->assertSame('schema_form', $exception->getInterruptType());

    $this->expectException(\LogicException::class);
    $this->expectExceptionMessageMatches('/message-shape/');
    $exception->getInterruptId();
  }

  /**
   * The payload property is readonly — mutation is rejected by PHP.
   */
  public function testPayloadIsReadonly(): void {
    $message = new HitlInterruptMessage(message: 'x');
    $exception = new InterruptRequiredException($message);

    $this->expectException(\Error::class);
    // @phpstan-ignore-next-line — deliberately writing to readonly.
    $exception->payload = new HitlInterruptMessage(message: 'y');
  }

  /**
   * Execution context defaults to empty and is accessible via getter.
   */
  public function testExecutionContext(): void {
    $message = new HitlInterruptMessage(message: 'x');
    $exception = new InterruptRequiredException($message, ['key' => 'val']);
    $this->assertSame(['key' => 'val'], $exception->getExecutionContext());
  }

  /**
   * ToArray() serializes entity and message shapes differently.
   *
   * Entity: full entity serialized via toApiArray. Message: minimal message
   * snapshot — the full entity lives on the dispatched event.
   */
  public function testToArray(): void {
    $entity = $this->prophesize(FlowDropInterruptInterface::class);
    $entity->toApiArray()->willReturn(['id' => 'uuid-entity', 'type' => 'confirmation']);
    $entityException = new InterruptRequiredException($entity->reveal());
    $this->assertSame(
      ['interrupt' => ['id' => 'uuid-entity', 'type' => 'confirmation'], 'context' => []],
      $entityException->toArray(),
    );

    $message = new HitlInterruptMessage(
      type: InterruptType::SchemaForm,
      message: 'm',
    );
    $messageException = new InterruptRequiredException($message);
    $this->assertSame(
      [
        'message' => ['type' => 'schema_form', 'message' => 'm'],
        'context' => [],
      ],
      $messageException->toArray(),
    );
  }

}
