<?php

declare(strict_types=1);

namespace Drupal\flowdrop_interrupt\Form;

use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\flowdrop_interrupt\Enum\InterruptStatus;
use Drupal\flowdrop_interrupt\Enum\InterruptType;
use Drupal\flowdrop_interrupt\Service\InterruptHandlerPluginManager;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Exposed filter form for the interrupt inbox.
 *
 * GET-based so filter state lives in the URL query and survives paging. The
 * inbox form reads the same query parameters to constrain its entity query.
 *
 * @internal This class is not part of the public API and may change without notice.
 */
final class InterruptFilterForm extends FormBase {

  public function __construct(
    private readonly Connection $database,
    private readonly EntityTypeManagerInterface $entityTypeManager,
    private readonly InterruptHandlerPluginManager $handlerManager,
  ) {}

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

  /**
   * {@inheritdoc}
   */
  public function getFormId(): string {
    return 'flowdrop_interrupt_filter_form';
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state): array {
    $request = $this->getRequest();

    $form['#method'] = 'get';
    $form['#action'] = Url::fromRoute('flowdrop_interrupt.inbox')->toString();
    $form['#attributes']['class'][] = 'views-exposed-form';

    $form['filters'] = [
      '#type' => 'container',
      '#attributes' => ['class' => ['form--inline', 'clearfix']],
    ];

    $form['filters']['workflow'] = [
      '#type' => 'select',
      '#title' => $this->t('Workflow'),
      '#options' => $this->getWorkflowOptions(),
      '#default_value' => $request->query->get('workflow', ''),
    ];

    $form['filters']['type'] = [
      '#type' => 'select',
      '#title' => $this->t('Type'),
      '#options' => $this->getTypeOptions(),
      '#default_value' => $request->query->get('type', ''),
    ];

    $form['filters']['session'] = [
      '#type' => 'number',
      '#title' => $this->t('Session'),
      '#min' => 1,
      '#default_value' => $request->query->get('session', ''),
      '#attributes' => ['placeholder' => $this->t('ID')],
    ];

    $form['filters']['search'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Message'),
      '#size' => 24,
      '#default_value' => $request->query->get('search', ''),
      '#attributes' => ['placeholder' => $this->t('Search text…')],
    ];

    $form['filters']['actions'] = [
      '#type' => 'actions',
    ];

    $form['filters']['actions']['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Filter'),
    ];

    $form['filters']['actions']['reset'] = [
      '#type' => 'link',
      '#title' => $this->t('Reset'),
      '#url' => Url::fromRoute('flowdrop_interrupt.inbox'),
      '#attributes' => ['class' => ['button']],
    ];

    // GET form: drop Drupal's internal POST fields from the query string.
    $form['form_build_id']['#access'] = FALSE;
    $form['form_token']['#access'] = FALSE;
    $form['form_id']['#access'] = FALSE;

    return $form;
  }

  /**
   * Builds the Workflow filter options from the user's pending interrupts.
   *
   * Only workflows that actually have a pending interrupt for this user are
   * listed, so the dropdown never offers a choice that yields zero rows.
   *
   * @return array<string, string|\Stringable>
   *   Options keyed by workflow ID, with an "any" sentinel first.
   */
  private function getWorkflowOptions(): array {
    $options = ['' => $this->t('- Any workflow -')];

    $ids = $this->distinctColumn('workflow_id');
    if ($ids === []) {
      return $options;
    }

    $storage = $this->entityTypeManager->getStorage('flowdrop_workflow');
    foreach ($ids as $id) {
      $workflow = $storage->load($id);
      $options[$id] = $workflow?->label() ?? $id;
    }
    asort($options);

    return $options;
  }

  /**
   * Builds the Type filter options limited to human-facing interrupt types.
   *
   * @return array<string, string>
   *   Options keyed by interrupt type value, with an "any" sentinel first.
   */
  private function getTypeOptions(): array {
    $options = ['' => $this->t('- Any type -')];

    foreach ($this->handlerManager->getHitlTypeIds() as $typeId) {
      $type = InterruptType::tryFrom($typeId);
      $options[$typeId] = $type?->label() ?? $typeId;
    }

    return $options;
  }

  /**
   * Returns the distinct non-empty values of a column for this user's inbox.
   *
   * @param string $column
   *   The column to read distinct values from.
   *
   * @return array<int, string>
   *   The distinct values.
   */
  private function distinctColumn(string $column): array {
    $hitlTypeIds = $this->handlerManager->getHitlTypeIds();
    if ($hitlTypeIds === []) {
      return [];
    }

    $query = $this->database->select('flowdrop_interrupt', 'i');
    $query->distinct();
    $query->addField('i', $column);
    $query->condition('i.uid', (int) $this->currentUser()->id());
    $query->condition('i.status', InterruptStatus::Pending->value);
    $query->condition('i.type', $hitlTypeIds, 'IN');
    $query->isNotNull('i.' . $column);
    $query->condition('i.' . $column, '', '<>');

    return array_values(array_filter($query->execute()->fetchCol()));
  }

  /**
   * {@inheritdoc}
   *
   * The form submits via GET; filter state is carried in the query string, so
   * there is no server-side submit work to do.
   */
  public function submitForm(array &$form, FormStateInterface $form_state): void {}

}
