<?php

declare(strict_types=1);

namespace Drupal\flowdrop_node_type\Form;

use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;

/**
 * Configuration form for managing visual types for FlowDrop nodes.
 *
 * Site builders can define the available visual types that determine
 * how nodes appear in the workflow editor.
 */
final class VisualTypesSettingsForm extends ConfigFormBase {

  /**
   * {@inheritdoc}
   */
  public function getFormId(): string {
    return "flowdrop_node_type_visual_types_settings";
  }

  /**
   * {@inheritdoc}
   */
  protected function getEditableConfigNames(): array {
    return ["flowdrop_node_type.settings"];
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state): array {
    $config = $this->config("flowdrop_node_type.settings");
    $visualTypes = $config->get("visual_types") ?? [];

    $form["description"] = [
      "#type" => "markup",
      "#markup" => "<p>" . $this->t("Define the available visual types for FlowDrop nodes. These types determine how nodes appear in the workflow editor. Site builders can select from these types when configuring node types.") . "</p>",
    ];

    $form["visual_types"] = [
      "#type" => "fieldset",
      "#title" => $this->t("Visual Types"),
      "#tree" => TRUE,
      "#prefix" => '<div id="visual-types-wrapper">',
      "#suffix" => "</div>",
    ];

    // Initialize items storage on first load.
    if (!$form_state->has("visual_types_items")) {
      $typesArray = [];
      foreach ($visualTypes as $key => $value) {
        $typesArray[] = [
          "key" => $key,
          "label" => $value["label"] ?? "",
          "description" => $value["description"] ?? "",
        ];
      }
      // Ensure at least one empty row.
      if (empty($typesArray)) {
        $typesArray[] = ["key" => "", "label" => "", "description" => ""];
      }
      $form_state->set("visual_types_items", $typesArray);
    }

    // Get the current items from storage.
    $typesArray = $form_state->get("visual_types_items");

    // Build table header.
    $form["visual_types"]["items"] = [
      "#type" => "table",
      "#header" => [
        $this->t("Machine Name"),
        $this->t("Label"),
        $this->t("Description"),
        $this->t("Operations"),
      ],
      "#empty" => $this->t("No visual types defined yet."),
    ];

    foreach ($typesArray as $i => $typeData) {
      $form["visual_types"]["items"][$i]["key"] = [
        "#type" => "textfield",
        "#title" => $this->t("Machine Name"),
        "#title_display" => "invisible",
        "#default_value" => $typeData["key"] ?? "",
        "#size" => 15,
        "#pattern" => "[a-z0-9_]+",
        "#placeholder" => $this->t("e.g., custom"),
      ];

      $form["visual_types"]["items"][$i]["label"] = [
        "#type" => "textfield",
        "#title" => $this->t("Label"),
        "#title_display" => "invisible",
        "#default_value" => $typeData["label"] ?? "",
        "#size" => 20,
        "#placeholder" => $this->t("e.g., Custom Type"),
      ];

      $form["visual_types"]["items"][$i]["description"] = [
        "#type" => "textfield",
        "#title" => $this->t("Description"),
        "#title_display" => "invisible",
        "#default_value" => $typeData["description"] ?? "",
        "#size" => 40,
        "#placeholder" => $this->t("Brief description"),
      ];

      $form["visual_types"]["items"][$i]["remove"] = [
        "#type" => "submit",
        "#value" => $this->t("Remove"),
        "#name" => "remove_" . $i,
        "#submit" => ["::removeTypeCallback"],
        "#ajax" => [
          "callback" => "::updateVisualTypesCallback",
          "wrapper" => "visual-types-wrapper",
        ],
        "#limit_validation_errors" => [],
      ];
    }

    $form["visual_types"]["add_more"] = [
      "#type" => "submit",
      "#value" => $this->t("Add another type"),
      "#submit" => ["::addMoreCallback"],
      "#ajax" => [
        "callback" => "::updateVisualTypesCallback",
        "wrapper" => "visual-types-wrapper",
      ],
      "#limit_validation_errors" => [],
    ];

    return parent::buildForm($form, $form_state);
  }

  /**
   * Callback for adding more visual types.
   *
   * @param array<string, mixed> $form
   *   The form array.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   */
  public function addMoreCallback(array &$form, FormStateInterface $form_state): void {
    // Get current items from user input and update storage.
    $items = $this->getItemsFromUserInput($form_state);
    $items[] = ["key" => "", "label" => "", "description" => ""];
    $form_state->set("visual_types_items", $items);
    $form_state->setRebuild();
  }

  /**
   * Callback for removing a visual type.
   *
   * @param array<string, mixed> $form
   *   The form array.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   */
  public function removeTypeCallback(array &$form, FormStateInterface $form_state): void {
    $triggeringElement = $form_state->getTriggeringElement();
    $name = $triggeringElement["#name"] ?? "";

    if (preg_match('/remove_(\d+)/', $name, $matches)) {
      $indexToRemove = (int) $matches[1];

      // Get current items from user input.
      $items = $this->getItemsFromUserInput($form_state);

      // Remove the item at the specified index.
      unset($items[$indexToRemove]);
      $items = array_values($items);

      // Ensure at least one empty row remains.
      if (empty($items)) {
        $items[] = ["key" => "", "label" => "", "description" => ""];
      }

      // Update storage with the modified items.
      $form_state->set("visual_types_items", $items);
    }

    $form_state->setRebuild();
  }

  /**
   * Extract items data from user input.
   *
   * Since we use #limit_validation_errors => [], getValue() doesn't work.
   * We need to read directly from user input.
   *
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   *
   * @return array<int, array<string, string>>
   *   Array of item data.
   */
  protected function getItemsFromUserInput(FormStateInterface $form_state): array {
    $userInput = $form_state->getUserInput();
    $inputItems = $userInput["visual_types"]["items"] ?? [];
    $items = [];

    foreach ($inputItems as $index => $item) {
      if (is_array($item)) {
        $items[] = [
          "key" => $item["key"] ?? "",
          "label" => $item["label"] ?? "",
          "description" => $item["description"] ?? "",
        ];
      }
    }

    // Fall back to stored items if no user input yet.
    if (empty($items)) {
      $items = $form_state->get("visual_types_items") ?? [];
    }

    return $items;
  }

  /**
   * AJAX callback to update the visual types section.
   *
   * @param array<string, mixed> $form
   *   The form array.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   *
   * @return array<string, mixed>
   *   The updated form element.
   */
  public function updateVisualTypesCallback(array &$form, FormStateInterface $form_state): array {
    return $form["visual_types"];
  }

  /**
   * {@inheritdoc}
   */
  public function validateForm(array &$form, FormStateInterface $form_state): void {
    parent::validateForm($form, $form_state);

    $items = $form_state->getValue(["visual_types", "items"]) ?? [];
    $keys = [];

    foreach ($items as $index => $item) {
      $key = trim($item["key"] ?? "");
      $label = trim($item["label"] ?? "");

      // Skip empty rows.
      if (empty($key) && empty($label)) {
        continue;
      }

      // Validate that both key and label are provided.
      if (!empty($key) && empty($label)) {
        $form_state->setError(
          $form["visual_types"]["items"][$index]["label"],
          $this->t("Label is required when machine name is provided.")
        );
      }

      if (empty($key) && !empty($label)) {
        $form_state->setError(
          $form["visual_types"]["items"][$index]["key"],
          $this->t("Machine name is required when label is provided.")
        );
      }

      // Validate machine name format.
      if (!empty($key) && !preg_match('/^[a-z0-9_]+$/', $key)) {
        $form_state->setError(
          $form["visual_types"]["items"][$index]["key"],
          $this->t("Machine name must contain only lowercase letters, numbers, and underscores.")
        );
      }

      // Check for duplicate keys.
      if (!empty($key)) {
        if (in_array($key, $keys, TRUE)) {
          $form_state->setError(
            $form["visual_types"]["items"][$index]["key"],
            $this->t("Duplicate machine name: @key", ["@key" => $key])
          );
        }
        $keys[] = $key;
      }
    }

    // Ensure at least one visual type is defined.
    if (empty($keys)) {
      $form_state->setErrorByName(
        "visual_types",
        $this->t("At least one visual type must be defined.")
      );
    }
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state): void {
    $items = $form_state->getValue(["visual_types", "items"]) ?? [];
    $visualTypes = [];

    foreach ($items as $item) {
      $key = trim($item["key"] ?? "");
      $label = trim($item["label"] ?? "");
      $description = trim($item["description"] ?? "");

      if (!empty($key) && !empty($label)) {
        $visualTypes[$key] = [
          "label" => $label,
          "description" => $description,
        ];
      }
    }

    $this->config("flowdrop_node_type.settings")
      ->set("visual_types", $visualTypes)
      ->save();

    parent::submitForm($form, $form_state);
  }

}
