<?php

declare(strict_types=1);

namespace Drupal\flowdrop_interrupt\Plugin\FlowDropNodeProcessor;

use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\flowdrop\Attribute\FlowDropNodeProcessor;
use Drupal\flowdrop\DTO\ParameterBagInterface;
use Drupal\flowdrop\DTO\ValidationResult;

/**
 * Delay node processor — pauses the workflow for a relative duration.
 *
 * Persists a Timer interrupt with `scheduled_at = now + value * unit`; the
 * cron sweep resolves it once due and the pipeline resumes. Operators can
 * skip the remaining wait from the interrupt inbox or the /resume API.
 *
 * Resume precision is cron-bound: a "5 minute" delay resumes on the first
 * cron run at least 5 minutes after the pause.
 *
 * Config:
 * - value: The amount to wait.
 * - unit: seconds | minutes | hours | days.
 *
 * Outputs:
 * - scheduled_at / resumed_at / drift_seconds: timing audit trail.
 * - resolved_by_timer: FALSE when a user skipped the wait.
 * - user_id: the skipping user, if any.
 *
 * @internal This class is not part of the public API and may change without notice.
 */
#[FlowDropNodeProcessor(
  id: "delay",
  label: new TranslatableMarkup("Delay"),
  description: "Pauses the workflow for a set duration, resuming automatically",
  version: "1.0.0"
)]
class DelayNode extends AbstractTimerNode {

  /**
   * Seconds per supported unit.
   */
  protected const UNIT_FACTORS = [
    'seconds' => 1,
    'minutes' => 60,
    'hours' => 3600,
    'days' => 86400,
  ];

  /**
   * {@inheritdoc}
   */
  public function validateParams(array $params): ValidationResult {
    $value = $params['value'] ?? NULL;
    if ($value === NULL || $value === '' || !is_numeric($value)) {
      return ValidationResult::error("value", "Delay value must be a number.", "INVALID_TYPE");
    }
    if ((float) $value < 0) {
      return ValidationResult::error("value", "Delay value cannot be negative.", "OUT_OF_RANGE");
    }

    $unit = $params['unit'] ?? 'seconds';
    if (!is_string($unit) || !isset(self::UNIT_FACTORS[$unit])) {
      return ValidationResult::error("unit", "Unit must be one of: " . implode(', ', array_keys(self::UNIT_FACTORS)) . ".", "INVALID_VALUE");
    }

    $maxDelay = $this->getMaxDelay();
    $seconds = (int) ((float) $value * self::UNIT_FACTORS[$unit]);
    if ($maxDelay > 0 && $seconds > $maxDelay) {
      return ValidationResult::error("value", "Delay exceeds the configured maximum of {$maxDelay} seconds.", "OUT_OF_RANGE");
    }

    return ValidationResult::success();
  }

  /**
   * {@inheritdoc}
   */
  protected function computeFireAt(ParameterBagInterface $params): int {
    $value = $params->getFloat('value', 0.0);
    $unit = $params->getString('unit', 'seconds');
    $factor = self::UNIT_FACTORS[$unit] ?? 1;
    return time() + (int) round($value * $factor);
  }

  /**
   * {@inheritdoc}
   */
  public function getParameterSchema(): array {
    return [
      "type" => "object",
      "properties" => [
        "value" => [
          "type" => "number",
          "title" => "Wait for",
          "description" => "How long to pause the workflow",
          "minimum" => 0,
          "default" => 5,
        ],
        "unit" => [
          "type" => "string",
          "title" => "Unit",
          "description" => "The unit for the wait duration",
          "enum" => array_keys(self::UNIT_FACTORS),
          "default" => "minutes",
        ],
        "__interrupt_id__" => $this->interruptIdParameterSchema(),
      ],
      "required" => ["value"],
    ];
  }

}
