<?php

declare(strict_types=1);

namespace Drupal\flowdrop_workflow_executor\Service;

use Drupal\flowdrop_workflow\WorkflowDefinitionInterface;
use Drupal\flowdrop_workflow_executor\DTO\LaunchOptions;
use Drupal\flowdrop_workflow_executor\DTO\LaunchResult;

/**
 * Public service for launching FlowDrop workflows.
 *
 * The standard way to start a workflow run from any context — HTTP
 * controllers, drush commands, trigger plugins, other modules. Replaces
 * the historical practice of POSTing a chat message to start a run, which
 * conflated conversational state with execution control.
 *
 * Public API — implementations may change, the contract on this
 * interface is the BC commitment.
 *
 * @api
 *
 * ## Semantics
 *
 * `launch()` is non-blocking from the caller's perspective by default. It
 * returns a {@see LaunchResult} handle once the pipeline entity exists and
 * execution has been scheduled. The actual workflow runs either:
 *
 *  - in a queue worker (`LaunchResult::$queued === true`), or
 *  - in the same PHP process after the response is flushed
 *    (`LaunchResult::$queued === false`).
 *
 * Which path is taken is determined by the workflow's declared execution
 * mode — the caller does not choose. This is deliberate: workflow authors
 * pick the orchestrator that matches their workflow's shape, and allowing
 * arbitrary callers to override that would expose implementation as part
 * of the public BC surface.
 *
 * ## Waiting for completion (PHP callers)
 *
 * `LaunchOptions::$wait` makes `launch()` execute the run inline and
 * return only when it reaches a terminal or paused state — the
 * `LaunchResult::$status` then carries the final pipeline status instead
 * of PENDING. Scope and limits:
 *
 *  - PHP-only. How an HTTP/websocket caller waits is a transport-layer
 *    concern, deliberately outside FlowDrop; the `/run` endpoint does not
 *    expose it.
 *  - For drush, cron, queue workers, and kernel tests — do NOT use it in
 *    a web request. `launch()` blocks for the whole run; a stategraph
 *    workflow may legitimately run up to its `timeout` budget (300s by
 *    default), far past typical PHP-FPM request limits.
 *  - Ignored, with a logged notice, for workflows that declare the
 *    asynchronous orchestrator — the queue is the author's declared
 *    durability choice, and `wait` never overrides orchestrators.
 *  - In-flight execution failures still do NOT throw (see Errors below);
 *    with `wait` they are visible immediately as a `failed` status on the
 *    returned handle.
 *  - Outputs are still read through the pipeline read endpoints — the
 *    execute-vs-view permission split holds regardless of `wait`.
 *
 * ## Permissions
 *
 * Launching requires `execute flowdrop_workflow`. Reading the resulting
 * pipeline (status, outputs, logs) requires the separate
 * `view own flowdrop_pipeline` / `view any flowdrop_pipeline` permissions.
 * This split lets sites grant "fire-and-forget execution" without granting
 * "see what came out" — useful for automation accounts and restricted API
 * consumers.
 *
 * ## Reading the result of a launch
 *
 * `launch()` returns only the handle. To follow the run, use the existing
 * pipeline read endpoints (all gated by the pipeline view permissions):
 *
 *  - `GET /api/flowdrop/pipeline/{pipelineId}/status`
 *      Lightweight status payload — current lifecycle state, timestamps,
 *      and the pending interrupt (if paused on a HITL). Safe for polling.
 *  - `GET /api/flowdrop/pipeline/{pipelineId}`
 *      Full pipeline detail — inputs, outputs, jobs, node statuses.
 *      Heavier; fetch on completion or when the user opens a detail view.
 *  - `GET /api/flowdrop/pipeline/{pipelineId}/logs`
 *      Execution logs.
 *  - `GET /api/flowdrop/workflow/{workflowId}/pipelines`
 *      List of pipelines for a workflow.
 *
 * Outputs land on `execution_data.output` of the pipeline detail payload
 * once the run reaches a terminal state.
 *
 * ## Errors
 *
 * `launch()` throws on failures it can detect synchronously (workflow not
 * loadable, input schema violation, orchestrator unavailable). It does
 * NOT throw for in-flight execution errors — those land on the pipeline's
 * status (`failed`/`cancelled`) and are surfaced through the read
 * endpoints above.
 *
 * @see LaunchOptions
 * @see LaunchResult
 * @see \Drupal\flowdrop_workflow_executor\Exception\WorkflowLaunchException
 * @see \Drupal\flowdrop_workflow_executor\Exception\InvalidLaunchInputException
 */
interface WorkflowLauncherInterface {

  /**
   * Launches a workflow run.
   *
   * @param \Drupal\flowdrop_workflow\WorkflowDefinitionInterface $workflow
   *   The workflow to launch.
   * @param array<string, mixed> $inputs
   *   Initial inputs keyed by the workflow's declared input port names
   *   (see `getInputPorts()`). Validated against the workflow's input
   *   schema; unknown keys, missing required keys, and type mismatches
   *   raise {@see InvalidLaunchInputException}.
   * @param \Drupal\flowdrop_workflow_executor\DTO\LaunchOptions|null $options
   *   Optional per-call options. Pass null to accept all defaults.
   *
   * @return \Drupal\flowdrop_workflow_executor\DTO\LaunchResult
   *   Handle to the launched pipeline.
   *
   * @throws \Drupal\flowdrop_workflow_executor\Exception\InvalidLaunchInputException
   *   If `$inputs` violates the workflow's input schema.
   * @throws \Drupal\flowdrop_workflow_executor\Exception\WorkflowLaunchException
   *   If launch cannot proceed for any other reason (orchestrator
   *   misconfigured, storage failure, etc.).
   */
  public function launch(
    WorkflowDefinitionInterface $workflow,
    array $inputs = [],
    ?LaunchOptions $options = NULL,
  ): LaunchResult;

}
