<?php

declare(strict_types=1);

namespace Drupal\flowdrop_runtime\Service\WorkflowState;

use Drupal\flowdrop_orchestration\DTO\Snapshot\NodeSnapshot;
use Drupal\flowdrop_runtime\DTO\Snapshot\ValidationResult;
use Drupal\flowdrop_orchestration\DTO\Snapshot\WorkflowSnapshot;

/**
 * Interface for workflow state management.
 *
 * WorkflowStateManagerInterface provides a storage-agnostic API for reading,
 * writing, and validating workflow execution state. It enables:
 *
 * - Starting workflows with initial state.
 * - Resuming workflows from snapshots.
 * - Orchestrator handover (sync-to-async) with pre-completed nodes.
 * - Trigger efficiency through injected node outputs.
 * - State validation against workflow definitions.
 *
 * This interface abstracts the underlying storage mechanism, allowing
 * implementations to use entity storage, Redis, or other backends.
 *
 * Example usage:
 * @code
 * // Create a new workflow execution
 * $snapshot = $stateManager->createSnapshot(
 *   workflowId: "my_workflow",
 *   workflowVersion: $this->computeWorkflowVersion($workflow),
 *   initialInput: ["trigger_data" => $entityData],
 * );
 *
 * // Initialize nodes from workflow definition
 * $snapshot = $stateManager->initializeNodesFromWorkflow($snapshot, $workflow);
 *
 * // Update node state after execution
 * $snapshot = $stateManager->setNodeCompleted(
 *   executionId: $snapshot->executionId,
 *   nodeId: "node_chat_model_1",
 *   output: ["response" => "Hello!"],
 * );
 *
 * // Validate before handover
 * $result = $stateManager->validateSnapshot($snapshot, $workflow);
 * if ($result->isValid()) {
 *   // Safe to handover to async orchestrator
 * }
 * @endcode
 *
 * @see \Drupal\flowdrop_orchestration\DTO\Snapshot\WorkflowSnapshot
 * @see \Drupal\flowdrop_orchestration\DTO\Snapshot\NodeSnapshot
 *
 * @internal
 *   This class is not part of the public API and may change without notice.
 */
interface WorkflowStateManagerInterface {

  /**
   * Creates a new workflow snapshot.
   *
   * This creates a fresh execution context for a workflow run. The snapshot
   * is initially in "pending" status with no node states.
   *
   * @param string $workflowId
   *   The workflow definition ID.
   * @param string $workflowVersion
   *   Hash/version of the workflow definition for validation.
   * @param array<string, mixed> $initialInput
   *   Initial input data for the workflow.
   * @param string|null $threadId
   *   Optional thread ID for conversation continuity.
   *   If NULL, a new thread ID will be generated.
   * @param array<string, mixed> $metadata
   *   Additional metadata to attach to the snapshot.
   *
   * @return \Drupal\flowdrop_orchestration\DTO\Snapshot\WorkflowSnapshot
   *   The created snapshot (not yet persisted).
   */
  public function createSnapshot(
    string $workflowId,
    string $workflowVersion,
    array $initialInput = [],
    ?string $threadId = NULL,
    array $metadata = [],
  ): WorkflowSnapshot;

  /**
   * Initializes node states from a workflow definition.
   *
   * Creates a pending NodeSnapshot for each node in the workflow definition.
   * This should be called after createSnapshot() to populate the node states.
   *
   * @param \Drupal\flowdrop_orchestration\DTO\Snapshot\WorkflowSnapshot $snapshot
   *   The snapshot to initialize.
   * @param array<string, mixed> $workflowDefinition
   *   The workflow definition containing "nodes" array.
   *
   * @return \Drupal\flowdrop_orchestration\DTO\Snapshot\WorkflowSnapshot
   *   The snapshot with initialized node states.
   */
  public function initializeNodesFromWorkflow(
    WorkflowSnapshot $snapshot,
    array $workflowDefinition,
  ): WorkflowSnapshot;

  /**
   * Gets a snapshot by execution ID.
   *
   * @param string $executionId
   *   The execution ID.
   *
   * @return \Drupal\flowdrop_orchestration\DTO\Snapshot\WorkflowSnapshot|null
   *   The snapshot or NULL if not found.
   */
  public function getSnapshot(string $executionId): ?WorkflowSnapshot;

  /**
   * Gets the latest snapshot for a thread.
   *
   * Useful for resuming conversations or continuing from the last checkpoint.
   *
   * @param string $threadId
   *   The thread ID.
   *
   * @return \Drupal\flowdrop_orchestration\DTO\Snapshot\WorkflowSnapshot|null
   *   The latest snapshot for the thread, or NULL if none exists.
   */
  public function getLatestByThread(string $threadId): ?WorkflowSnapshot;

  /**
   * Gets a specific node state from a snapshot.
   *
   * @param string $executionId
   *   The execution ID.
   * @param string $nodeId
   *   The node ID.
   *
   * @return \Drupal\flowdrop_orchestration\DTO\Snapshot\NodeSnapshot|null
   *   The node snapshot or NULL if not found.
   */
  public function getNodeState(string $executionId, string $nodeId): ?NodeSnapshot;

  /**
   * Persists a snapshot to storage.
   *
   * @param \Drupal\flowdrop_orchestration\DTO\Snapshot\WorkflowSnapshot $snapshot
   *   The snapshot to save.
   *
   * @return \Drupal\flowdrop_orchestration\DTO\Snapshot\WorkflowSnapshot
   *   The saved snapshot (may have updated timestamps).
   */
  public function save(WorkflowSnapshot $snapshot): WorkflowSnapshot;

  /**
   * Deletes a snapshot from storage.
   *
   * @param string $executionId
   *   The execution ID of the snapshot to delete.
   *
   * @return bool
   *   TRUE if deleted, FALSE if not found.
   */
  public function delete(string $executionId): bool;

  /**
   * Updates the workflow status.
   *
   * @param string $executionId
   *   The execution ID.
   * @param string $status
   *   The new status (pending|running|completed|failed|paused).
   *
   * @return \Drupal\flowdrop_orchestration\DTO\Snapshot\WorkflowSnapshot
   *   The updated snapshot.
   *
   * @throws \InvalidArgumentException
   *   If the execution ID is not found.
   */
  public function updateStatus(string $executionId, string $status): WorkflowSnapshot;

  /**
   * Marks a node as running.
   *
   * @param string $executionId
   *   The execution ID.
   * @param string $nodeId
   *   The node ID.
   *
   * @return \Drupal\flowdrop_orchestration\DTO\Snapshot\WorkflowSnapshot
   *   The updated snapshot.
   *
   * @throws \InvalidArgumentException
   *   If the execution ID is not found.
   */
  public function setNodeRunning(string $executionId, string $nodeId): WorkflowSnapshot;

  /**
   * Marks a node as completed with output.
   *
   * @param string $executionId
   *   The execution ID.
   * @param string $nodeId
   *   The node ID.
   * @param array<string, mixed> $output
   *   The node output data.
   * @param bool $injected
   *   Whether this is injected data (not actually executed).
   *
   * @return \Drupal\flowdrop_orchestration\DTO\Snapshot\WorkflowSnapshot
   *   The updated snapshot.
   *
   * @throws \InvalidArgumentException
   *   If the execution ID is not found.
   */
  public function setNodeCompleted(
    string $executionId,
    string $nodeId,
    array $output,
    bool $injected = FALSE,
  ): WorkflowSnapshot;

  /**
   * Marks a node as failed with an error.
   *
   * @param string $executionId
   *   The execution ID.
   * @param string $nodeId
   *   The node ID.
   * @param string $error
   *   The error message.
   *
   * @return \Drupal\flowdrop_orchestration\DTO\Snapshot\WorkflowSnapshot
   *   The updated snapshot.
   *
   * @throws \InvalidArgumentException
   *   If the execution ID is not found.
   */
  public function setNodeFailed(
    string $executionId,
    string $nodeId,
    string $error,
  ): WorkflowSnapshot;

  /**
   * Marks a node as skipped.
   *
   * @param string $executionId
   *   The execution ID.
   * @param string $nodeId
   *   The node ID.
   * @param string $reason
   *   The reason for skipping.
   *
   * @return \Drupal\flowdrop_orchestration\DTO\Snapshot\WorkflowSnapshot
   *   The updated snapshot.
   *
   * @throws \InvalidArgumentException
   *   If the execution ID is not found.
   */
  public function setNodeSkipped(
    string $executionId,
    string $nodeId,
    string $reason = "",
  ): WorkflowSnapshot;

  /**
   * Marks multiple nodes as completed with injected data.
   *
   * Used during orchestrator handover to inject pre-computed outputs for
   * nodes that were already executed in a previous orchestrator context.
   *
   * @param string $executionId
   *   The execution ID.
   * @param array<string, array<string, mixed>> $nodeOutputs
   *   Map of node ID to output data.
   *
   * @return \Drupal\flowdrop_orchestration\DTO\Snapshot\WorkflowSnapshot
   *   The updated snapshot.
   *
   * @throws \InvalidArgumentException
   *   If the execution ID is not found.
   */
  public function markNodesCompleted(
    string $executionId,
    array $nodeOutputs,
  ): WorkflowSnapshot;

  /**
   * Initializes a new snapshot from an existing snapshot (for handover).
   *
   * Creates a new execution context that inherits completed node states
   * from the source snapshot. Used for orchestrator handover scenarios.
   *
   * @param \Drupal\flowdrop_orchestration\DTO\Snapshot\WorkflowSnapshot $source
   *   The source snapshot containing completed node states.
   * @param bool $preserveCompletedNodes
   *   Whether to copy completed node states (marked as injected).
   *
   * @return \Drupal\flowdrop_orchestration\DTO\Snapshot\WorkflowSnapshot
   *   A new snapshot initialized from the source.
   */
  public function initializeFromSnapshot(
    WorkflowSnapshot $source,
    bool $preserveCompletedNodes = TRUE,
  ): WorkflowSnapshot;

  /**
   * Validates a snapshot against a workflow definition.
   *
   * Checks:
   * - Workflow version matches.
   * - All node IDs exist in the workflow definition.
   * - Completed nodes have their dependencies also completed.
   * - No invalid status transitions.
   *
   * @param \Drupal\flowdrop_orchestration\DTO\Snapshot\WorkflowSnapshot $snapshot
   *   The snapshot to validate.
   * @param array<string, mixed> $workflowDefinition
   *   The workflow definition to validate against.
   *
   * @return \Drupal\flowdrop_runtime\DTO\Snapshot\ValidationResult
   *   The validation result with any errors/warnings.
   */
  public function validateSnapshot(
    WorkflowSnapshot $snapshot,
    array $workflowDefinition,
  ): ValidationResult;

  /**
   * Validates dependency integrity for a snapshot.
   *
   * Ensures that if a node is marked as completed, all its upstream
   * dependencies are also completed. This prevents invalid states where
   * a node's output depends on a pending upstream node.
   *
   * @param \Drupal\flowdrop_orchestration\DTO\Snapshot\WorkflowSnapshot $snapshot
   *   The snapshot to validate.
   * @param array<string, mixed> $workflowDefinition
   *   The workflow definition with edges.
   *
   * @return \Drupal\flowdrop_runtime\DTO\Snapshot\ValidationResult
   *   The validation result.
   */
  public function validateDependencies(
    WorkflowSnapshot $snapshot,
    array $workflowDefinition,
  ): ValidationResult;

  /**
   * Computes a version hash for a workflow definition.
   *
   * The version hash is used to detect workflow changes that might
   * invalidate existing snapshots.
   *
   * @param array<string, mixed> $workflowDefinition
   *   The workflow definition.
   *
   * @return string
   *   A hash representing the workflow version.
   */
  public function computeWorkflowVersion(array $workflowDefinition): string;

  /**
   * Adds a message to the snapshot's message history.
   *
   * Used for chat/agent workflows that need to track conversation history.
   *
   * @param string $executionId
   *   The execution ID.
   * @param string $role
   *   The message role (e.g., "user", "assistant", "system").
   * @param string $content
   *   The message content.
   * @param array<string, mixed> $metadata
   *   Optional message metadata.
   *
   * @return \Drupal\flowdrop_orchestration\DTO\Snapshot\WorkflowSnapshot
   *   The updated snapshot.
   *
   * @throws \InvalidArgumentException
   *   If the execution ID is not found.
   */
  public function addMessage(
    string $executionId,
    string $role,
    string $content,
    array $metadata = [],
  ): WorkflowSnapshot;

  /**
   * Updates the iterator state.
   *
   * Used for workflows with loop/iteration processing.
   *
   * @param string $executionId
   *   The execution ID.
   * @param array<string, mixed>|null $iterator
   *   The iterator state or NULL to clear.
   *
   * @return \Drupal\flowdrop_orchestration\DTO\Snapshot\WorkflowSnapshot
   *   The updated snapshot.
   *
   * @throws \InvalidArgumentException
   *   If the execution ID is not found.
   */
  public function updateIterator(
    string $executionId,
    ?array $iterator,
  ): WorkflowSnapshot;

  /**
   * Increments the iteration count.
   *
   * Used for loop protection in iterative workflows.
   *
   * @param string $executionId
   *   The execution ID.
   *
   * @return \Drupal\flowdrop_orchestration\DTO\Snapshot\WorkflowSnapshot
   *   The updated snapshot.
   *
   * @throws \InvalidArgumentException
   *   If the execution ID is not found.
   */
  public function incrementIteration(string $executionId): WorkflowSnapshot;

  /**
   * Lists all snapshots for a workflow.
   *
   * @param string $workflowId
   *   The workflow ID.
   * @param int $limit
   *   Maximum number of snapshots to return.
   * @param int $offset
   *   Number of snapshots to skip.
   *
   * @return array<WorkflowSnapshot>
   *   Array of snapshots, ordered by creation time (newest first).
   */
  public function listByWorkflow(
    string $workflowId,
    int $limit = 100,
    int $offset = 0,
  ): array;

  /**
   * Lists all snapshots for a thread.
   *
   * @param string $threadId
   *   The thread ID.
   * @param int $limit
   *   Maximum number of snapshots to return.
   *
   * @return array<WorkflowSnapshot>
   *   Array of snapshots, ordered by creation time (newest first).
   */
  public function listByThread(string $threadId, int $limit = 100): array;

  /**
   * Cleans up old snapshots.
   *
   * @param int $olderThan
   *   Unix timestamp - delete snapshots created before this time.
   * @param array<string> $statuses
   *   Only delete snapshots with these statuses (empty = all).
   *
   * @return int
   *   Number of snapshots deleted.
   */
  public function cleanup(int $olderThan, array $statuses = []): int;

  /**
   * Gets the underlying snapshot entity by execution ID.
   *
   * This provides access to the persistent entity for linking with other
   * entities (e.g., Pipeline -> Snapshot relationship).
   *
   * @param string $executionId
   *   The execution ID.
   *
   * @return object|null
   *   The snapshot entity, or NULL if not found or no entity storage available.
   */
  public function getSnapshotEntity(string $executionId): ?object;

}
