<?php

declare(strict_types=1);

namespace Drupal\Tests\flowdrop_stategraph\Kernel;

use Drupal\Tests\flowdrop\Kernel\WorkflowExecutionTestBase;
use Drupal\flowdrop_pipeline\Constants\PipelineStatus;

/**
 * Pins the StateAwareProcessorInterface injection contract.
 *
 * Processors implementing StateAwareProcessorInterface must receive the
 * current GraphState via setGraphState() before process() — the supported
 * replacement for reading the reserved `__data__`/`__state__` input
 * parameters. The fixture also appends a plain-array message through a
 * state_update, covering the messages channel normalization in
 * GraphState::withUpdate() at the integration level.
 *
 * @group flowdrop_stategraph
 */
class StateGraphStateInjectionTest extends WorkflowExecutionTestBase {

  /**
   * {@inheritdoc}
   */
  protected static $modules = [
    'flowdrop_stategraph_test',
  ];

  /**
   * {@inheritdoc}
   */
  protected function setUp(): void {
    parent::setUp();
    $this->installConfig(['flowdrop_stategraph_test']);
  }

  /**
   * A state-aware processor receives the graph state before process().
   *
   * Two chained probes: the first sees the initial (empty-messages) state
   * and appends a plain-array message via state_update; the second must see
   * that message as a normalized Message object — proving both the
   * setGraphState() injection and that orchestrator-applied updates are
   * visible to downstream state-aware nodes.
   */
  public function testStateAwareProcessorReceivesGraphState(): void {
    $workflow = $this->createStateGraphWorkflow('sg_state_injection', [
      $this->probeNode('probe_one'),
      $this->probeNode('probe_two'),
    ], [
      $this->edge('probe_one', 'thread_id', 'probe_two', 'input'),
    ]);

    $result = $this->launchAndWait($workflow);

    $pipeline = $this->loadPipeline($result->pipelineId);
    $this->assertSame(PipelineStatus::COMPLETED, $result->status, $this->jobTrail($pipeline));

    $firstOutput = $this->jobOutputForNode($pipeline, 'probe_one');
    $this->assertTrue($firstOutput['has_state'] ?? NULL,
      'setGraphState() must be called before process().');
    $this->assertNotSame('', $firstOutput['thread_id'] ?? '',
      'The injected state must carry the run thread ID.');
    $this->assertSame(0, $firstOutput['message_count'] ?? NULL);

    $secondOutput = $this->jobOutputForNode($pipeline, 'probe_two');
    $this->assertTrue($secondOutput['has_state'] ?? NULL);
    $this->assertSame(1, $secondOutput['message_count'] ?? NULL,
      'The first probe\'s plain-array message append must be visible downstream.');
    $this->assertSame('probe saw 0 messages', $secondOutput['last_message'] ?? NULL,
      'The appended plain array must be normalized to a Message object.');
  }

  /**
   * Builds a state_probe node definition.
   *
   * @param string $id
   *   The node id.
   *
   * @return array<string, mixed>
   *   The node definition.
   */
  protected function probeNode(string $id): array {
    return $this->node($id, 'state_probe', [], [
      'name' => 'State Probe',
      'category' => 'helpers',
    ]);
  }

}
