<?php

declare(strict_types=1);

namespace Drupal\flowdrop_stategraph\Routing;

/**
 * Result of edge condition evaluation.
 *
 * This DTO captures the result of evaluating conditional edges,
 * including which edges matched and the routing decisions made.
 *
 * @internal This class is not part of the public API and may change without notice.
 */
final class RoutingResult implements \JsonSerializable {

  /**
   * Constructs a new RoutingResult.
   *
   * @param string $sourceNodeId
   *   The source node ID that was evaluated.
   * @param array<string> $targetNodeIds
   *   The target node IDs to route to.
   * @param array<string, bool> $evaluatedConditions
   *   Map of edge ID to condition result.
   * @param bool $hasConditionalEdges
   *   Whether any conditional edges were found.
   */
  public function __construct(
    public readonly string $sourceNodeId,
    public readonly array $targetNodeIds,
    public readonly array $evaluatedConditions = [],
    public readonly bool $hasConditionalEdges = FALSE,
  ) {}

  /**
   * Creates a result for no matching targets.
   *
   * @param string $sourceNodeId
   *   The source node ID.
   *
   * @return self
   *   A RoutingResult with no targets.
   */
  public static function noTargets(string $sourceNodeId): self {
    return new self(
      sourceNodeId: $sourceNodeId,
      targetNodeIds: [],
      evaluatedConditions: [],
      hasConditionalEdges: FALSE,
    );
  }

  /**
   * Creates a result for default (all edges) routing.
   *
   * @param string $sourceNodeId
   *   The source node ID.
   * @param array<string> $targetNodeIds
   *   All target node IDs.
   *
   * @return self
   *   A RoutingResult with all targets.
   */
  public static function defaultRouting(string $sourceNodeId, array $targetNodeIds): self {
    return new self(
      sourceNodeId: $sourceNodeId,
      targetNodeIds: $targetNodeIds,
      evaluatedConditions: [],
      hasConditionalEdges: FALSE,
    );
  }

  /**
   * Checks if any targets were matched.
   *
   * @return bool
   *   TRUE if at least one target was matched.
   */
  public function hasTargets(): bool {
    return !empty($this->targetNodeIds);
  }

  /**
   * Gets the first target node ID.
   *
   * @return string|null
   *   The first target or NULL.
   */
  public function getFirstTarget(): ?string {
    return $this->targetNodeIds[0] ?? NULL;
  }

  /**
   * Converts to array.
   *
   * @return array<string, mixed>
   *   The result as an array.
   */
  public function toArray(): array {
    return [
      "sourceNodeId" => $this->sourceNodeId,
      "targetNodeIds" => $this->targetNodeIds,
      "evaluatedConditions" => $this->evaluatedConditions,
      "hasConditionalEdges" => $this->hasConditionalEdges,
    ];
  }

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

}
