<?php

declare(strict_types=1);

namespace Drupal\flowdrop_trigger\Controller;

use Drupal\Component\Utility\Html;
use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\Query\QueryInterface;
use Drupal\Core\Form\FormBuilderInterface;
use Drupal\Core\Pager\PagerManagerInterface;
use Drupal\Core\Url;
use Drupal\flowdrop_trigger\FlowDropTriggerConfigInterface;
use Drupal\flowdrop_trigger\Form\TriggerConfigFilterForm;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RequestStack;

/**
 * List builder for FlowDrop Trigger Configuration entities.
 *
 * Provides an admin list of all trigger configurations with status,
 * event type, workflow reference, and action links.
 * Features sortable table headers with default sorting by weight.
 * Supports pagination for large datasets.
 *
 * @internal This class is not part of the public API and may change without notice.
 */
class TriggerConfigListBuilder extends ConfigEntityListBuilder {

  /**
   * The number of entities to list per page.
   *
   * @var int
   */
  protected $limit = 50;

  /**
   * The current sort field.
   *
   * @var string
   */
  protected string $sortField = "weight";

  /**
   * The current sort direction.
   *
   * @var string
   */
  protected string $sortDirection = "asc";

  /**
   * Constructs a new TriggerConfigListBuilder object.
   *
   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
   *   The entity type definition.
   * @param \Drupal\Core\Entity\EntityStorageInterface $storage
   *   The entity storage class.
   * @param \Drupal\Core\Entity\EntityStorageInterface $workflowStorage
   *   The workflow entity storage.
   * @param \Drupal\Core\Pager\PagerManagerInterface $pagerManager
   *   The pager manager service.
   * @param \Symfony\Component\HttpFoundation\RequestStack $requestStack
   *   The request stack.
   * @param \Drupal\Core\Form\FormBuilderInterface $formBuilder
   *   The form builder.
   */
  public function __construct(
    EntityTypeInterface $entity_type,
    EntityStorageInterface $storage,
    protected readonly EntityStorageInterface $workflowStorage,
    protected readonly PagerManagerInterface $pagerManager,
    protected readonly RequestStack $requestStack,
    protected readonly FormBuilderInterface $formBuilder,
  ) {
    parent::__construct($entity_type, $storage);
  }

  /**
   * {@inheritdoc}
   */
  public static function createInstance(
    ContainerInterface $container,
    EntityTypeInterface $entity_type,
  ): static {
    $entity_type_manager = $container->get("entity_type.manager");
    return new static(
      $entity_type,
      $entity_type_manager->getStorage($entity_type->id()),
      $entity_type_manager->getStorage("flowdrop_workflow"),
      $container->get("pager.manager"),
      $container->get("request_stack"),
      $container->get("form_builder"),
    );
  }

  /**
   * {@inheritdoc}
   *
   * Loads the full result set with no query pager so that filtering, sorting,
   * and pagination can be applied in-memory in ::load(). The base
   * implementation applies a query pager whenever $limit is set, which would
   * slice the result to the first page before this list builder's custom
   * logic runs — breaking cross-page filtering/sorting and suppressing the
   * pager (its total item count would be capped at one page's worth).
   */
  protected function getEntityListQuery(): QueryInterface {
    return $this->getStorage()->getQuery()->accessCheck(TRUE);
  }

  /**
   * {@inheritdoc}
   *
   * @return array<\Drupal\Core\Entity\EntityInterface>
   *   An array of entities sorted according to the current tablesort.
   */
  public function load(): array {
    $entities = parent::load();

    // Apply filters from URL query parameters.
    $request = $this->requestStack->getCurrentRequest();
    $workflowFilter = $request->query->get('workflow', '');
    $statusFilter = $request->query->get('status', '');

    if (!empty($workflowFilter)) {
      $entities = array_filter($entities, function ($e) use ($workflowFilter): bool {
        /** @var \Drupal\flowdrop_trigger\FlowDropTriggerConfigInterface $e */
        return $e->getWorkflowId() === $workflowFilter;
      });
    }
    if ($statusFilter === 'enabled') {
      $entities = array_filter($entities, function ($e): bool {
        /** @var \Drupal\flowdrop_trigger\FlowDropTriggerConfigInterface $e */
        return (bool) $e->status();
      });
    }
    elseif ($statusFilter === 'disabled') {
      $entities = array_filter($entities, function ($e): bool {
        /** @var \Drupal\flowdrop_trigger\FlowDropTriggerConfigInterface $e */
        return !$e->status();
      });
    }

    // Get sort parameters from request.
    $order = $request->query->get("order");
    $sort = $request->query->get("sort", "asc");

    // Determine the field to sort by based on header label.
    if ($order !== NULL) {
      $fieldMap = [
        "Label" => "label",
        "Event Type" => "event_type",
        "Workflow" => "workflow",
        "Conditions" => "conditions",
        "Status" => "status",
        "Weight" => "weight",
      ];
      if (isset($fieldMap[$order])) {
        $this->sortField = $fieldMap[$order];
        $this->sortDirection = in_array($sort, ['asc', 'desc'], TRUE) ? $sort : 'asc';
      }
    }

    // Sort entities in memory.
    usort($entities, function ($a, $b) {
      /** @var \Drupal\flowdrop_trigger\FlowDropTriggerConfigInterface $a */
      /** @var \Drupal\flowdrop_trigger\FlowDropTriggerConfigInterface $b */
      $valueA = $this->getEntitySortValue($a, $this->sortField);
      $valueB = $this->getEntitySortValue($b, $this->sortField);

      // Compare values.
      if ($valueA === $valueB) {
        return 0;
      }

      $result = $valueA < $valueB ? -1 : 1;

      // Reverse for descending order.
      if ($this->sortDirection === "desc") {
        $result = -$result;
      }

      return $result;
    });

    // Apply pagination.
    return $this->getEntityListForPager($entities);
  }

  /**
   * Gets a paginated slice of entities.
   *
   * @param array<\Drupal\Core\Entity\EntityInterface> $entities
   *   All sorted entities.
   *
   * @return array<\Drupal\Core\Entity\EntityInterface>
   *   Entities for the current page.
   */
  protected function getEntityListForPager(array $entities): array {
    // Get current page from request.
    $page = $this->requestStack->getCurrentRequest()?->query->getInt("page", 0) ?? 0;

    // Calculate offset.
    $offset = $page * $this->limit;

    // Initialize pager.
    $this->pagerManager->createPager(count($entities), $this->limit);

    // Return slice of entities for current page.
    return array_slice($entities, $offset, $this->limit);
  }

  /**
   * Gets the value for sorting from an entity.
   *
   * @param \Drupal\flowdrop_trigger\FlowDropTriggerConfigInterface $entity
   *   The entity to get the value from.
   * @param string $field
   *   The field name to get.
   *
   * @return mixed
   *   The value for sorting.
   */
  protected function getEntitySortValue(FlowDropTriggerConfigInterface $entity, string $field): mixed {
    switch ($field) {
      case "label":
        return strtolower($entity->label() ?? "");

      case "event_type":
        return strtolower($entity->getEventType());

      case "workflow":
        return strtolower($entity->getWorkflowId());

      case "conditions":
        return count($entity->getConditions());

      case "status":
        return $entity->status() ? 1 : 0;

      case "weight":
        return $entity->getWeight();

      default:
        return "";
    }
  }

  /**
   * {@inheritdoc}
   *
   * Consistent column order: Label, Status, Event Type, Workflow,
   * Conditions, Weight.
   *
   * @return array<string, array<string, mixed>>
   *   An array of sortable header definitions.
   */
  public function buildHeader(): array {
    $header = [
      "label" => [
        "data" => $this->t("Label"),
        "field" => "label",
        "specifier" => "label",
      ],
      "status" => [
        "data" => $this->t("Status"),
        "field" => "status",
        "specifier" => "status",
      ],
      "event_type" => [
        "data" => $this->t("Event Type"),
        "field" => "event_type",
        "specifier" => "event_type",
      ],
      "workflow" => [
        "data" => $this->t("Workflow"),
        "field" => "workflow",
        "specifier" => "workflow",
      ],
      "conditions" => [
        "data" => $this->t("Conditions"),
        "field" => "conditions",
        "specifier" => "conditions",
      ],
      "weight" => [
        "data" => $this->t("Weight"),
        "field" => "weight",
        "specifier" => "weight",
        "sort" => "asc",
      ],
    ];

    return $header + parent::buildHeader();
  }

  /**
   * {@inheritdoc}
   */
  public function buildRow(EntityInterface $entity): array {
    if (!$entity instanceof FlowDropTriggerConfigInterface) {
      return parent::buildRow($entity);
    }

    // Get workflow label.
    $workflowLabel = $this->getWorkflowLabel($entity->getWorkflowId());

    // Format conditions summary.
    $conditionsSummary = $this->formatConditions($entity->getConditions());

    $row = [
      "label" => [
        "data" => [
          "#markup" => Html::escape($entity->label()),
        ],
      ],
      "status" => $this->buildStatusPill($entity->status()),
      "event_type" => [
        "data" => [
          "#markup" => sprintf("<code>%s</code>", Html::escape($entity->getEventType())),
        ],
      ],
      "workflow" => [
        "data" => $workflowLabel,
      ],
      "conditions" => [
        "data" => [
          "#markup" => Html::escape($conditionsSummary),
        ],
      ],
      "weight" => $entity->getWeight(),
    ];

    return $row + parent::buildRow($entity);
  }

  /**
   * {@inheritdoc}
   *
   * @return array<string, mixed>
   *   The render array for the list.
   */
  public function render(): array {
    $build = parent::render();

    // Add tablesort to the table.
    $build["table"]["#header"] = $this->buildHeader();
    $build["table"]["#attributes"]["class"][] = "sortable-table";

    $build["filter_form"] = $this->formBuilder->getForm(TriggerConfigFilterForm::class);
    $build["filter_form"]["#weight"] = -15;

    // Add description.
    $build["description"] = [
      "#markup" => "<p>" . $this->t(
        "Trigger configurations define when workflows are executed in response to system events. Each trigger is linked to a specific node in a workflow."
      ) . "</p>",
      "#weight" => -10,
    ];

    // Add empty message.
    if (empty($this->load())) {
      $build["empty"] = [
        "#markup" => "<p>" . $this->t(
          "No trigger configurations found. Triggers are created automatically when you add a Trigger node to a workflow in the visual editor."
        ) . "</p>",
      ];
    }

    // Add pager for pagination.
    $build["pager"] = [
      "#type" => "pager",
    ];

    return $build;
  }

  /**
   * Get workflow label with link.
   *
   * @param string $workflowId
   *   The workflow ID.
   *
   * @return array<string, mixed>
   *   Render array for workflow label.
   */
  protected function getWorkflowLabel(string $workflowId): array {
    $workflow = $this->workflowStorage->load($workflowId);

    if ($workflow === NULL) {
      return [
        "#markup" => sprintf(
          '<span class="color-error">%s (missing)</span>',
          Html::escape($workflowId)
        ),
      ];
    }

    return [
      "#type" => "link",
      "#title" => $workflow->label(),
      "#url" => Url::fromRoute("entity.flowdrop_workflow.edit_form", [
        "flowdrop_workflow" => $workflowId,
      ]),
    ];
  }

  /**
   * Builds a status pill render array for table cell display.
   *
   * @param bool $isEnabled
   *   Whether the entity is enabled.
   *
   * @return array<string, mixed>
   *   A table cell render array containing the status pill component.
   */
  protected function buildStatusPill(bool $isEnabled): array {
    return [
      "data" => [
        "#type" => "component",
        "#component" => "flowdrop_ui_components:pill",
        "#props" => [
          "label" => $isEnabled ? (string) $this->t("Enabled") : (string) $this->t("Disabled"),
          "variant" => $isEnabled ? "success" : "neutral",
          "style" => "soft",
          "size" => "sm",
        ],
      ],
    ];
  }

  /**
   * Format conditions as a summary string.
   *
   * @param array<string, mixed> $conditions
   *   The conditions array.
   *
   * @return string
   *   Formatted summary.
   */
  protected function formatConditions(array $conditions): string {
    $parts = [];

    $entityTypes = $conditions["entity_types"] ?? [];
    if (!empty($entityTypes)) {
      $parts[] = sprintf("Types: %s", implode(", ", $entityTypes));
    }

    $bundles = $conditions["bundles"] ?? [];
    if (!empty($bundles)) {
      $parts[] = sprintf("Bundles: %s", implode(", ", $bundles));
    }

    $roles = $conditions["roles"] ?? [];
    if (!empty($roles)) {
      $parts[] = sprintf("Roles: %s", implode(", ", $roles));
    }

    if (empty($parts)) {
      return (string) $this->t("All");
    }

    return implode("; ", $parts);
  }

  /**
   * {@inheritdoc}
   */
  public function getDefaultOperations(EntityInterface $entity): array {
    $operations = parent::getDefaultOperations($entity);

    // Add view operation using the custom route.
    $operations["view"] = [
      "title" => $this->t("View"),
      "url" => Url::fromRoute("flowdrop.trigger.view", [
        "flowdrop_trigger_config" => $entity->id(),
      ]),
      "weight" => -10,
    ];

    return $operations;
  }

}
