<?php

declare(strict_types=1);

namespace Drupal\flowdrop_trigger\Controller;

use Drupal\Core\Controller\ControllerBase;
use Drupal\flowdrop_trigger\Entity\FlowDropTriggerConfig;
use Drupal\flowdrop_trigger\FlowDropTriggerConfigInterface;
use Drupal\flowdrop_trigger\Service\EventTypePluginManager;
use Drupal\flowdrop_trigger\Service\TriggerConfigSchemaBuilder;
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\NotFoundHttpException;

/**
 * API controller for trigger configuration CRUD operations.
 *
 * Provides REST endpoints for the workflow editor to create,
 * read, update, and delete trigger configurations.
 *
 * Endpoints:
 * - POST   /api/flowdrop/triggers              Create new trigger
 * - GET    /api/flowdrop/triggers/{id}         Get trigger with schema
 * - PATCH  /api/flowdrop/triggers/{id}         Update trigger
 * - DELETE /api/flowdrop/triggers/{id}         Delete trigger
 * - GET    /api/flowdrop/triggers/schema       Get schema for new trigger
 * - GET    /api/flowdrop/triggers/event-types  List available event types
 */
class TriggerConfigApiController extends ControllerBase {

  /**
   * The event type plugin manager.
   *
   * @var \Drupal\flowdrop_trigger\Service\EventTypePluginManager
   */
  protected EventTypePluginManager $eventTypeManager;

  /**
   * The schema builder service.
   *
   * @var \Drupal\flowdrop_trigger\Service\TriggerConfigSchemaBuilder
   */
  protected TriggerConfigSchemaBuilder $schemaBuilder;

  /**
   * Constructs a TriggerConfigApiController.
   *
   * @param \Drupal\flowdrop_trigger\Service\EventTypePluginManager $eventTypeManager
   *   The event type plugin manager.
   * @param \Drupal\flowdrop_trigger\Service\TriggerConfigSchemaBuilder $schemaBuilder
   *   The schema builder service.
   */
  public function __construct(
    EventTypePluginManager $eventTypeManager,
    TriggerConfigSchemaBuilder $schemaBuilder,
  ) {
    $this->eventTypeManager = $eventTypeManager;
    $this->schemaBuilder = $schemaBuilder;
  }

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

  /**
   * Create a new trigger configuration.
   *
   * POST /api/flowdrop/triggers.
   *
   * Request body:
   * {
   *   "workflow_id": "my_workflow",
   *   "node_id": "node_123",
   *   "event_type": "entity.insert" (optional, defaults to entity.insert)
   * }
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The request object.
   *
   * @return \Symfony\Component\HttpFoundation\JsonResponse
   *   JSON response with created entity data and schema.
   */
  public function createTriggerConfig(Request $request): JsonResponse {
    $data = json_decode($request->getContent(), TRUE);

    if (empty($data["workflow_id"])) {
      throw new BadRequestHttpException("workflow_id is required");
    }

    if (empty($data["node_id"])) {
      throw new BadRequestHttpException("node_id is required");
    }

    $workflowId = (string) $data["workflow_id"];
    $nodeId = (string) $data["node_id"];
    $eventType = (string) ($data["event_type"] ?? "entity.insert");

    // Validate event type exists.
    if (!$this->eventTypeManager->hasDefinition($eventType)) {
      throw new BadRequestHttpException(
        sprintf("Unknown event type: %s", $eventType)
      );
    }

    // Generate deterministic entity ID.
    $entityId = FlowDropTriggerConfig::generateId($workflowId, $nodeId);

    // Check if entity already exists.
    $storage = $this->entityTypeManager()->getStorage("flowdrop_trigger_config");
    $existing = $storage->load($entityId);

    if ($existing !== NULL) {
      // Return existing entity instead of creating duplicate.
      return new JsonResponse([
        "id" => $existing->id(),
        "schema" => $this->schemaBuilder->buildSchema($existing),
        "uiSchema" => $this->schemaBuilder->buildUiSchema($existing),
        "values" => $this->entityToValues($existing),
        "created" => FALSE,
      ]);
    }

    // Get default values.
    $defaults = $this->schemaBuilder->getDefaultValues($eventType);

    // Create entity with defaults.
    /** @var \Drupal\flowdrop_trigger\FlowDropTriggerConfigInterface $entity */
    $entity = $storage->create([
      "id" => $entityId,
      "label" => sprintf("Trigger: %s", $eventType),
      "workflow_id" => $workflowId,
      "node_id" => $nodeId,
      "event_type" => $eventType,
      "conditions" => $defaults["conditions"],
      "orchestrator_settings" => $defaults["orchestrator_settings"],
      "initial_data_mapping" => [],
      "status" => TRUE,
      "weight" => 0,
    ]);

    $entity->save();

    return new JsonResponse([
      "id" => $entity->id(),
      "schema" => $this->schemaBuilder->buildSchema($entity),
      "uiSchema" => $this->schemaBuilder->buildUiSchema($entity),
      "values" => $this->entityToValues($entity),
      "created" => TRUE,
    ], Response::HTTP_CREATED);
  }

  /**
   * Get a trigger configuration with schema.
   *
   * GET /api/flowdrop/triggers/{id}
   *
   * @param string $id
   *   The trigger config ID.
   *
   * @return \Symfony\Component\HttpFoundation\JsonResponse
   *   JSON response with entity data and schema.
   */
  public function get(string $id): JsonResponse {
    $entity = $this->loadEntity($id);

    return new JsonResponse([
      "id" => $entity->id(),
      "schema" => $this->schemaBuilder->buildSchema($entity),
      "uiSchema" => $this->schemaBuilder->buildUiSchema($entity),
      "values" => $this->entityToValues($entity),
    ]);
  }

  /**
   * Update a trigger configuration.
   *
   * PATCH /api/flowdrop/triggers/{id}
   *
   * @param string $id
   *   The trigger config ID.
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The request object.
   *
   * @return \Symfony\Component\HttpFoundation\JsonResponse
   *   JSON response with updated entity data.
   */
  public function update(string $id, Request $request): JsonResponse {
    $entity = $this->loadEntity($id);
    $data = json_decode($request->getContent(), TRUE);

    if ($data === NULL) {
      throw new BadRequestHttpException("Invalid JSON in request body");
    }

    // Update allowed fields.
    if (array_key_exists("label", $data)) {
      $entity->set("label", (string) $data["label"]);
    }

    if (array_key_exists("description", $data)) {
      $entity->setDescription((string) $data["description"]);
    }

    if (array_key_exists("event_type", $data)) {
      $eventType = (string) $data["event_type"];
      if (!$this->eventTypeManager->hasDefinition($eventType)) {
        throw new BadRequestHttpException(
          sprintf("Unknown event type: %s", $eventType)
        );
      }
      $entity->setEventType($eventType);
    }

    if (array_key_exists("conditions", $data) && is_array($data["conditions"])) {
      $entity->setConditions($data["conditions"]);
    }

    if (array_key_exists("orchestrator_settings", $data) && is_array($data["orchestrator_settings"])) {
      $entity->setOrchestratorSettings($data["orchestrator_settings"]);
    }

    if (array_key_exists("initial_data_mapping", $data) && is_array($data["initial_data_mapping"])) {
      $entity->setInitialDataMapping($data["initial_data_mapping"]);
    }

    if (array_key_exists("status", $data)) {
      $entity->set("status", (bool) $data["status"]);
    }

    if (array_key_exists("weight", $data)) {
      $entity->setWeight((int) $data["weight"]);
    }

    $entity->save();

    // Rebuild schema if event_type changed.
    return new JsonResponse([
      "id" => $entity->id(),
      "schema" => $this->schemaBuilder->buildSchema($entity),
      "uiSchema" => $this->schemaBuilder->buildUiSchema($entity),
      "values" => $this->entityToValues($entity),
      "success" => TRUE,
    ]);
  }

  /**
   * Delete a trigger configuration.
   *
   * DELETE /api/flowdrop/triggers/{id}
   *
   * @param string $id
   *   The trigger config ID.
   *
   * @return \Symfony\Component\HttpFoundation\JsonResponse
   *   JSON response confirming deletion.
   */
  public function delete(string $id): JsonResponse {
    $entity = $this->loadEntity($id);
    $entity->delete();

    return new JsonResponse([
      "success" => TRUE,
      "deleted_id" => $id,
    ]);
  }

  /**
   * Get schema for a new trigger (without creating entity).
   *
   * GET /api/flowdrop/triggers/schema?event_type=entity.insert.
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The request object.
   *
   * @return \Symfony\Component\HttpFoundation\JsonResponse
   *   JSON response with schema.
   */
  public function schema(Request $request): JsonResponse {
    $eventType = $request->query->get("event_type", "entity.insert");

    if (!is_string($eventType)) {
      $eventType = "entity.insert";
    }

    if (!$this->eventTypeManager->hasDefinition($eventType)) {
      throw new BadRequestHttpException(
        sprintf("Unknown event type: %s", $eventType)
      );
    }

    return new JsonResponse([
      "schema" => $this->schemaBuilder->buildSchemaForEventType($eventType),
      "uiSchema" => $this->schemaBuilder->buildUiSchemaForEventType($eventType),
      "defaults" => $this->schemaBuilder->getDefaultValues($eventType),
    ]);
  }

  /**
   * List available event types.
   *
   * GET /api/flowdrop/triggers/event-types?category=user.
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The request object.
   *
   * @return \Symfony\Component\HttpFoundation\JsonResponse
   *   JSON response with event types, optionally filtered by category.
   */
  public function eventTypes(Request $request): JsonResponse {
    $definitions = $this->eventTypeManager->getDefinitions();
    $eventTypes = [];

    // Get optional category filter from query parameter.
    $categoryFilter = $request->query->get("category");
    $categoryFilter = is_string($categoryFilter) ? $categoryFilter : NULL;

    foreach ($definitions as $id => $definition) {
      $category = $definition["category"] ?? "other";

      // Filter by category if specified.
      if ($categoryFilter !== NULL && $category !== $categoryFilter) {
        continue;
      }

      $eventTypes[] = [
        "id" => $id,
        "label" => (string) ($definition["label"] ?? $id),
        "description" => (string) ($definition["description"] ?? ""),
        "category" => $category,
        "defaultOrchestrator" => $definition["defaultOrchestrator"] ?? "asynchronous",
      ];
    }

    // Sort by category then label.
    usort($eventTypes, function ($a, $b) {
      $categoryCompare = $a["category"] <=> $b["category"];
      if ($categoryCompare !== 0) {
        return $categoryCompare;
      }
      return $a["label"] <=> $b["label"];
    });

    return new JsonResponse(["event_types" => $eventTypes]);
  }

  /**
   * Load a trigger config entity by ID.
   *
   * @param string $id
   *   The entity ID.
   *
   * @return \Drupal\flowdrop_trigger\FlowDropTriggerConfigInterface
   *   The loaded entity.
   *
   * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
   *   If entity not found.
   */
  protected function loadEntity(string $id): FlowDropTriggerConfigInterface {
    $storage = $this->entityTypeManager()->getStorage("flowdrop_trigger_config");
    $entity = $storage->load($id);

    if (!$entity instanceof FlowDropTriggerConfigInterface) {
      throw new NotFoundHttpException(
        sprintf("Trigger configuration not found: %s", $id)
      );
    }

    return $entity;
  }

  /**
   * Convert entity to values array for frontend.
   *
   * @param \Drupal\flowdrop_trigger\FlowDropTriggerConfigInterface $entity
   *   The entity.
   *
   * @return array<string, mixed>
   *   The values array.
   */
  protected function entityToValues(FlowDropTriggerConfigInterface $entity): array {
    $entityId = $entity->id();

    return [
      "id" => $entityId,
      "label" => $entity->label(),
      "description" => $entity->getDescription(),
      "workflow_id" => $entity->getWorkflowId(),
      "node_id" => $entity->getNodeId(),
      "event_type" => $entity->getEventType(),
      "conditions" => $entity->getConditions(),
      "orchestrator_settings" => $entity->getOrchestratorSettings(),
      "initial_data_mapping" => $entity->getInitialDataMapping(),
      "status" => $entity->status(),
      "weight" => $entity->getWeight(),
      // Admin links for configuration UI.
      "_links" => [
        "admin_edit" => sprintf("/admin/flowdrop/triggers/%s/edit", $entityId),
        "admin_view" => sprintf("/admin/flowdrop/triggers/%s", $entityId),
        "admin_delete" => sprintf("/admin/flowdrop/triggers/%s/delete", $entityId),
      ],
    ];
  }

}
