<?php

declare(strict_types=1);

namespace Drupal\native_observability\Form;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\native_observability\Service\RequestLogExclusionRepository;
use Drupal\native_observability\Service\RequestLogExclusionRuleNormalizer;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Add/edit form for request-log exclusion rules.
 */
final class RequestLogExclusionRuleForm extends FormBase {

  /**
   * Current rule ID when editing.
   */
  private int $ruleId = 0;

  /**
   * Redirect destination after save.
   */
  private ?string $destination = NULL;

  public function __construct(
    private readonly RequestLogExclusionRepository $repository,
    private readonly RequestLogExclusionRuleNormalizer $normalizer,
  ) {
  }

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

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

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state, ?int $id = NULL): array {
    $this->ruleId = (int) ($id ?? 0);
    $rule = $this->ruleId > 0 ? ($this->repository->load($this->ruleId) ?? []) : [];
    $request = $this->getRequest();
    $prefillMethod = strtoupper((string) $request->query->get('method', 'ANY'));
    $prefillPathMatchType = (string) $request->query->get('path_match_type', 'exact');
    $prefillPathPattern = (string) $request->query->get('path_pattern', '');
    $prefillQueryConditions = (string) $request->query->get('query_conditions', '');
    $prefillLabel = (string) $request->query->get('label', '');
    $reactivate = (bool) $request->query->get('reactivate', FALSE);
    $this->destination = (string) $request->query->get('destination', '');

    if ($this->ruleId === 0 && $prefillPathPattern !== '') {
      $canonicalConditions = $prefillQueryConditions !== ''
        ? $prefillQueryConditions
        : '[]';
      $matchedRule = $this->repository->findByCanonicalDefinition(
        $prefillMethod !== '' ? $prefillMethod : 'ANY',
        $prefillPathMatchType,
        $prefillPathPattern,
        $canonicalConditions,
      );

      if ($matchedRule) {
        $rule = $matchedRule;
        $this->ruleId = (int) ($matchedRule['id'] ?? 0);
      }
    }

    $form['label'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Label'),
      '#required' => TRUE,
      '#default_value' => (string) ($rule['label'] ?? $prefillLabel),
    ];

    $form['enabled'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Enabled'),
      '#default_value' => $reactivate
        ? FALSE
        : (!array_key_exists('enabled', $rule) || !empty($rule['enabled'])),
    ];

    $form['method'] = [
      '#type' => 'select',
      '#title' => $this->t('HTTP method'),
      '#default_value' => (string) ($rule['method'] ?? $prefillMethod ?: 'ANY'),
      '#options' => $this->buildHttpMethodOptions(),
    ];

    $form['path_match_type'] = [
      '#type' => 'select',
      '#title' => $this->t('Path match type'),
      '#default_value' => (string) ($rule['path_match_type'] ?? $prefillPathMatchType),
      '#options' => [
        'exact' => $this->t('Exact'),
        'prefix' => $this->t('Prefix'),
        'regex' => $this->t('Regex'),
      ],
    ];

    $form['path_pattern'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Path pattern'),
      '#required' => TRUE,
      '#default_value' => (string) ($rule['path_pattern'] ?? $prefillPathPattern),
      '#description' => $this->t('Examples: <code>/foo/bar</code>, <code>/reports/</code>, or a regex such as <code>@^/reports/.*$@</code>.'),
    ];

    $form['query_conditions'] = [
      '#type' => 'textarea',
      '#title' => $this->t('Query conditions'),
      '#default_value' => $this->buildConditionTextareaValue((string) ($rule['query_conditions'] ?? $prefillQueryConditions)),
      '#description' => $this->t('One condition per line using the format <code>key|operator|value</code>. Supported operators: <code>exists</code>, <code>missing</code>, <code>equals</code>, <code>regex</code>, <code>gte</code>, <code>lte</code>, <code>between</code>. Example: <code>from|between|2026-01-01|2026-01-31</code>.'),
    ];

    $form['notes'] = [
      '#type' => 'textarea',
      '#title' => $this->t('Notes'),
      '#default_value' => (string) ($rule['notes'] ?? ''),
    ];

    $form['actions'] = ['#type' => 'actions'];
    $form['actions']['submit'] = [
      '#type' => 'submit',
      '#value' => $reactivate ? $this->t('Re-enable logs') : ($this->ruleId > 0 ? $this->t('Save rule') : $this->t('Add rule')),
      '#button_type' => 'primary',
    ];
    $form['actions']['cancel'] = [
      '#type' => 'link',
      '#title' => $this->t('Cancel'),
      '#url' => $this->destination !== NULL && $this->destination !== ''
        ? Url::fromUserInput($this->destination)
        : Url::fromRoute('native_observability.request_log_exclusion_list'),
      '#attributes' => ['class' => ['button']],
    ];

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function validateForm(array &$form, FormStateInterface $form_state): void {
    $pathMatchType = (string) $form_state->getValue('path_match_type');
    $pathPattern = (string) $form_state->getValue('path_pattern');
    $conditions = $this->normalizer->parseConditionDefinition((string) $form_state->getValue('query_conditions'));

    if ($pathMatchType === 'regex' && @preg_match($pathPattern, '') === FALSE) {
      $form_state->setErrorByName('path_pattern', $this->t('The regex path pattern is invalid.'));
    }

    foreach ($conditions as $index => $condition) {
      $operator = $condition['operator'] ?? '';
      if (!in_array($operator, ['exists', 'missing', 'equals', 'regex', 'gte', 'lte', 'between'], TRUE)) {
        $form_state->setErrorByName('query_conditions', $this->t('Unsupported operator on line @line.', ['@line' => $index + 1]));
      }

      if ($operator === 'regex' && !empty($condition['value']) && @preg_match((string) $condition['value'], '') === FALSE) {
        $form_state->setErrorByName('query_conditions', $this->t('Invalid regex condition on line @line.', ['@line' => $index + 1]));
      }
    }

    parent::validateForm($form, $form_state);
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state): void {
    $conditions = $this->normalizer->parseConditionDefinition((string) $form_state->getValue('query_conditions'));
    $this->repository->save([
      'id' => $this->ruleId,
      'label' => (string) $form_state->getValue('label'),
      'enabled' => (bool) $form_state->getValue('enabled'),
      'method' => (string) $form_state->getValue('method'),
      'path_match_type' => (string) $form_state->getValue('path_match_type'),
      'path_pattern' => (string) $form_state->getValue('path_pattern'),
      'query_conditions' => $this->normalizer->encodeConditions($conditions),
      'notes' => (string) $form_state->getValue('notes'),
    ]);

    $this->messenger()->addStatus($this->t('The exclusion rule has been saved.'));
    if ($this->destination !== NULL && $this->destination !== '') {
      $form_state->setRedirectUrl(Url::fromUserInput($this->destination));
      return;
    }

    $form_state->setRedirect('native_observability.request_log_exclusion_list');
  }

  /**
   * Converts stored JSON conditions to textarea lines.
   */
  private function buildConditionTextareaValue(string $encoded): string {
    $lines = [];
    foreach ($this->normalizer->decodeConditions($encoded) as $condition) {
      $parts = [
        (string) ($condition['key'] ?? ''),
        (string) ($condition['operator'] ?? ''),
      ];

      if (($condition['value'] ?? '') !== '') {
        $parts[] = (string) $condition['value'];
      }
      if (($condition['value_to'] ?? '') !== '') {
        $parts[] = (string) $condition['value_to'];
      }

      $lines[] = implode('|', $parts);
    }

    return implode("\n", $lines);
  }

  /**
   * Builds the HTTP method select options.
   *
   * HTTP verbs are canonical protocol labels and are intentionally left
   * untranslated.
   *
   * @return array<string, string>
   *   Method options keyed by machine value.
   */
  private function buildHttpMethodOptions(): array {
    return [
      'ANY' => (string) $this->t('Any'),
      'GET' => 'GET',
      'POST' => 'POST',
      'PUT' => 'PUT',
      'PATCH' => 'PATCH',
      'DELETE' => 'DELETE',
      'HEAD' => 'HEAD',
      'OPTIONS' => 'OPTIONS',
    ];
  }

}
