<?php

declare(strict_types=1);

namespace Drupal\flowdrop_runtime\DTO\Compiler;

/**
 * Represents a node in the execution graph.
 *
 * Contains information needed by the orchestrator to execute a node,
 * including its trigger dependencies, position in the graph (root/leaf),
 * and any special handling requirements.
 */
final class ExecutionNode {

  /**
   * Special type constant for agent nodes.
   */
  public const SPECIAL_TYPE_AGENT = "agent";

  /**
   * Special type constant for iterator nodes.
   */
  public const SPECIAL_TYPE_ITERATOR = "iterator";

  /**
   * Constructs a new ExecutionNode.
   *
   * @param string $id
   *   The node ID.
   * @param string $typeId
   *   The node type ID (processor ID).
   * @param array<string, mixed> $config
   *   The node configuration.
   * @param array<string> $triggerDependencies
   *   Node IDs that must complete before this node executes (trigger edges).
   * @param array<string> $dataDependencies
   *   Node IDs this node has data dependencies on (for data resolution).
   * @param bool $isRoot
   *   Whether this is a root node (no trigger dependencies).
   * @param bool $isLeaf
   *   Whether this is a leaf node (no dependents in execution graph).
   * @param string|null $specialType
   *   Special type requiring custom handling ("agent", "iterator", or NULL).
   * @param array<string, mixed> $metadata
   *   Additional node metadata.
   */
  public function __construct(
    private readonly string $id,
    private readonly string $typeId,
    private readonly array $config,
    private readonly array $triggerDependencies,
    private readonly array $dataDependencies,
    private readonly bool $isRoot,
    private readonly bool $isLeaf,
    private readonly ?string $specialType,
    private readonly array $metadata = [],
  ) {}

  /**
   * Gets the node ID.
   *
   * @return string
   *   The node ID.
   */
  public function getId(): string {
    return $this->id;
  }

  /**
   * Gets the node type ID (processor ID).
   *
   * @return string
   *   The node type ID.
   */
  public function getTypeId(): string {
    return $this->typeId;
  }

  /**
   * Gets the node configuration.
   *
   * @return array<string, mixed>
   *   The node configuration.
   */
  public function getConfig(): array {
    return $this->config;
  }

  /**
   * Gets a specific configuration value.
   *
   * @param string $key
   *   The configuration key.
   * @param mixed $default
   *   The default value if key doesn't exist.
   *
   * @return mixed
   *   The configuration value.
   */
  public function getConfigValue(string $key, mixed $default = NULL): mixed {
    return $this->config[$key] ?? $default;
  }

  /**
   * Gets the trigger dependencies (nodes that must complete first).
   *
   * These are nodes connected via trigger edges that control execution order.
   *
   * @return array<string>
   *   Array of trigger dependency node IDs.
   */
  public function getTriggerDependencies(): array {
    return $this->triggerDependencies;
  }

  /**
   * Gets the data dependencies (nodes to fetch data from).
   *
   * These are nodes connected via data edges. Their outputs should be
   * merged into this node's inputs at runtime via the DependencyGraph.
   *
   * @return array<string>
   *   Array of data dependency node IDs.
   */
  public function getDataDependencies(): array {
    return $this->dataDependencies;
  }

  /**
   * Gets all dependencies (trigger + data combined).
   *
   * @return array<string>
   *   Array of all dependency node IDs.
   */
  public function getAllDependencies(): array {
    return array_unique(array_merge($this->triggerDependencies, $this->dataDependencies));
  }

  /**
   * Checks if this is a root node.
   *
   * Root nodes have no trigger dependencies and execute first.
   *
   * @return bool
   *   TRUE if this is a root node.
   */
  public function isRoot(): bool {
    return $this->isRoot;
  }

  /**
   * Checks if this is a leaf node.
   *
   * Leaf nodes have no dependents in the execution graph.
   *
   * @return bool
   *   TRUE if this is a leaf node.
   */
  public function isLeaf(): bool {
    return $this->isLeaf;
  }

  /**
   * Gets the special type if this node requires special handling.
   *
   * @return string|null
   *   The special type ("agent", "iterator") or NULL for regular nodes.
   */
  public function getSpecialType(): ?string {
    return $this->specialType;
  }

  /**
   * Checks if this node requires special handling.
   *
   * @return bool
   *   TRUE if this node has a special type.
   */
  public function requiresSpecialHandling(): bool {
    return $this->specialType !== NULL;
  }

  /**
   * Checks if this is an agent node.
   *
   * Agent nodes require special handling for tool discovery and ReAct loops.
   *
   * @return bool
   *   TRUE if this is an agent node.
   */
  public function isAgent(): bool {
    return $this->specialType === self::SPECIAL_TYPE_AGENT;
  }

  /**
   * Checks if this is an iterator node.
   *
   * Iterator nodes require special handling for sub-workflow execution.
   *
   * @return bool
   *   TRUE if this is an iterator node.
   */
  public function isIterator(): bool {
    return $this->specialType === self::SPECIAL_TYPE_ITERATOR;
  }

  /**
   * Gets the node metadata.
   *
   * @return array<string, mixed>
   *   The node metadata.
   */
  public function getMetadata(): array {
    return $this->metadata;
  }

  /**
   * Gets a specific metadata value.
   *
   * @param string $key
   *   The metadata key.
   * @param mixed $default
   *   The default value if key doesn't exist.
   *
   * @return mixed
   *   The metadata value.
   */
  public function getMetadataValue(string $key, mixed $default = NULL): mixed {
    return $this->metadata[$key] ?? $default;
  }

  /**
   * Converts to array format.
   *
   * @return array<string, mixed>
   *   Array representation.
   */
  public function toArray(): array {
    return [
      "id" => $this->id,
      "typeId" => $this->typeId,
      "config" => $this->config,
      "triggerDependencies" => $this->triggerDependencies,
      "dataDependencies" => $this->dataDependencies,
      "isRoot" => $this->isRoot,
      "isLeaf" => $this->isLeaf,
      "specialType" => $this->specialType,
      "metadata" => $this->metadata,
    ];
  }

}
