<?php

declare(strict_types=1);

namespace Drupal\flowdrop_interrupt\Controller;

use Drupal\Core\Controller\ControllerBase;
use Drupal\flowdrop\Controller\Api\ApiResponseTrait;
use Drupal\flowdrop_interrupt\Service\InterruptManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

/**
 * Controller for FlowDrop Interrupt API endpoints.
 *
 * Provides REST API endpoints for managing interrupt requests,
 * including listing, viewing, resolving, and cancelling interrupts.
 *
 * @internal This class is not part of the public API and may change without notice.
 */
final class InterruptApiController extends ControllerBase {

  use ApiResponseTrait;

  /**
   * Constructs a new InterruptApiController.
   *
   * @param \Drupal\flowdrop_interrupt\Service\InterruptManagerInterface $interruptManager
   *   The interrupt manager service.
   */
  public function __construct(
    protected readonly InterruptManagerInterface $interruptManager,
  ) {}

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container): static {
    return new static(
      $container->get("flowdrop_interrupt.manager")
    );
  }

  /**
   * Gets an interrupt by UUID.
   *
   * @param string $interrupt_id
   *   The interrupt UUID.
   *
   * @return \Symfony\Component\HttpFoundation\JsonResponse
   *   JSON response with interrupt details.
   */
  public function getInterrupt(string $interrupt_id): JsonResponse {
    $interrupt = $this->interruptManager->getInterrupt($interrupt_id);

    if ($interrupt === NULL) {
      return $this->errorResponse("Interrupt not found", Response::HTTP_NOT_FOUND);
    }

    // Check entity-level access for ownership-based permissions.
    if (!$interrupt->access('view', $this->currentUser())) {
      return $this->errorResponse("Access denied", Response::HTTP_FORBIDDEN);
    }

    return $this->successResponse($interrupt->toApiArray());
  }

  /**
   * Resolves an interrupt with user input.
   *
   * @param string $interrupt_id
   *   The interrupt UUID.
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The HTTP request.
   *
   * @return \Symfony\Component\HttpFoundation\JsonResponse
   *   JSON response with resolution result.
   */
  public function resolveInterrupt(string $interrupt_id, Request $request): JsonResponse {
    $data = $this->decodeJsonRequest($request);

    if (!array_key_exists("value", $data)) {
      return $this->errorResponse("Missing required field: value", Response::HTTP_BAD_REQUEST);
    }

    $value = $data["value"];

    // Check entity-level access for ownership-based permissions.
    $interruptEntity = $this->interruptManager->getInterrupt($interrupt_id);
    if ($interruptEntity === NULL) {
      return $this->errorResponse("Interrupt not found", Response::HTTP_NOT_FOUND);
    }
    if (!$interruptEntity->access('resolve', $this->currentUser())) {
      return $this->errorResponse("Access denied", Response::HTTP_FORBIDDEN);
    }

    try {
      $currentUser = $this->currentUser();
      $userId = $currentUser->isAuthenticated() ? (int) $currentUser->id() : NULL;

      $interrupt = $this->interruptManager->resolveInterrupt(
        $interrupt_id,
        $value,
        $userId
      );

      if ($interrupt === NULL) {
        return $this->errorResponse("Interrupt not found", Response::HTTP_NOT_FOUND);
      }

      return $this->successResponse($interrupt->toApiArray());
    }
    catch (\InvalidArgumentException $e) {
      return $this->errorResponse($e->getMessage(), Response::HTTP_CONFLICT);
    }
    catch (\Exception $e) {
      $this->getLogger("flowdrop_interrupt")->error(
        "Error resolving interrupt @id: @message",
        ["@id" => $interrupt_id, "@message" => $e->getMessage()]
      );

      return $this->errorResponse(
        "An error occurred while resolving the interrupt",
        Response::HTTP_INTERNAL_SERVER_ERROR
      );
    }
  }

  /**
   * Cancels an interrupt.
   *
   * @param string $interrupt_id
   *   The interrupt UUID.
   *
   * @return \Symfony\Component\HttpFoundation\JsonResponse
   *   JSON response with cancellation result.
   */
  public function cancelInterrupt(string $interrupt_id): JsonResponse {
    // Check entity-level access for ownership-based permissions.
    $interruptEntity = $this->interruptManager->getInterrupt($interrupt_id);
    if ($interruptEntity === NULL) {
      return $this->errorResponse("Interrupt not found", Response::HTTP_NOT_FOUND);
    }
    if (!$interruptEntity->access('cancel', $this->currentUser())) {
      return $this->errorResponse("Access denied", Response::HTTP_FORBIDDEN);
    }

    $success = $this->interruptManager->cancelInterrupt($interrupt_id);

    if (!$success) {
      // Check if interrupt exists.
      $interrupt = $this->interruptManager->getInterrupt($interrupt_id);

      if ($interrupt === NULL) {
        return $this->errorResponse("Interrupt not found", Response::HTTP_NOT_FOUND);
      }

      return $this->errorResponse(
        "Cannot cancel interrupt: status is " . $interrupt->getStatus()->value,
        Response::HTTP_CONFLICT
      );
    }

    // Reload to get the updated entity state after cancellation.
    $interrupt = $this->interruptManager->getInterrupt($interrupt_id);
    return $this->successResponse($interrupt?->toApiArray());
  }

  /**
   * Lists pending interrupts for a playground session.
   *
   * @param string $session_id
   *   The playground session UUID.
   *
   * @return \Symfony\Component\HttpFoundation\JsonResponse
   *   JSON response with list of interrupts.
   */
  public function listSessionInterrupts(string $session_id): JsonResponse {
    $session = $this->entityTypeManager()
      ->getStorage('flowdrop_session')
      ->loadByProperties(['uuid' => $session_id]);
    $session = reset($session);

    if (!$session) {
      return $this->errorResponse("Session not found", Response::HTTP_NOT_FOUND);
    }

    $interrupts = $this->interruptManager->getPendingInterruptsForSession((int) $session->id());

    $currentUser = $this->currentUser();
    $interrupts = array_filter($interrupts, fn($i) => $i->access('view', $currentUser));

    $data = array_map(
      fn($interrupt) => $interrupt->toApiArray(),
      $interrupts
    );

    return $this->successResponse(array_values($data));
  }

  /**
   * Lists pending interrupts for a pipeline.
   *
   * @param string $pipeline_id
   *   The pipeline ID.
   *
   * @return \Symfony\Component\HttpFoundation\JsonResponse
   *   JSON response with list of interrupts.
   */
  public function listPipelineInterrupts(string $pipeline_id): JsonResponse {
    $interrupts = $this->interruptManager->getPendingInterruptsForPipeline($pipeline_id);

    $currentUser = $this->currentUser();
    $interrupts = array_filter($interrupts, fn($i) => $i->access('view', $currentUser));

    $data = array_map(
      fn($interrupt) => $interrupt->toApiArray(),
      $interrupts
    );

    return $this->successResponse(array_values($data));
  }

  /**
   * Lists all pending interrupts owned by the current user.
   *
   * @return \Symfony\Component\HttpFoundation\JsonResponse
   *   JSON response with list of interrupts.
   */
  public function listUserInterrupts(): JsonResponse {
    $user_id = (int) $this->currentUser()->id();
    $interrupts = $this->interruptManager->getPendingInterruptsForUser($user_id);

    $data = array_map(
      fn($interrupt) => $interrupt->toApiArray(),
      $interrupts
    );

    return $this->successResponse($data);
  }

}
