<?php

declare(strict_types=1);

namespace Drupal\flowdrop\DTO\NodeMetadata;

use Drupal\flowdrop\DTO\ConfigEdit\ConfigEdit;

/**
 * Fluent builder for constructing NodeMetadata instances.
 *
 * This builder simplifies the creation of NodeMetadata objects,
 * especially in contexts where properties are set incrementally
 * (like in NodesController::buildNodeData()).
 *
 * Example usage:
 * @code
 * $metadata = NodeMetadataBuilder::create()
 *     ->setId("my_node")
 *     ->setName("My Node")
 *     ->setType("basic")
 *     ->setCategory("processing")
 *     ->build();
 * @endcode
 *
 * @see \Drupal\flowdrop\DTO\NodeMetadata\NodeMetadata
 *
 * @api
 */
final class NodeMetadataBuilder {

  /**
   * The node type ID.
   *
   * @var string
   */
  private string $id = "";

  /**
   * The human-readable node name.
   *
   * @var string
   */
  private string $name = "";

  /**
   * The visual type.
   *
   * @var string
   */
  private string $type = "basic";

  /**
   * Supported visual types.
   *
   * @var array<string>
   */
  private array $supportedTypes = [];

  /**
   * The node description.
   *
   * @var string
   */
  private string $description = "";

  /**
   * The category (plural form).
   *
   * @var string
   */
  private string $category = "";

  /**
   * The icon identifier.
   *
   * @var string
   */
  private string $icon = "";

  /**
   * The plugin version.
   *
   * @var string
   */
  private string $pluginVersion = "";

  /**
   * Whether the node is enabled.
   *
   * @var bool
   */
  private bool $enabled = TRUE;

  /**
   * Associated tags.
   *
   * @var array<string>
   */
  private array $tags = [];

  /**
   * The executor plugin ID.
   *
   * @var string
   */
  private string $executorPlugin = "";

  /**
   * Input ports.
   *
   * @var array<NodePort>
   */
  private array $inputs = [];

  /**
   * Output ports.
   *
   * @var array<NodePort>
   */
  private array $outputs = [];

  /**
   * Default configuration values.
   *
   * @var array<string, mixed>
   */
  private array $config = [];

  /**
   * Configuration JSON Schema.
   *
   * @var array<string, mixed>
   */
  private array $configSchema = [];

  /**
   * Configuration edit options.
   *
   * @var \Drupal\flowdrop\DTO\ConfigEdit\ConfigEdit|null
   */
  private ?ConfigEdit $configEdit = NULL;

  /**
   * Optional config form uiSchema (layout/grouping), or NULL for flat render.
   *
   * @var array<string, mixed>|null
   */
  private ?array $uiSchema = NULL;

  /**
   * Private constructor - use create() static method.
   */
  private function __construct() {}

  /**
   * Creates a new builder instance.
   *
   * @return self
   *   A new builder instance.
   */
  public static function create(): self {
    return new self();
  }

  /**
   * Sets the node type ID.
   *
   * @param string $id
   *   The node type ID.
   *
   * @return self
   *   The builder for method chaining.
   */
  public function setId(string $id): self {
    $this->id = $id;
    return $this;
  }

  /**
   * Sets the human-readable name.
   *
   * @param string $name
   *   The node name.
   *
   * @return self
   *   The builder for method chaining.
   */
  public function setName(string $name): self {
    $this->name = $name;
    return $this;
  }

  /**
   * Sets the visual type.
   *
   * @param string $type
   *   The visual type (e.g., "basic", "terminal").
   *
   * @return self
   *   The builder for method chaining.
   */
  public function setType(string $type): self {
    $this->type = $type;
    return $this;
  }

  /**
   * Sets the supported visual types.
   *
   * @param array<string> $supportedTypes
   *   List of supported visual types.
   *
   * @return self
   *   The builder for method chaining.
   */
  public function setSupportedTypes(array $supportedTypes): self {
    $this->supportedTypes = $supportedTypes;
    return $this;
  }

  /**
   * Sets the node description.
   *
   * @param string $description
   *   The description.
   *
   * @return self
   *   The builder for method chaining.
   */
  public function setDescription(string $description): self {
    $this->description = $description;
    return $this;
  }

  /**
   * Sets the category.
   *
   * @param string $category
   *   The category in plural form (e.g., "triggers", "processing").
   *
   * @return self
   *   The builder for method chaining.
   */
  public function setCategory(string $category): self {
    $this->category = $category;
    return $this;
  }

  /**
   * Sets the icon identifier.
   *
   * @param string $icon
   *   The icon (e.g., "mdi:play").
   *
   * @return self
   *   The builder for method chaining.
   */
  public function setIcon(string $icon): self {
    $this->icon = $icon;
    return $this;
  }

  /**
   * Sets the plugin version.
   *
   * @param string $pluginVersion
   *   The version string.
   *
   * @return self
   *   The builder for method chaining.
   */
  public function setPluginVersion(string $pluginVersion): self {
    $this->pluginVersion = $pluginVersion;
    return $this;
  }

  /**
   * Sets whether the node is enabled.
   *
   * @param bool $enabled
   *   TRUE if enabled.
   *
   * @return self
   *   The builder for method chaining.
   */
  public function setEnabled(bool $enabled): self {
    $this->enabled = $enabled;
    return $this;
  }

  /**
   * Sets the associated tags.
   *
   * @param array<string> $tags
   *   The tags array.
   *
   * @return self
   *   The builder for method chaining.
   */
  public function setTags(array $tags): self {
    $this->tags = $tags;
    return $this;
  }

  /**
   * Sets the executor plugin ID.
   *
   * @param string $executorPlugin
   *   The plugin ID.
   *
   * @return self
   *   The builder for method chaining.
   */
  public function setExecutorPlugin(string $executorPlugin): self {
    $this->executorPlugin = $executorPlugin;
    return $this;
  }

  /**
   * Sets the input ports.
   *
   * @param array<NodePort> $inputs
   *   Array of input ports.
   *
   * @return self
   *   The builder for method chaining.
   */
  public function setInputs(array $inputs): self {
    $this->inputs = $inputs;
    return $this;
  }

  /**
   * Adds a single input port.
   *
   * @param \Drupal\flowdrop\DTO\NodeMetadata\NodePort $input
   *   The input port to add.
   *
   * @return self
   *   The builder for method chaining.
   */
  public function addInput(NodePort $input): self {
    $this->inputs[] = $input;
    return $this;
  }

  /**
   * Sets the output ports.
   *
   * @param array<NodePort> $outputs
   *   Array of output ports.
   *
   * @return self
   *   The builder for method chaining.
   */
  public function setOutputs(array $outputs): self {
    $this->outputs = $outputs;
    return $this;
  }

  /**
   * Adds a single output port.
   *
   * @param \Drupal\flowdrop\DTO\NodeMetadata\NodePort $output
   *   The output port to add.
   *
   * @return self
   *   The builder for method chaining.
   */
  public function addOutput(NodePort $output): self {
    $this->outputs[] = $output;
    return $this;
  }

  /**
   * Sets the default configuration values.
   *
   * @param array<string, mixed> $config
   *   The config defaults.
   *
   * @return self
   *   The builder for method chaining.
   */
  public function setConfig(array $config): self {
    $this->config = $config;
    return $this;
  }

  /**
   * Sets the configuration JSON Schema.
   *
   * @param array<string, mixed> $configSchema
   *   The config schema.
   *
   * @return self
   *   The builder for method chaining.
   */
  public function setConfigSchema(array $configSchema): self {
    $this->configSchema = $configSchema;
    return $this;
  }

  /**
   * Sets the configuration edit options.
   *
   * @param \Drupal\flowdrop\DTO\ConfigEdit\ConfigEdit|null $configEdit
   *   The config edit options or NULL.
   *
   * @return self
   *   The builder for method chaining.
   */
  public function setConfigEdit(?ConfigEdit $configEdit): self {
    $this->configEdit = $configEdit;
    return $this;
  }

  /**
   * Sets the config form uiSchema.
   *
   * @param array<string, mixed>|null $uiSchema
   *   The uiSchema (layout/grouping) or NULL for flat field rendering.
   *
   * @return self
   *   The builder for method chaining.
   */
  public function setUiSchema(?array $uiSchema): self {
    $this->uiSchema = $uiSchema;
    return $this;
  }

  /**
   * Builds the NodeMetadata instance.
   *
   * @return \Drupal\flowdrop\DTO\NodeMetadata\NodeMetadata
   *   The constructed NodeMetadata instance.
   */
  public function build(): NodeMetadata {
    return new NodeMetadata(
      id: $this->id,
      name: $this->name,
      type: $this->type,
      supportedTypes: $this->supportedTypes,
      description: $this->description,
      category: $this->category,
      icon: $this->icon,
      pluginVersion: $this->pluginVersion,
      enabled: $this->enabled,
      tags: $this->tags,
      executorPlugin: $this->executorPlugin,
      inputs: $this->inputs,
      outputs: $this->outputs,
      config: $this->config,
      configSchema: $this->configSchema,
      configEdit: $this->configEdit,
      uiSchema: $this->uiSchema,
    );
  }

}
