<?php

declare(strict_types=1);

namespace Drupal\flowdrop_memory\Plugin\FlowDropNodeProcessor;

use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\flowdrop\Attribute\FlowDropNodeProcessor;
use Drupal\flowdrop\DTO\ParameterBagInterface;
use Drupal\flowdrop\DTO\ValidationError;
use Drupal\flowdrop\DTO\ValidationResult;
use Drupal\flowdrop\Plugin\FlowDropNodeProcessor\AbstractFlowDropNodeProcessor;
use Drupal\flowdrop\Plugin\FlowDropNodeProcessor\HasSideEffectsInterface;
use Drupal\flowdrop_memory\Service\MemoryManager;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Explicit memory write node processor.
 *
 * Writes a value to memory using fully explicit scope and scope_id parameters.
 * No auto-population from execution context.
 *
 * Use this processor when you need to:
 * - Write memory accessible to a different workflow or pipeline
 * - Use a custom scoping strategy (e.g., keyed by user ID, node ID)
 * - Write memory without being inside a standard FlowDrop execution context
 *
 * SECURITY NOTE: This processor accepts an arbitrary scope_id with no
 * ownership validation. It is an administrator/developer-tier tool and must
 * only be used in workflows whose execution is restricted to trusted roles.
 * Do not expose it in workflows accessible to untrusted users, as it could
 * allow overwriting memory belonging to other users or executions.
 *
 * @see \Drupal\flowdrop_memory\Plugin\FlowDropNodeProcessor\MemoryWrite
 */
#[FlowDropNodeProcessor(
  id: 'memory_write_raw',
  label: new TranslatableMarkup('Memory Write (Explicit)'),
  description: 'Write a value to memory with fully explicit scope and scope ID. No auto-population from execution context.',
  version: '1.0.0',
)]
class MemoryWriteRaw extends AbstractFlowDropNodeProcessor implements HasSideEffectsInterface {

  /**
   * {@inheritdoc}
   */
  public function __construct(
    array $configuration,
    string $plugin_id,
    mixed $plugin_definition,
    private readonly MemoryManager $memoryManager,
  ) {
    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_memory.manager'),
    );
  }

  /**
   * {@inheritdoc}
   */
  public function validateParams(array $params): ValidationResult {
    $errors = [];
    if (empty($params['scope'])) {
      $errors[] = new ValidationError('scope', 'Memory scope is required.');
    }
    if (empty($params['key'])) {
      $errors[] = new ValidationError('key', 'Memory key is required.');
    }
    if (!array_key_exists('value', $params)) {
      $errors[] = new ValidationError('value', 'Memory value is required.');
    }
    return empty($errors) ? ValidationResult::success() : ValidationResult::failure($errors);
  }

  /**
   * {@inheritdoc}
   */
  public function process(ParameterBagInterface $params): array {
    $scope = $params->getString('scope');
    $scopeId = $params->getString('scope_id', '');
    $key = $params->getString('key');
    $value = $params->get('value');
    $ttl = $params->getInt('ttl', 0);
    $backend = $params->getString('backend', 'static');

    $this->memoryManager->set($scope, $scopeId, $key, $value, $ttl > 0 ? $ttl : NULL, $backend);

    return [
      'success' => TRUE,
      'key'     => $key,
      'scope'   => $scope,
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function getParameterSchema(): array {
    return [
      'type' => 'object',
      'properties' => [
        'scope' => [
          'type'        => 'string',
          'title'       => 'Scope',
          'description' => 'The memory scope (any string).',
        ],
        'scope_id' => [
          'type'        => 'string',
          'title'       => 'Scope ID',
          'description' => 'The scope identifier. Leave blank if not applicable.',
          'default'     => '',
        ],
        'key' => [
          'type'        => 'string',
          'title'       => 'Key',
          'description' => 'The memory key to write.',
        ],
        'value' => [
          'type'        => 'mixed',
          'title'       => 'Value',
          'description' => 'The value to store.',
        ],
        'ttl' => [
          'type'        => 'integer',
          'title'       => 'TTL (seconds)',
          'description' => 'Time-to-live in seconds. 0 means no expiry. Ignored by backends that do not support TTL.',
          'default'     => 0,
          'minimum'     => 0,
        ],
        'backend' => [
          'type'        => 'string',
          'title'       => 'Memory Backend',
          'description' => 'The storage backend to use.',
          'enum'        => ['static', 'cached', 'entity'],
          'default'     => 'static',
        ],
      ],
      'required' => ['scope', 'key', 'value', 'backend'],
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function getOutputSchema(): array {
    return [
      'type' => 'object',
      'properties' => [
        'success' => [
          'type'        => 'boolean',
          'description' => 'Always TRUE when the write succeeds.',
        ],
        'key' => [
          'type'        => 'string',
          'description' => 'The key that was written.',
        ],
        'scope' => [
          'type'        => 'string',
          'description' => 'The scope used for the write.',
        ],
      ],
    ];
  }

}
