<?php

declare(strict_types=1);

namespace Drupal\flowdrop_workflow\Controller\Api;

use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityMalformedException;
use Drupal\flowdrop\Controller\Api\ApiResponseTrait;
use Drupal\flowdrop_workflow\FlowDropWorkflowInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

/**
 * API controller for workflow management.
 *
 * @internal This class is not part of the public API and may change without notice.
 */
final class WorkflowsController extends ControllerBase {

  use ApiResponseTrait;

  /**
   * Constructs a WorkflowsController.
   */
  public function __construct(
    private readonly Connection $database,
  ) {}

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

  /**
   * Get all workflows.
   */
  public function getWorkflows(Request $request): JsonResponse {
    try {
      // Get query parameters.
      $search = $request->query->get('search');
      if ($search) {
        $search = $this->database->escapeLike($search);
      }
      $limit = (int) ($request->query->get('limit') ?? 50);
      $offset = (int) ($request->query->get('offset') ?? 0);

      // Validate parameters.
      $limit = max(1, min(100, $limit));
      $offset = max(0, $offset);

      // Get workflows from entity storage.
      $storage = $this->entityTypeManager()->getStorage('flowdrop_workflow');

      // Compute total count via a separate count query before pagination.
      $countQuery = $storage->getQuery();
      if ($search) {
        $countQuery = $countQuery->condition('label', '%' . $search . '%', 'LIKE');
      }
      $total = (int) $countQuery
        ->accessCheck(TRUE)
        ->count()
        ->execute();

      // Fetch paginated results.
      $query = $storage->getQuery();
      if ($search) {
        $query = $query->condition('label', '%' . $search . '%', 'LIKE');
      }

      $ids = $query
        ->sort('changed', 'DESC')
        ->accessCheck(TRUE)
        ->range($offset, $limit)
        ->execute();

      $workflows = $storage->loadMultiple($ids);

      $data = [];
      foreach ($workflows as $workflow) {
        if ($workflow instanceof FlowDropWorkflowInterface) {
          $data[] = [
            'id' => $workflow->id(),
            'name' => $workflow->label(),
            'description' => $workflow->getDescription(),
            'nodes' => $workflow->getNodes(),
            'edges' => $workflow->getEdges(),
            'metadata' => $workflow->getMetadata(),
            'created' => $workflow->getCreated(),
            'changed' => $workflow->getChanged(),
            'uid' => $workflow->getUid(),
          ];
        }
      }

      return $this->paginatedResponse($data, $total, $limit, $offset);

    }
    catch (HttpExceptionInterface $e) {
      throw $e;
    }
    catch (\Exception $e) {
      $this->getLogger('flowdrop')->error('Error fetching workflows: @error', ['@error' => $e->getMessage()]);

      return $this->errorResponse('Failed to fetch workflows', Response::HTTP_INTERNAL_SERVER_ERROR);
    }
  }

  /**
   * Create a new workflow.
   */
  public function createWorkflow(Request $request): JsonResponse {
    try {
      $data = $this->decodeJsonRequest($request);

      // Validate required fields.
      if (empty($data['name'])) {
        throw new BadRequestHttpException('Workflow name is required');
      }

      if (strlen($data['name']) > 255) {
        throw new BadRequestHttpException('Workflow name too long (max 255 characters)');
      }

      // Reject if client-supplied ID exists (prevents silent overwrite).
      if (!empty($data['id'])) {
        $existing = $this->entityTypeManager()->getStorage('flowdrop_workflow')->load($data['id']);
        if ($existing) {
          return $this->errorResponse('A workflow with this ID already exists', Response::HTTP_CONFLICT);
        }
      }

      // Enforce complexity limits.
      if (count($data['nodes'] ?? []) > 500) {
        throw new BadRequestHttpException('Workflow exceeds maximum node limit (500)');
      }
      if (count($data['edges'] ?? []) > 1000) {
        throw new BadRequestHttpException('Workflow exceeds maximum edge limit (1000)');
      }

      // Create workflow entity.
      $workflow = $this->entityTypeManager()->getStorage('flowdrop_workflow')->create([
        'id' => $data['id'] ?? $this->generateWorkflowId(),
        'label' => trim($data['name']),
        'description' => trim($data['description'] ?? ''),
        'nodes' => $data['nodes'] ?? [],
        'edges' => $data['edges'] ?? [],
        'metadata' => $data['metadata'] ?? [],
        'uid' => $this->currentUser()->id(),
      ]);

      if (!$workflow instanceof FlowDropWorkflowInterface) {
        throw new EntityMalformedException("Workflow creation failed");
      }

      $workflow->save();

      return $this->successResponse([
        'id' => $workflow->id(),
        'name' => $workflow->label(),
        'description' => $workflow->getDescription(),
        'nodes' => $workflow->getNodes(),
        'edges' => $workflow->getEdges(),
        'metadata' => $workflow->getMetadata(),
        'created' => $workflow->getCreated(),
        'changed' => $workflow->getChanged(),
        'uid' => $workflow->getUid(),
      ], Response::HTTP_CREATED);

    }
    catch (HttpExceptionInterface $e) {
      throw $e;
    }
    catch (\Exception $e) {
      $this->getLogger('flowdrop')->error('Error creating workflow: @error', ['@error' => $e->getMessage()]);

      return $this->errorResponse('Failed to create workflow', Response::HTTP_INTERNAL_SERVER_ERROR);
    }
  }

  /**
   * Get a specific workflow by ID.
   */
  public function getWorkflow(Request $request, string $id): JsonResponse {
    try {
      // Get workflow from entity storage.
      $workflow = $this->entityTypeManager()
        ->getStorage('flowdrop_workflow')
        ->load($id);

      if (!$workflow instanceof FlowDropWorkflowInterface) {
        throw new NotFoundHttpException('Workflow not found');
      }

      return $this->successResponse([
        'id' => $workflow->id(),
        'name' => $workflow->label(),
        'description' => $workflow->getDescription(),
        'nodes' => $workflow->getNodes(),
        'edges' => $workflow->getEdges(),
        'metadata' => $workflow->getMetadata(),
        'created' => $workflow->getCreated(),
        'changed' => $workflow->getChanged(),
        'uid' => $workflow->getUid(),
      ]);

    }
    catch (HttpExceptionInterface $e) {
      throw $e;
    }
    catch (\Exception $e) {
      $this->getLogger('flowdrop')->error('Error fetching workflow: @error', ['@error' => $e->getMessage()]);

      return $this->errorResponse('Failed to fetch workflow', Response::HTTP_INTERNAL_SERVER_ERROR);
    }
  }

  /**
   * Update an existing workflow.
   */
  public function updateWorkflow(Request $request, string $id): JsonResponse {
    try {
      $data = $this->decodeJsonRequest($request);

      // Get existing workflow.
      $workflow = $this->entityTypeManager()
        ->getStorage('flowdrop_workflow')
        ->load($id);

      if (!$workflow instanceof FlowDropWorkflowInterface) {
        throw new NotFoundHttpException('Workflow not found');
      }
      // Validate required fields.
      if (empty($data['name'])) {
        throw new BadRequestHttpException('Workflow name is required');
      }

      if (strlen($data['name']) > 255) {
        throw new BadRequestHttpException('Workflow name too long (max 255 characters)');
      }

      // Update workflow fields.
      if (isset($data['name'])) {
        $workflow->setLabel($data['name']);
      }
      if (isset($data['description'])) {
        $workflow->setDescription($data['description']);
      }

      if (isset($data['nodes'])) {
        if (count($data['nodes']) > 500) {
          throw new BadRequestHttpException('Workflow exceeds maximum node limit (500)');
        }
        $workflow->setNodes($data['nodes']);
      }
      if (isset($data['edges'])) {
        if (count($data['edges']) > 1000) {
          throw new BadRequestHttpException('Workflow exceeds maximum edge limit (1000)');
        }
        $workflow->setEdges($data['edges']);
      }
      if (isset($data['metadata'])) {
        $workflow->setMetadata($data['metadata']);
      }

      $workflow->save();

      return $this->successResponse([
        'id' => $workflow->id(),
        'name' => $workflow->label(),
        'description' => $workflow->getDescription(),
        'nodes' => $workflow->getNodes(),
        'edges' => $workflow->getEdges(),
        'metadata' => $workflow->getMetadata(),
        'created' => $workflow->getCreated(),
        'changed' => $workflow->getChanged(),
        'uid' => $workflow->getUid(),
      ]);

    }
    catch (HttpExceptionInterface $e) {
      throw $e;
    }
    catch (\Exception $e) {
      $this->getLogger('flowdrop')->error('Error updating workflow: @error', ['@error' => $e->getMessage()]);

      return $this->errorResponse('Failed to update workflow', Response::HTTP_INTERNAL_SERVER_ERROR);
    }
  }

  /**
   * Delete a workflow.
   */
  public function deleteWorkflow(Request $request, string $id): JsonResponse {
    try {
      // Get existing workflow.
      $workflow = $this->entityTypeManager()
        ->getStorage('flowdrop_workflow')
        ->load($id);

      if (!$workflow) {
        throw new NotFoundHttpException('Workflow not found');
      }

      // Delete the workflow.
      $workflow->delete();

      return $this->successResponse(NULL);

    }
    catch (HttpExceptionInterface $e) {
      throw $e;
    }
    catch (\Exception $e) {
      $this->getLogger('flowdrop')->error('Error deleting workflow: @error', ['@error' => $e->getMessage()]);

      return $this->errorResponse('Failed to delete workflow', Response::HTTP_INTERNAL_SERVER_ERROR);
    }
  }

  /**
   * Generate a unique workflow ID.
   *
   * @return string
   *   A unique workflow ID.
   */
  protected function generateWorkflowId(): string {
    return 'workflow_' . bin2hex(random_bytes(8));
  }

}
