<?php

namespace Drupal\visitors\Plugin\views\sort;

use Drupal\Core\Form\FormStateInterface;
use Drupal\views\Attribute\ViewsSort;
use Drupal\views\Plugin\views\sort\SortPluginBase;
use Drupal\visitors\Service\SequenceService;

/**
 * Sort handler that uses defined number ranges instead of raw values.
 */
#[ViewsSort("visitors_number_range")]
class NumberRange extends SortPluginBase {

  /**
   * Gets the total count of configured ranges.
   *
   * @return int
   *   The total number of ranges.
   */
  public function getTotalRanges(): int {
    $ranges_text = $this->options['ranges'];
    $ranges = preg_split('/\r\n|\r|\n/', $ranges_text);
    $ranges = array_map('trim', $ranges);
    $ranges = array_filter($ranges, function ($range) {
      return trim($range) !== '';
    });

    return count($ranges);
  }

  /**
   * {@inheritdoc}
   */
  public function defineOptions() {
    $options = parent::defineOptions();
    $options['ranges'] = ['default' => "0\n1\n2\n3\n4\n5\n6-7\n8-10\n11-14\n15-20\n21+"];
    return $options;
  }

  /**
   * {@inheritdoc}
   */
  public function buildOptionsForm(&$form, FormStateInterface $form_state) {
    parent::buildOptionsForm($form, $form_state);

    $form['ranges'] = [
      '#type' => 'textarea',
      '#title' => $this->t('Number ranges'),
      '#description' => $this->t("Enter one range per line. Examples: <code>0</code>, <code>6-7</code>, <code>21+</code>. The sort order will follow the order you define here."),
      '#default_value' => $this->options['ranges'],
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function query() {
    // Add the base field to the query.
    $this->ensureMyTable();

    // Check if the field already exists in the query.
    $existing_field_alias = $this->tableAlias . '_' . $this->realField . '__range';

    // Look through the query fields to see if our target field already exists.
    foreach ($this->query->fields as $field_info) {
      if (isset($field_info['alias']) && $field_info['alias'] === $existing_field_alias) {
        // Field already exists, use it for sorting.
        $this->query->addOrderBy(NULL, NULL, $this->options['order'], $existing_field_alias);
        return;
      }
    }

    // Field doesn't exist, create our own CASE expression.
    $field = "$this->tableAlias.$this->realField";

    $ranges_text = $this->options['ranges'];
    $ranges = preg_split('/\r\n|\r|\n/', $ranges_text);
    $ranges = array_map('trim', $ranges);
    $ranges = array_filter($ranges, function ($range) {
      return trim($range) !== '';
    });

    $case_sql = [];
    $i = 0;
    foreach ($ranges as $range) {
      // Range "X-Y".
      if (preg_match('/^(\d+)\-(\d+)$/', $range, $m)) {
        $case_sql[] = "WHEN $field BETWEEN {$m[1]} AND {$m[2]} THEN $i";
      }
      // Range "X+".
      elseif (preg_match('/^(\d+)\+$/', $range, $m)) {
        $case_sql[] = "WHEN $field >= {$m[1]} THEN $i";
      }
      // Exact number.
      elseif (is_numeric($range)) {
        $case_sql[] = "WHEN $field = {$range} THEN $i";
      }
    }

    $case_expression = "CASE " . implode(' ', $case_sql) . " ELSE 9999 END";

    // Tell Views how to sort.
    $this->query->addOrderBy(NULL, $case_expression, $this->options['order'], $existing_field_alias);
  }

  /**
   * {@inheritdoc}
   */
  public function postExecute(&$values) {
    $values = SequenceService::integer($values, $this->tableAlias . '_' . $this->realField . '__range', $this->getTotalRanges());
  }

}
