<?php

declare(strict_types=1);

namespace Drupal\flowdrop_node_processor\Plugin\FlowDropNodeProcessor;

use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\flowdrop\Attribute\FlowDropNodeProcessor;
use Drupal\flowdrop\Constants\ReservedName;
use Drupal\flowdrop\DTO\ParameterBagInterface;
use Drupal\flowdrop\Plugin\FlowDropNodeProcessor\AbstractFlowDropNodeProcessor;
use Drupal\flowdrop\Plugin\FlowDropNodeProcessor\ToolPassthroughInterface;

/**
 * Shares a set of tools with multiple consumers.
 *
 * Wire any number of tool nodes into the ToolBox's tool input, then wire its
 * tool output into one or more tool-consuming nodes (agents/LLMs). Each
 * consumer receives the box's whole tool set as if every tool were wired to it
 * directly — N tools shared by M consumers take N+M edges instead of N×M.
 * ToolBoxes can be nested (a box wired into another box contributes its
 * flattened contents).
 *
 * This node never executes: it is tool-only by classification (all outgoing
 * edges are tool connections), so the compiler excludes it from the execution
 * graph, and the runtime flattens through it when projecting a consumer's
 * tools ({@see ToolPassthroughInterface}). Authorization is unchanged — a
 * consumer's allow-list is the flattened set of tool nodes the author wired
 * into the box.
 *
 * @see \Drupal\flowdrop\Plugin\FlowDropNodeProcessor\ToolPassthroughInterface
 */
#[FlowDropNodeProcessor(
  id: "toolbox",
  label: new TranslatableMarkup("Tool Box"),
  description: "Bundle tools into a shared set that multiple agents can consume",
  version: "1.0.0"
)]
class ToolBox extends AbstractFlowDropNodeProcessor implements ToolPassthroughInterface {

  /**
   * {@inheritdoc}
   */
  public function process(ParameterBagInterface $params): array {
    // A ToolBox only forwards tool wiring; it is excluded from the execution
    // graph as tool-only (all outgoing edges are tool connections). Reaching
    // process() means the node was scheduled anyway — i.e. a non-tool outgoing
    // edge was wired, which defeats the exclusion. Fail loudly rather than
    // silently no-op.
    throw new \LogicException(
      "ToolBox nodes are never executed; they only forward tool wiring. " .
      "Check that every outgoing edge of this node targets a tool input port.",
    );
  }

  /**
   * {@inheritdoc}
   */
  public function getParameterSchema(): array {
    return [
      "type" => "object",
      "properties" => [
        ReservedName::PORT_TOOL => [
          "type" => "tool",
          "description" => "Tools bundled into this box.",
        ],
      ],
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function getOutputSchema(): array {
    return [
      "type" => "object",
      "properties" => [
        ReservedName::PORT_TOOL => [
          "type" => "tool",
          "description" => "The bundled tools, offered as a set to consumers.",
        ],
      ],
    ];
  }

}
