<?php

declare(strict_types=1);

namespace Drupal\flowdrop_node_processor\Plugin\FlowDropNodeProcessor;

use Drupal\flowdrop\Attribute\FlowDropNodeProcessor;
use Drupal\flowdrop\DTO\ParameterBagInterface;
use Drupal\flowdrop\DTO\ValidationResult;
use Drupal\flowdrop\Plugin\FlowDropNodeProcessor\AbstractFlowDropNodeProcessor;
use Drupal\flowdrop\Service\JsonPath\JsonPathServiceInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Executor for Data Extractor nodes.
 *
 * This processor takes input and extracts data using either:
 * - JSONPath expressions (RFC 9535) starting with "$"
 * - Simple property paths (dot or bracket notation)
 *
 * JSONPath Examples:
 *   - "$.users[*].name" - Extract all user names
 *   - "$.items[?(@.status == 'active')]" - Filter active items
 *   - "$..email" - Find all email fields recursively
 *   - "$.books[-1]" - Get last book
 *
 * Property Path Examples (legacy):
 *   - "users.0.name" or "[users][0][name]"
 *   - "data.user.email"
 *
 * @internal This class is not part of the public API and may change without notice.
 */
#[FlowDropNodeProcessor(
  id: "data_extractor",
  label: new \Drupal\Core\StringTranslation\TranslatableMarkup("Data Extractor"),
  description: "Extract data from JSON using JSONPath or property path notation.",
  version: "2.0.0"
)]
class DataExtractor extends AbstractFlowDropNodeProcessor {

  /**
   * Constructs a DataExtractor object.
   *
   * @param array<string, mixed> $configuration
   *   The plugin configuration.
   * @param string $plugin_id
   *   The plugin ID.
   * @param mixed $plugin_definition
   *   The plugin definition.
   * @param \Drupal\flowdrop\Service\JsonPath\JsonPathServiceInterface|null $jsonPathService
   *   The JSONPath service.
   */
  public function __construct(
    array $configuration,
    $plugin_id,
    $plugin_definition,
    protected ?JsonPathServiceInterface $jsonPathService = NULL,
  ) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
  }

  /**
   * {@inheritdoc}
   */
  public static function create(
    ContainerInterface $container,
    array $configuration,
    $plugin_id,
    $plugin_definition,
  ): static {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get("flowdrop_runtime.json_path"),
    );
  }

  /**
   * {@inheritdoc}
   */
  public function validateParams(array $params): ValidationResult {
    if (!isset($params["data"])) {
      return ValidationResult::error(
        "data",
        "Data Extractor requires input data",
        "MISSING_DATA"
      );
    }
    return ValidationResult::success();
  }

  /**
   * {@inheritdoc}
   */
  public function process(ParameterBagInterface $params): array {
    $path = $params->getString("path", "");
    $dataInput = $params->get("data");
    $defaultValue = $params->get("default");
    $extractAll = $params->getBool("extract_all", FALSE);

    // Use JsonPathService if available.
    if ($this->jsonPathService !== NULL) {
      if ($extractAll) {
        $extractedData = $this->jsonPathService->extractAll($dataInput, $path);
        $success = !empty($extractedData);
      }
      else {
        $extractedData = $this->jsonPathService->extractFirst($dataInput, $path, $defaultValue);
        $success = $extractedData !== NULL || $defaultValue !== NULL;
      }

      return [
        "data" => $extractedData,
        "path" => $path,
        "success" => $success,
        "is_jsonpath" => $this->jsonPathService->isJsonPath($path),
        "match_count" => $extractAll ? count($extractedData) : ($success ? 1 : 0),
      ];
    }

    // Fallback: No JsonPathService available (should not happen in normal use).
    return [
      "data" => $dataInput,
      "path" => $path,
      "success" => FALSE,
      "is_jsonpath" => FALSE,
      "match_count" => 0,
      "error" => "JSONPath service not available",
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function getParameterSchema(): array {
    return [
      "type" => "object",
      "properties" => [
        "data" => [
          "type" => "mixed",
          "title" => "Input Data",
          "description" => "Input data to extract from (array, object, or JSON string)",
          "required" => TRUE,
        ],
        "path" => [
          "type" => "string",
          "title" => "Path Expression",
          "description" => "JSONPath expression (starting with $) or property path. Examples: $.users[*].name, $.items[?(@.active)], user.email",
          "default" => "",
        ],
        "default" => [
          "type" => "mixed",
          "title" => "Default Value",
          "description" => "Value to return if path doesn't match anything",
          "default" => NULL,
        ],
        "extract_all" => [
          "type" => "boolean",
          "title" => "Extract All Matches",
          "description" => "When true, returns all matching values as an array. When false, returns only the first match.",
          "default" => FALSE,
        ],
      ],
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function getOutputSchema(): array {
    return [
      "type" => "object",
      "properties" => [
        "data" => [
          "type" => "mixed",
          "description" => "The extracted data (single value or array depending on extract_all)",
        ],
        "path" => [
          "type" => "string",
          "description" => "The path expression that was used",
        ],
        "success" => [
          "type" => "boolean",
          "description" => "Whether the extraction found any matches",
        ],
        "is_jsonpath" => [
          "type" => "boolean",
          "description" => "Whether a JSONPath expression was used (vs property path)",
        ],
        "match_count" => [
          "type" => "integer",
          "description" => "Number of values matched by the path",
        ],
      ],
    ];
  }

}
