<?php

declare(strict_types=1);

namespace Drupal\flowdrop_runtime\DTO\Runtime;

use Drupal\flowdrop\DTO\OutputInterface;

/**
 * Result of a single node execution.
 *
 * @internal This class is not part of the public API and may change without notice.
 */
class NodeExecutionResult {

  /**
   * Constructs a new NodeExecutionResult.
   *
   * @param string $nodeId
   *   The node ID.
   * @param \Drupal\flowdrop\DTO\OutputInterface $output
   *   The output data.
   * @param string $status
   *   The execution status ('success', 'warning', 'error').
   * @param float $executionTime
   *   The execution time in seconds.
   * @param string $nodeType
   *   The node type/processor ID.
   * @param int|null $timestamp
   *   The execution timestamp (defaults to current time).
   * @param NodeExecutionContext|null $context
   *   The execution context.
   * @param array<string, mixed> $metadata
   *   Additional metadata about the execution.
   */
  public function __construct(
    private readonly string $nodeId,
    private readonly OutputInterface $output,
    private readonly string $status = 'success',
    private readonly float $executionTime = 0.0,
    private readonly string $nodeType = '',
    private readonly ?int $timestamp = NULL,
    private readonly ?NodeExecutionContext $context = NULL,
    private readonly array $metadata = [],
  ) {}

  /**
   * Get the node ID.
   *
   * @return string
   *   The node ID.
   */
  public function getNodeId(): string {
    return $this->nodeId;
  }

  /**
   * Get the node type.
   *
   * @return string
   *   The node type.
   */
  public function getNodeType(): string {
    return $this->nodeType;
  }

  /**
   * Get the execution status.
   *
   * @return string
   *   The execution status.
   */
  public function getStatus(): string {
    return $this->status;
  }

  /**
   * Get the node output.
   *
   * @return \Drupal\flowdrop\DTO\OutputInterface
   *   The node output.
   */
  public function getOutput(): OutputInterface {
    return $this->output;
  }

  /**
   * Get the execution time in seconds.
   *
   * @return float
   *   The execution time.
   */
  public function getExecutionTime(): float {
    return $this->executionTime;
  }

  /**
   * Get the execution timestamp.
   *
   * @return int
   *   The execution timestamp.
   */
  public function getTimestamp(): int {
    return $this->timestamp ?? time();
  }

  /**
   * Get the execution context.
   *
   * @return \Drupal\flowdrop_runtime\DTO\Runtime\NodeExecutionContext|null
   *   The execution context or NULL.
   */
  public function getContext(): ?NodeExecutionContext {
    return $this->context;
  }

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

  /**
   * Get a specific metadata value.
   *
   * @param string $key
   *   The metadata key.
   * @param mixed $default
   *   Default value if not found.
   *
   * @return mixed
   *   The metadata value.
   */
  public function getMetadataValue(string $key, mixed $default = NULL): mixed {
    return $this->metadata[$key] ?? $default;
  }

  /**
   * Check if this is an iterator result.
   *
   * @return bool
   *   TRUE if this is an iterator result.
   */
  public function isIteratorResult(): bool {
    return $this->metadata['is_iterator'] ?? FALSE;
  }

  /**
   * Check if this is an agent result.
   *
   * @return bool
   *   TRUE if this is an agent result.
   */
  public function isAgentResult(): bool {
    return $this->metadata['is_agent'] ?? FALSE;
  }

  /**
   * Convert to array for serialization.
   *
   * @return array<string, mixed>
   *   Array representation of the result.
   */
  public function toArray(): array {
    $result = [
      'node_id' => $this->nodeId,
      'node_type' => $this->nodeType,
      'status' => $this->status,
      'output' => $this->output->toArray(),
      'execution_time' => $this->executionTime,
      'timestamp' => $this->getTimestamp(),
      'metadata' => $this->metadata,
    ];

    if ($this->context !== NULL) {
      $result['context'] = [
        'workflow_id' => $this->context->getWorkflowId(),
        'pipeline_id' => $this->context->getPipelineId(),
      ];
    }

    return $result;
  }

}
