<?php

declare(strict_types=1);

namespace Drupal\Tests\flowdrop_stategraph\Unit\DTO;

use Drupal\Tests\UnitTestCase;
use Drupal\flowdrop_stategraph\DTO\ExecutionConfig;

/**
 * Tests the ExecutionConfig DTO.
 *
 * @coversDefaultClass \Drupal\flowdrop_stategraph\DTO\ExecutionConfig
 * @group flowdrop_stategraph
 */
class ExecutionConfigTest extends UnitTestCase {

  /**
   * @covers ::fromArray
   */
  public function testFromArrayCamelCase(): void {
    $config = ExecutionConfig::fromArray([
      "checkpointerType" => "entity",
      "maxIterations" => 50,
      "threadId" => "thread_1",
      "resumeFromCheckpoint" => "cp_9",
      "debugMode" => TRUE,
      "createCheckpoints" => FALSE,
      "maxExecutionTime" => 120.5,
    ]);

    $this->assertSame("entity", $config->checkpointerType);
    $this->assertSame(50, $config->maxIterations);
    $this->assertSame("thread_1", $config->threadId);
    $this->assertSame("cp_9", $config->resumeFromCheckpoint);
    $this->assertTrue($config->debugMode);
    $this->assertFalse($config->createCheckpoints);
    $this->assertSame(120.5, $config->maxExecutionTime);
  }

  /**
   * The session/form shape (snake_case) must be honored.
   *
   * SessionExecutionService and the orchestrator settings form produce
   * snake_case keys; these were silently dropped before the fromArray fix,
   * so chat-session thread continuity never worked.
   *
   * @covers ::fromArray
   */
  public function testFromArraySnakeCase(): void {
    $config = ExecutionConfig::fromArray([
      "checkpointer_type" => "entity",
      "max_iterations" => 25,
      "thread_id" => "playground_session_7",
      "create_checkpoints" => TRUE,
      "debug_mode" => TRUE,
      "timeout" => 300,
    ]);

    $this->assertSame("entity", $config->checkpointerType);
    $this->assertSame(25, $config->maxIterations);
    $this->assertSame("playground_session_7", $config->threadId);
    $this->assertTrue($config->createCheckpoints);
    $this->assertTrue($config->debugMode);
    $this->assertSame(300.0, $config->maxExecutionTime);
  }

  /**
   * The camelCase shape wins when both shapes carry the same key.
   *
   * @covers ::fromArray
   */
  public function testCamelCaseWinsTies(): void {
    $config = ExecutionConfig::fromArray([
      "threadId" => "camel",
      "thread_id" => "snake",
      "maxIterations" => 10,
      "max_iterations" => 99,
    ]);

    $this->assertSame("camel", $config->threadId);
    $this->assertSame(10, $config->maxIterations);
  }

  /**
   * Empty strings and non-positive timeouts are treated as absent.
   *
   * @covers ::fromArray
   */
  public function testFromArrayNormalizesEmptyValues(): void {
    $config = ExecutionConfig::fromArray([
      "thread_id" => "",
      "resume_from_checkpoint" => "",
      "timeout" => 0,
    ]);

    $this->assertNull($config->threadId);
    $this->assertNull($config->resumeFromCheckpoint);
    $this->assertNull($config->maxExecutionTime);

    $config = ExecutionConfig::fromArray(["timeout" => "not-a-number"]);
    $this->assertNull($config->maxExecutionTime);
  }

  /**
   * @covers ::toArray
   * @covers ::fromArray
   */
  public function testToArrayRoundTrip(): void {
    $config = new ExecutionConfig(
      checkpointerType: "entity",
      maxIterations: 42,
      threadId: "t1",
      maxExecutionTime: 60.0,
    );

    $rebuilt = ExecutionConfig::fromArray($config->toArray());

    $this->assertEquals($config->toArray(), $rebuilt->toArray());
  }

  /**
   * @covers ::withThreadId
   */
  public function testWithThreadIdPreservesAllFields(): void {
    $config = new ExecutionConfig(
      checkpointerType: "entity",
      maxIterations: 7,
      debugMode: TRUE,
      createCheckpoints: FALSE,
      metadata: ["k" => "v"],
      maxExecutionTime: 30.0,
    );

    $updated = $config->withThreadId("new_thread");

    $this->assertSame("new_thread", $updated->threadId);
    $this->assertSame("entity", $updated->checkpointerType);
    $this->assertSame(7, $updated->maxIterations);
    $this->assertTrue($updated->debugMode);
    $this->assertFalse($updated->createCheckpoints);
    $this->assertSame(["k" => "v"], $updated->metadata);
    $this->assertSame(30.0, $updated->maxExecutionTime);
  }

}
