<?php

declare(strict_types=1);

namespace Drupal\Tests\flowdrop\Unit\DTO;

use Drupal\Tests\UnitTestCase;
use Drupal\flowdrop\DTO\ExecutionContextDTO;
use Drupal\Tests\flowdrop\Unit\Traits\DtoContractTestTrait;

/**
 * Tests the ExecutionContextDTO.
 *
 * @coversDefaultClass \Drupal\flowdrop\DTO\ExecutionContextDTO
 * @group flowdrop
 */
class ExecutionContextDTOTest extends UnitTestCase {

  use DtoContractTestTrait;

  /**
   * @covers ::__construct
   * @covers ::getInitialData
   * @covers ::getWorkflowId
   * @covers ::getPipelineId
   * @covers ::getExecutionId
   * @covers ::getNodeId
   * @covers ::getMetadata
   */
  public function testConstructorAndGetters(): void {
    $dto = new ExecutionContextDTO(
      initialData: ["trigger" => "webhook"],
      workflowId: "wf_1",
      pipelineId: "pl_1",
      executionId: "exec_1",
      nodeId: "node_1",
      metadata: ["source" => "test"],
    );

    $this->assertEquals(["trigger" => "webhook"], $dto->getInitialData());
    $this->assertEquals("wf_1", $dto->getWorkflowId());
    $this->assertEquals("pl_1", $dto->getPipelineId());
    $this->assertEquals("exec_1", $dto->getExecutionId());
    $this->assertEquals("node_1", $dto->getNodeId());
    $this->assertEquals(["source" => "test"], $dto->getMetadata());
  }

  /**
   * @covers ::getInitialDataValue
   */
  public function testGetInitialDataValue(): void {
    $dto = new ExecutionContextDTO(
      initialData: ["key" => "value"],
      workflowId: "wf",
      pipelineId: "pl",
      executionId: "ex",
      nodeId: "nd",
    );
    $this->assertEquals("value", $dto->getInitialDataValue("key"));
    $this->assertNull($dto->getInitialDataValue("missing"));
    $this->assertEquals("default", $dto->getInitialDataValue("missing", "default"));
  }

  /**
   * @covers ::hasInitialDataKey
   */
  public function testHasInitialDataKey(): void {
    $dto = new ExecutionContextDTO(
      initialData: ["exists" => NULL],
      workflowId: "wf",
      pipelineId: "pl",
      executionId: "ex",
      nodeId: "nd",
    );
    $this->assertTrue($dto->hasInitialDataKey("exists"));
    $this->assertFalse($dto->hasInitialDataKey("missing"));
  }

  /**
   * @covers ::getMetadataValue
   */
  public function testGetMetadataValue(): void {
    $dto = new ExecutionContextDTO(
      initialData: [],
      workflowId: "wf",
      pipelineId: "pl",
      executionId: "ex",
      nodeId: "nd",
      metadata: ["priority" => "high"],
    );
    $this->assertEquals("high", $dto->getMetadataValue("priority"));
    $this->assertNull($dto->getMetadataValue("missing"));
  }

  /**
   * @covers ::fromArray
   */
  public function testFromArray(): void {
    $dto = ExecutionContextDTO::fromArray([
      "initial_data" => ["data" => TRUE],
      "workflow_id" => "wf_2",
      "pipeline_id" => "pl_2",
      "execution_id" => "exec_2",
      "node_id" => "node_2",
      "metadata" => ["key" => "val"],
    ]);

    $this->assertEquals(["data" => TRUE], $dto->getInitialData());
    $this->assertEquals("wf_2", $dto->getWorkflowId());
    $this->assertEquals("node_2", $dto->getNodeId());
  }

  /**
   * @covers ::fromArray
   */
  public function testFromArrayWithDefaults(): void {
    $dto = ExecutionContextDTO::fromArray([]);
    $this->assertEquals([], $dto->getInitialData());
    $this->assertEquals("", $dto->getWorkflowId());
    $this->assertEquals([], $dto->getMetadata());
  }

  /**
   * @covers ::toArray
   */
  public function testToArray(): void {
    $dto = new ExecutionContextDTO(
      initialData: ["key" => "val"],
      workflowId: "wf",
      pipelineId: "pl",
      executionId: "ex",
      nodeId: "nd",
      metadata: ["m" => 1],
    );

    $array = $dto->toArray();
    $this->assertArrayHasKey("initial_data", $array);
    $this->assertArrayHasKey("workflow_id", $array);
    $this->assertArrayHasKey("pipeline_id", $array);
    $this->assertArrayHasKey("execution_id", $array);
    $this->assertArrayHasKey("metadata", $array);
  }

  /**
   * @covers ::toArray
   * @covers ::fromArray
   */
  public function testRoundTrip(): void {
    $dto = new ExecutionContextDTO(
      initialData: ["trigger" => "cron"],
      workflowId: "wf_rt",
      pipelineId: "pl_rt",
      executionId: "exec_rt",
      nodeId: "node_rt",
      metadata: ["env" => "test"],
    );
    $this->assertRoundTripSerialization($dto);
  }

}
