<?php

declare(strict_types=1);

namespace Drupal\flowdrop_workflow_executor\Controller;

use Drupal\Core\Controller\ControllerBase;
use Drupal\flowdrop\Controller\Api\ApiResponseTrait;
use Drupal\flowdrop_workflow\FlowDropWorkflowInterface;
use Drupal\flowdrop_workflow_executor\DTO\LaunchOptions;
use Drupal\flowdrop_workflow_executor\Exception\InvalidLaunchInputException;
use Drupal\flowdrop_workflow_executor\Exception\WorkflowLaunchException;
use Drupal\flowdrop_workflow_executor\Exception\WorkflowLaunchValidationException;
use Drupal\flowdrop_workflow_executor\Service\WorkflowLauncherInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

/**
 * HTTP launch endpoint for workflows.
 *
 * Thin HTTP boundary over {@see WorkflowLauncherInterface}. Parses the
 * request, hands off to the launcher, maps the result (or typed
 * exceptions) to HTTP status codes. All business logic — input validation,
 * orchestrator resolution, dispatch — lives in the launcher; this class
 * does not duplicate any of it.
 *
 * @internal
 */
final class WorkflowLaunchApiController extends ControllerBase {

  use ApiResponseTrait;

  public function __construct(
    private readonly WorkflowLauncherInterface $launcher,
  ) {}

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container): static {
    return new static(
      $container->get(WorkflowLauncherInterface::class),
    );
  }

  /**
   * POST /api/flowdrop/workflow/{flowdrop_workflow}/run.
   *
   * Body: `{ "inputs": { <port_name>: <value>, ... }, "sessionId"?: "..." }`.
   *
   * Returns 202 with `{ pipelineId, status, queued }`. To follow the run,
   * call the pipeline GET endpoints (`/api/flowdrop/pipeline/{id}/status`
   * etc.) — gated by the separate `view own|any flowdrop_pipeline`
   * permission so launching does not imply read access.
   */
  public function runWorkflow(FlowDropWorkflowInterface $flowdrop_workflow, Request $request): JsonResponse {
    $body = $request->getContent();
    $payload = $body === '' ? [] : json_decode($body, TRUE);
    if (!is_array($payload)) {
      return $this->errorResponse('Invalid JSON body', Response::HTTP_BAD_REQUEST);
    }

    $inputs = $payload['inputs'] ?? [];
    if (!is_array($inputs)) {
      return $this->errorResponse('"inputs" must be an object', Response::HTTP_BAD_REQUEST);
    }

    $sessionId = isset($payload['sessionId']) ? (string) $payload['sessionId'] : NULL;
    if ($sessionId === '') {
      $sessionId = NULL;
    }
    $options = new LaunchOptions(sessionId: $sessionId);

    try {
      $result = $this->launcher->launch($flowdrop_workflow, $inputs, $options);
    }
    catch (InvalidLaunchInputException $e) {
      return $this->errorResponse($e->getMessage(), Response::HTTP_BAD_REQUEST);
    }
    catch (WorkflowLaunchValidationException $e) {
      // The stored definition is structurally invalid — the caller's problem,
      // not a server fault. Mirror the save path's 422 + per-error detail so
      // the launch and save surfaces report the same condition identically.
      return new JsonResponse([
        'success' => FALSE,
        'error' => 'Workflow validation failed',
        'details' => array_map(
          static fn (array $error): array => [
            'code' => $error['code'],
            'message' => $error['message'],
            'locator' => $error['parameter'],
          ],
          $e->getResult()->toArray()['errors'],
        ),
      ], Response::HTTP_UNPROCESSABLE_ENTITY);
    }
    catch (WorkflowLaunchException $e) {
      $this->getLogger('flowdrop_workflow_executor')->error(
        'Workflow launch failed for @id: @message',
        ['@id' => (string) $flowdrop_workflow->id(), '@message' => $e->getMessage()],
      );
      return $this->errorResponse('Failed to launch workflow', Response::HTTP_INTERNAL_SERVER_ERROR);
    }

    return $this->successResponse(
      [
        'pipelineId' => $result->pipelineId,
        'status' => $result->status,
        'queued' => $result->queued,
      ],
      Response::HTTP_ACCEPTED,
    );
  }

}
