<?php

declare(strict_types=1);

namespace Drupal\Tests\flowdrop_interrupt\Unit\DTO;

use Drupal\Tests\UnitTestCase;
use Drupal\flowdrop_interrupt\DTO\InterruptState;
use Drupal\Tests\flowdrop\Unit\Traits\DtoContractTestTrait;

/**
 * Tests the InterruptState DTO.
 *
 * @coversDefaultClass \Drupal\flowdrop_interrupt\DTO\InterruptState
 * @group flowdrop_interrupt
 */
class InterruptStateTest extends UnitTestCase {

  use DtoContractTestTrait;

  /**
   * Creates a sample InterruptState for testing.
   */
  private function createSample(): InterruptState {
    return new InterruptState(
      interruptId: 'int_001',
      interruptType: 'confirmation',
      pipelineId: 'pl_1',
      jobId: 'job_1',
      nodeId: 'node_1',
      executionId: 'exec_1',
      workflowId: 'wf_1',
      interruptData: ['prompt' => 'Approve?'],
      executionContext: ['step' => 3],
      partialResults: ['result' => 'partial'],
      createdAt: 1700000000,
    );
  }

  /**
   * @covers ::__construct
   */
  public function testConstructor(): void {
    $state = $this->createSample();
    $this->assertEquals('int_001', $state->interruptId);
    $this->assertEquals('confirmation', $state->interruptType);
    $this->assertEquals('pl_1', $state->pipelineId);
    $this->assertEquals('job_1', $state->jobId);
    $this->assertEquals('node_1', $state->nodeId);
    $this->assertEquals('exec_1', $state->executionId);
    $this->assertEquals('wf_1', $state->workflowId);
    $this->assertEquals(['prompt' => 'Approve?'], $state->interruptData);
    $this->assertEquals(InterruptState::STATUS_PENDING, $state->status);
    $this->assertNull($state->userInput);
    $this->assertNull($state->resolvedAt);
  }

  /**
   * @covers ::isPending
   * @covers ::isResolved
   */
  public function testStatusChecks(): void {
    $state = $this->createSample();
    $this->assertTrue($state->isPending());
    $this->assertFalse($state->isResolved());
  }

  /**
   * @covers ::withResolution
   */
  public function testWithResolution(): void {
    $state = $this->createSample();
    $resolved = $state->withResolution(['approved' => TRUE]);

    // Original unchanged.
    $this->assertTrue($state->isPending());
    $this->assertNull($state->userInput);

    // New instance resolved.
    $this->assertNotSame($state, $resolved);
    $this->assertTrue($resolved->isResolved());
    $this->assertFalse($resolved->isPending());
    $this->assertEquals(['approved' => TRUE], $resolved->userInput);
    $this->assertNotNull($resolved->resolvedAt);
    $this->assertEquals(InterruptState::STATUS_RESOLVED, $resolved->status);

    // Original data preserved.
    $this->assertEquals('int_001', $resolved->interruptId);
    $this->assertEquals('pl_1', $resolved->pipelineId);
  }

  /**
   * @covers ::toArray
   */
  public function testToArray(): void {
    $state = $this->createSample();
    $array = $state->toArray();

    $this->assertEquals('int_001', $array['interrupt_id']);
    $this->assertEquals('confirmation', $array['interrupt_type']);
    $this->assertEquals('pl_1', $array['pipeline_id']);
    $this->assertEquals('pending', $array['status']);
    $this->assertNull($array['user_input']);
    $this->assertEquals(1700000000, $array['created_at']);
  }

  /**
   * @covers ::fromArray
   */
  public function testFromArray(): void {
    $state = InterruptState::fromArray([
      'interrupt_id' => 'int_002',
      'interrupt_type' => 'text',
      'pipeline_id' => 'pl_2',
      'job_id' => 'job_2',
      'node_id' => 'node_2',
      'execution_id' => 'exec_2',
      'workflow_id' => 'wf_2',
      'interrupt_data' => ['prompt' => 'Enter text'],
      'execution_context' => [],
      'partial_results' => [],
      'status' => 'resolved',
      'user_input' => ['text' => 'Hello'],
    ]);

    $this->assertEquals('int_002', $state->interruptId);
    $this->assertEquals('resolved', $state->status);
    $this->assertEquals(['text' => 'Hello'], $state->userInput);
  }

  /**
   * @covers ::fromArray
   */
  public function testFromArrayDefaults(): void {
    $state = InterruptState::fromArray([]);
    $this->assertEquals('', $state->interruptId);
    $this->assertEquals(InterruptState::STATUS_PENDING, $state->status);
    $this->assertNull($state->userInput);
  }

  /**
   * @covers ::toArray
   * @covers ::fromArray
   */
  public function testRoundTrip(): void {
    $state = $this->createSample();
    $this->assertRoundTripSerialization($state);
  }

  /**
   * Tests status constants are stable.
   */
  public function testStatusConstants(): void {
    $this->assertEquals('pending', InterruptState::STATUS_PENDING);
    $this->assertEquals('resolved', InterruptState::STATUS_RESOLVED);
    $this->assertEquals('expired', InterruptState::STATUS_EXPIRED);
  }

}
