<?php

declare(strict_types=1);

namespace Drupal\flowdrop\Plugin\ExpressionEvaluator;

use Drupal\Core\Plugin\PluginBase;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\flowdrop\Attribute\ExpressionEvaluator;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
use Symfony\Component\ExpressionLanguage\SyntaxError;

/**
 * Expression evaluator using Symfony ExpressionLanguage.
 *
 * Supports computed values, string concatenation, math, comparisons,
 * and ternary expressions. No HTML escaping — designed for data
 * transformation, not template rendering.
 *
 * Expression examples:
 *   - first_name ~ " " ~ last_name
 *   - age >= 18 ? "adult" : "minor"
 *   - price * quantity
 *   - items[0]["name"]
 *
 * @internal
 *   This class is not part of the public API and may change without notice.
 */
#[ExpressionEvaluator(
  id: "expression_language",
  label: new TranslatableMarkup("Expression Language"),
  description: "Evaluate expressions using Symfony ExpressionLanguage (e.g., first_name ~ \" \" ~ last_name)",
)]
class ExpressionLanguageEvaluator extends PluginBase implements ExpressionEvaluatorInterface {

  /**
   * The expression language instance.
   */
  protected ExpressionLanguage $expressionLanguage;

  /**
   * {@inheritdoc}
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->expressionLanguage = new ExpressionLanguage();
  }

  /**
   * {@inheritdoc}
   */
  public function evaluate(string $expression, mixed $context): mixed {
    if ($expression === "") {
      return NULL;
    }

    $variables = is_array($context) ? $context : ["data" => $context];

    try {
      return $this->expressionLanguage->evaluate($expression, $variables);
    }
    catch (SyntaxError $e) {
      throw new \RuntimeException(
        "Expression syntax error: {$e->getMessage()}",
        0,
        $e
      );
    }
    catch (\Throwable $e) {
      throw new \RuntimeException(
        "Expression evaluation error: {$e->getMessage()}",
        0,
        $e
      );
    }
  }

  /**
   * {@inheritdoc}
   */
  public function validate(string $expression): bool {
    if ($expression === "") {
      return TRUE;
    }

    try {
      // Extract all identifier-like tokens and declare them as valid names
      // so that parse() only rejects genuine syntax errors, not unknown
      // variable references.
      preg_match_all('/\b([a-zA-Z_]\w*)\b/', $expression, $matches);
      $names = array_unique($matches[1]);
      $this->expressionLanguage->parse($expression, $names);
      return TRUE;
    }
    catch (SyntaxError) {
      return FALSE;
    }
  }

  /**
   * {@inheritdoc}
   */
  public function getSyntaxHint(): string {
    return 'first_name ~ " " ~ last_name or price * quantity';
  }

}
