<?php

declare(strict_types=1);

namespace Drupal\flowdrop_stategraph\DTO;

/**
 * Configuration for graph execution.
 *
 * This DTO holds all configuration options for a stategraph execution,
 * including:
 * - Checkpointer type selection.
 * - Iteration limits for loop protection.
 * - Thread configuration for conversation continuity.
 * - Debug and logging options.
 *
 * Example usage:
 * @code
 * $config = new ExecutionConfig(
 *   checkpointerType: "entity",
 *   maxIterations: 50,
 *   threadId: "existing_thread_123",
 * );
 * @endcode
 *
 * @internal This class is not part of the public API and may change without notice.
 */
final class ExecutionConfig implements \JsonSerializable {

  /**
   * Constructs a new ExecutionConfig.
   *
   * @param string $checkpointerType
   *   The checkpointer type ("memory" or "entity").
   * @param int $maxIterations
   *   Maximum loop iterations before throwing exception.
   * @param string|null $threadId
   *   Optional thread ID to resume from existing conversation.
   * @param string|null $resumeFromCheckpoint
   *   Optional checkpoint ID to resume execution from.
   * @param bool $debugMode
   *   Enable verbose logging for debugging.
   * @param bool $createCheckpoints
   *   Whether to create checkpoints during execution.
   * @param array<string, mixed> $metadata
   *   Additional execution metadata.
   */
  public function __construct(
    public readonly string $checkpointerType = "memory",
    public readonly int $maxIterations = 100,
    public readonly ?string $threadId = NULL,
    public readonly ?string $resumeFromCheckpoint = NULL,
    public readonly bool $debugMode = FALSE,
    public readonly bool $createCheckpoints = TRUE,
    public readonly array $metadata = [],
  ) {}

  /**
   * Creates a config for memory-based execution (development/testing).
   *
   * @param int $maxIterations
   *   Maximum iterations.
   *
   * @return self
   *   A new ExecutionConfig with memory checkpointer.
   */
  public static function forDevelopment(int $maxIterations = 100): self {
    return new self(
      checkpointerType: "memory",
      maxIterations: $maxIterations,
      debugMode: TRUE,
      createCheckpoints: TRUE,
    );
  }

  /**
   * Creates a config for entity-based execution (production).
   *
   * @param int $maxIterations
   *   Maximum iterations.
   * @param string|null $threadId
   *   Optional thread ID for conversation continuity.
   *
   * @return self
   *   A new ExecutionConfig with entity checkpointer.
   */
  public static function forProduction(
    int $maxIterations = 100,
    ?string $threadId = NULL,
  ): self {
    return new self(
      checkpointerType: "entity",
      maxIterations: $maxIterations,
      threadId: $threadId,
      debugMode: FALSE,
      createCheckpoints: TRUE,
    );
  }

  /**
   * Creates a config for resuming from a checkpoint.
   *
   * @param string $checkpointId
   *   The checkpoint ID to resume from.
   * @param string $checkpointerType
   *   The checkpointer type to use.
   *
   * @return self
   *   A new ExecutionConfig configured for resume.
   */
  public static function forResume(
    string $checkpointId,
    string $checkpointerType = "entity",
  ): self {
    return new self(
      checkpointerType: $checkpointerType,
      resumeFromCheckpoint: $checkpointId,
      createCheckpoints: TRUE,
    );
  }

  /**
   * Creates a new config with updated thread ID.
   *
   * @param string $threadId
   *   The thread ID.
   *
   * @return self
   *   A new ExecutionConfig with updated thread ID.
   */
  public function withThreadId(string $threadId): self {
    return new self(
      checkpointerType: $this->checkpointerType,
      maxIterations: $this->maxIterations,
      threadId: $threadId,
      resumeFromCheckpoint: $this->resumeFromCheckpoint,
      debugMode: $this->debugMode,
      createCheckpoints: $this->createCheckpoints,
      metadata: $this->metadata,
    );
  }

  /**
   * Creates a new config with added metadata.
   *
   * @param array<string, mixed> $metadata
   *   Additional metadata to merge.
   *
   * @return self
   *   A new ExecutionConfig with merged metadata.
   */
  public function withMetadata(array $metadata): self {
    return new self(
      checkpointerType: $this->checkpointerType,
      maxIterations: $this->maxIterations,
      threadId: $this->threadId,
      resumeFromCheckpoint: $this->resumeFromCheckpoint,
      debugMode: $this->debugMode,
      createCheckpoints: $this->createCheckpoints,
      metadata: array_merge($this->metadata, $metadata),
    );
  }

  /**
   * Creates an ExecutionConfig from an array.
   *
   * @param array<string, mixed> $data
   *   The config data.
   *
   * @return self
   *   A new ExecutionConfig instance.
   */
  public static function fromArray(array $data): self {
    return new self(
      checkpointerType: (string) ($data["checkpointerType"] ?? "memory"),
      maxIterations: (int) ($data["maxIterations"] ?? 100),
      threadId: isset($data["threadId"]) ? (string) $data["threadId"] : NULL,
      resumeFromCheckpoint: isset($data["resumeFromCheckpoint"])
        ? (string) $data["resumeFromCheckpoint"]
        : NULL,
      debugMode: (bool) ($data["debugMode"] ?? FALSE),
      createCheckpoints: (bool) ($data["createCheckpoints"] ?? TRUE),
      metadata: $data["metadata"] ?? [],
    );
  }

  /**
   * Converts the config to an array.
   *
   * @return array<string, mixed>
   *   The config as an array.
   */
  public function toArray(): array {
    return [
      "checkpointerType" => $this->checkpointerType,
      "maxIterations" => $this->maxIterations,
      "threadId" => $this->threadId,
      "resumeFromCheckpoint" => $this->resumeFromCheckpoint,
      "debugMode" => $this->debugMode,
      "createCheckpoints" => $this->createCheckpoints,
      "metadata" => $this->metadata,
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function jsonSerialize(): mixed {
    return $this->toArray();
  }

}
