<?php

declare(strict_types=1);

namespace Drupal\flowdrop_interrupt\Plugin\FlowDropNodeProcessor;

use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\flowdrop\Attribute\FlowDropNodeProcessor;
use Drupal\flowdrop\Constants\ReservedName;
use Drupal\flowdrop\DTO\ExecutionContextDTO;
use Drupal\flowdrop\DTO\ParameterBagInterface;
use Drupal\flowdrop\DTO\ValidationResult;
use Drupal\flowdrop\Plugin\FlowDropNodeProcessor\AbstractFlowDropNodeProcessor;
use Drupal\flowdrop\Plugin\FlowDropNodeProcessor\BranchingNodeProcessorInterface;
use Drupal\flowdrop\Plugin\FlowDropNodeProcessor\ExecutionContextAwareInterface;
use Drupal\flowdrop\Plugin\FlowDropNodeProcessor\ResumableNodeProcessorInterface;
use Drupal\flowdrop_interrupt\Message\HitlInterruptMessage;
use Drupal\flowdrop_interrupt\Enum\InterruptType;
use Drupal\flowdrop_interrupt\Exception\InterruptRequiredException;
use Drupal\flowdrop_orchestration\DTO\ResolvedInterruptInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Confirmation node processor for human-in-loop workflows.
 *
 * Pauses workflow execution and requests a Yes/No confirmation from the user.
 * On first entry, throws an InterruptRequiredException carrying a
 * HitlInterruptMessage descriptor — the orchestrator stamps execution
 * identity and persists the entity.
 *
 * Config:
 * - message: The confirmation message to display.
 * - confirm_label: Label for the confirm button (default: "Yes").
 * - cancel_label: Label for the cancel button (default: "No").
 *
 * Outputs:
 * - confirmed: Boolean indicating if the user confirmed.
 * - response_time: Timestamp when the response was received.
 * - user_id: ID of the user who responded (if authenticated).
 * - active_branches: "true"/"false" gateway signal for flow control.
 *
 * Dual-mode: this node is both a data node (the `confirmed` value can feed a
 * downstream node) and a gateway (its true/false branches can route flow
 * directly). It implements BranchingNodeProcessorInterface so the editor
 * surfaces it as a gateway; branch routing itself is driven by the
 * `active_branches` output, not the marker.
 *
 * @internal This class is not part of the public API and may change without notice.
 */
#[FlowDropNodeProcessor(
  id: "confirmation",
  label: new TranslatableMarkup("Confirmation"),
  description: "Pauses workflow for user confirmation (Yes/No)",
  version: "1.0.0"
)]
class ConfirmationNode extends AbstractFlowDropNodeProcessor implements ExecutionContextAwareInterface, ResumableNodeProcessorInterface, BranchingNodeProcessorInterface {

  /**
   * The execution context.
   */
  protected ?ExecutionContextDTO $executionContext = NULL;

  /**
   * {@inheritdoc}
   */
  public static function create(
    ContainerInterface $container,
    array $configuration,
    $plugin_id,
    $plugin_definition,
  ): static {
    return new static($configuration, $plugin_id, $plugin_definition);
  }

  /**
   * {@inheritdoc}
   */
  public function setExecutionContext(ExecutionContextDTO $context): void {
    $this->executionContext = $context;
  }

  /**
   * {@inheritdoc}
   */
  public function validateParams(array $params): ValidationResult {
    if (empty($params["message"])) {
      return ValidationResult::error("message", "Confirmation message is required.", "REQUIRED");
    }

    return ValidationResult::success();
  }

  /**
   * {@inheritdoc}
   *
   * First-time entry only — the orchestrator routes resume invocations to
   * {@see self::resume()} via the ResumableNodeProcessorInterface contract.
   */
  public function process(ParameterBagInterface $params): array {
    $message = $params->getString("message", "Do you want to continue?");
    $confirmLabel = $params->getString("confirm_label", "Yes");
    $cancelLabel = $params->getString("cancel_label", "No");

    throw new InterruptRequiredException(new HitlInterruptMessage(
      type: InterruptType::SchemaForm,
      message: $message,
      schema: [
        'type' => 'boolean',
        'presentation' => 'confirmation',
        'confirmLabel' => $confirmLabel,
        'declineLabel' => $cancelLabel,
      ],
    ), [], "Workflow paused for user confirmation");
  }

  /**
   * {@inheritdoc}
   *
   * Builds outputs from a resolved interrupt. Cancelled/expired interrupts
   * resolve to `confirmed = false` so downstream gateways take the false
   * branch — matching the legacy process() fallback behavior.
   */
  public function resume(
    ResolvedInterruptInterface $interrupt,
    ParameterBagInterface $params,
  ): array {
    $responseData = $interrupt->getResponseData();

    $confirmed = FALSE;
    if (is_bool($responseData)) {
      $confirmed = $responseData;
    }
    elseif (is_string($responseData)) {
      $confirmed = strtolower($responseData) === "true" || $responseData === "1";
    }
    elseif (is_array($responseData) && isset($responseData["value"])) {
      $value = $responseData["value"];
      $confirmed = $value === TRUE || $value === "true" || $value === "1";
    }

    return [
      "confirmed" => $confirmed,
      "response_time" => $interrupt->getResolvedAt() ?? time(),
      "user_id" => $interrupt->getResolvedBy(),
      "interrupt_id" => $interrupt->getInterruptId(),
      ReservedName::ACTIVE_BRANCHES => $confirmed ? "true" : "false",
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function getParameterSchema(): array {
    return [
      "type" => "object",
      "properties" => [
        "message" => [
          "type" => "string",
          "title" => "Confirmation Message",
          "description" => "The message to display to the user",
          "default" => "Do you want to continue?",
          "format" => "multiline",
        ],
        "confirm_label" => [
          "type" => "string",
          "title" => "Confirm Button Label",
          "description" => "Label for the confirmation button",
          "default" => "Yes",
        ],
        "cancel_label" => [
          "type" => "string",
          "title" => "Cancel Button Label",
          "description" => "Label for the cancel button",
          "default" => "No",
        ],
        "__interrupt_id__" => [
          "type" => "string",
          "description" => "Internal: Interrupt ID for resume operations",
          "connectable" => FALSE,
          "configurable" => FALSE,
        ],
        ReservedName::BRANCHES => [
          "type" => "array",
          "description" => "The available branches for gateway visual mode",
          "readOnly" => TRUE,
          "default" => [
            [
              "name" => "True",
              "value" => TRUE,
            ],
            [
              "name" => "False",
              "value" => FALSE,
            ],
          ],
          "items" => [
            "type" => "object",
            "properties" => [
              "name" => [
                "type" => "string",
                "description" => "The name of the branch",
              ],
              "value" => [
                "type" => "boolean",
                "description" => "The value of the branch",
              ],
            ],
          ],
        ],
      ],
      "required" => ["message"],
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function getOutputSchema(): array {
    return [
      "type" => "object",
      "properties" => [
        "confirmed" => [
          "type" => "boolean",
          "description" => "Whether the user confirmed (true) or cancelled (false)",
        ],
        "response_time" => [
          "type" => "integer",
          "description" => "Unix timestamp when the user responded",
        ],
        "user_id" => [
          "type" => "integer",
          "description" => "ID of the user who responded (null if anonymous)",
        ],
        "interrupt_id" => [
          "type" => "string",
          "description" => "UUID of the interrupt that was resolved",
        ],
        ReservedName::ACTIVE_BRANCHES => [
          "type" => "string",
          "description" => "Gateway output: 'true' or 'false' for flow control in gateway mode",
        ],
      ],
    ];
  }

}
