<?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_node_processor\Service\DataMapperService;
use Drupal\flowdrop_node_processor\Service\JsonSchemaValidator;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Data Shaper node processor.
 *
 * Takes mixed input data and reshapes it into a clean structure defined by:
 * 1. A mapping configuration (source paths → target paths)
 * 2. A JSON Schema (defines expected output structure for validation)
 *
 * Supports:
 * - Simple scalar mappings using Symfony PropertyAccessor syntax
 * - Array iteration with "_source" and "_each" nested configuration
 * - Special values: _NOW_, _LITERAL:value, _NULL_, _EMPTY_ARRAY_,
 *   _EMPTY_OBJECT_
 * - Optional JSON Schema validation (strict or soft mode)
 */
#[FlowDropNodeProcessor(
  id: "data_shaper",
  label: new \Drupal\Core\StringTranslation\TranslatableMarkup("Data Shaper"),
  description: "Reshape mixed input data into a clean structure using field mappings and optional JSON Schema validation",
  version: "1.0.0"
)]
class DataShaper extends AbstractFlowDropNodeProcessor {

  /**
   * Constructs a DataShaper 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_node_processor\Service\DataMapperService $dataMapper
   *   The data mapper service.
   * @param \Drupal\flowdrop_node_processor\Service\JsonSchemaValidator $schemaValidator
   *   The JSON schema validator service.
   */
  public function __construct(
    array $configuration,
    $plugin_id,
    $plugin_definition,
    protected DataMapperService $dataMapper,
    protected JsonSchemaValidator $schemaValidator,
  ) {
    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_node_processor.data_mapper"),
      $container->get("flowdrop_node_processor.json_schema_validator"),
    );
  }

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

  /**
   * {@inheritdoc}
   */
  public function process(ParameterBagInterface $params): array {
    // Get input parameters.
    $sourceData = $params->get("data");
    $mapping = $params->getArray("mapping", []);
    $schema = $params->getArray("schema", []);
    $validationMode = $params->getString("validationMode", "soft");

    // Validate validation mode.
    if (!in_array($validationMode, ["strict", "soft"], TRUE)) {
      $validationMode = "soft";
    }

    // If no mapping provided, pass through the data.
    if (empty($mapping)) {
      return $this->buildResult($sourceData, $schema, $validationMode, 0);
    }

    // Perform the mapping.
    $mapResult = $this->dataMapper->map($sourceData, $mapping);
    $mappedData = $mapResult["data"];
    $mappedFieldsCount = $mapResult["mappedFields"];

    return $this->buildResult($mappedData, $schema, $validationMode, $mappedFieldsCount);
  }

  /**
   * Build the result array with optional schema validation.
   *
   * @param mixed $data
   *   The mapped data.
   * @param array<string, mixed> $schema
   *   The JSON Schema for validation.
   * @param string $validationMode
   *   The validation mode (strict or soft).
   * @param int $mappedFieldsCount
   *   The count of successfully mapped fields.
   *
   * @return array{data: mixed, valid: bool, errors: array<int, array{property: string, message: string}>, mappedFields: int}
   *   The result array.
   */
  protected function buildResult(
    mixed $data,
    array $schema,
    string $validationMode,
    int $mappedFieldsCount,
  ): array {
    // Perform schema validation if schema is provided.
    $validationResult = $this->schemaValidator->validate($data, $schema, $validationMode);

    return [
      "data" => $data,
      "valid" => $validationResult["valid"],
      "errors" => $validationResult["errors"],
      "mappedFields" => $mappedFieldsCount,
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function getParameterSchema(): array {
    return [
      "type" => "object",
      "properties" => [
        "data" => [
          "type" => "mixed",
          "title" => "Input Data",
          "description" => "The source data to reshape (any type: object, array, string, etc.)",
          "required" => TRUE,
        ],
        "mapping" => [
          "type" => "object",
          "format" => "json",
          "title" => "Field Mapping",
          "description" => "Mapping configuration: target path => source path.<br/> Use PropertyAccessor syntax for paths (e.g.,<br/><code> \"[users][0][name]\"). For arrays, use { \"_source\": \"[path]\", \"_each\": { \"field\": \"[subpath]\" } }</code>",
          "default" => new \stdClass(),
        ],
        "schema" => [
          "type" => "object",
          "format" => "json",
          "title" => "Output JSON Schema",
          "description" => "Optional JSON Schema to validate the output structure against",
          "default" => new \stdClass(),
        ],
        "validationMode" => [
          "type" => "string",
          "title" => "Validation Mode",
          "description" => "How to handle validation failures: 'strict' throws error, 'soft' continues with errors in output",
          "default" => "soft",
          "enum" => ["strict", "soft"],
        ],
      ],
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function getOutputSchema(): array {
    return [
      "type" => "object",
      "properties" => [
        "data" => [
          "type" => "mixed",
          "description" => "The reshaped output data conforming to the defined structure",
        ],
        "valid" => [
          "type" => "boolean",
          "description" => "Whether the output passes JSON Schema validation (true if no schema provided)",
        ],
        "errors" => [
          "type" => "array",
          "description" => "Array of validation errors (empty if valid or no schema)",
          "items" => [
            "type" => "object",
            "properties" => [
              "property" => [
                "type" => "string",
                "description" => "The property path that failed validation",
              ],
              "message" => [
                "type" => "string",
                "description" => "The validation error message",
              ],
            ],
          ],
        ],
        "mappedFields" => [
          "type" => "integer",
          "description" => "Count of successfully mapped fields",
        ],
      ],
    ];
  }

}
