<?php

declare(strict_types=1);

namespace Drupal\flowdrop_session\Controller;

use Drupal\Core\Controller\ControllerBase;
use Drupal\flowdrop\Controller\Api\ApiResponseTrait;
use Drupal\flowdrop_session\DTO\TurnOptions;
use Drupal\flowdrop_session\Entity\FlowDropSessionInterface;
use Drupal\flowdrop_session\Exception\ConcurrentTurnException;
use Drupal\flowdrop_session\Service\SessionTurnServiceInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

/**
 * HTTP boundary for the chat-turn verb.
 *
 * Thin layer over {@see SessionTurnServiceInterface}. Parses the request,
 * hands off to the turn service, maps the result (or typed exceptions) to
 * HTTP status codes. No `wait` over HTTP — the session turn events are the
 * completion signal (see docs/reference/api/session-turn-events.md).
 *
 * @internal
 */
final class SessionTurnApiController extends ControllerBase {

  use ApiResponseTrait;

  public function __construct(
    private readonly SessionTurnServiceInterface $turnService,
  ) {}

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

  /**
   * POST /api/flowdrop/session/{flowdrop_session}/turn.
   *
   * Body: `{ "content": "...", "inputs"?: { <port_name>: <value>, ... } }`.
   *
   * Returns 202 with the TurnResult shape: `{ sessionId, userMessageId,
   * pipelineId, status, assistantMessageIds }`. The userMessageId doubles
   * as the turn id in the session turn events. pipelineId is null at this
   * point — the pipeline entity is created after the HTTP response; the
   * `session.turn.started` event carries it once execution starts.
   *
   * 409 when the session is already running a turn (post again once the
   * `session.turn.completed` event for the in-flight turn arrives).
   */
  public function executeTurn(FlowDropSessionInterface $flowdrop_session, 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);
    }

    $content = $payload['content'] ?? NULL;
    if (!is_string($content) || $content === '') {
      return $this->errorResponse('"content" is required and must be a non-empty string', Response::HTTP_BAD_REQUEST);
    }

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

    try {
      $result = $this->turnService->executeTurn(
        (string) $flowdrop_session->id(),
        $content,
        new TurnOptions(inputs: $inputs),
      );
    }
    catch (ConcurrentTurnException $e) {
      return $this->errorResponse($e->getMessage(), Response::HTTP_CONFLICT);
    }
    catch (\RuntimeException $e) {
      // Session without a workflow — a configuration problem, not a
      // malformed request.
      return $this->errorResponse($e->getMessage(), Response::HTTP_UNPROCESSABLE_ENTITY);
    }

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

}
