<?php

declare(strict_types=1);

namespace Drupal\flowdrop_trigger\Hook;

use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Hook\Attribute\Hook;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\flowdrop_trigger\Service\TriggerManagerInterface;

/**
 * Implements form hooks for FlowDrop ecosystem.
 *
 * @internal This class is not part of the public API and may change without notice.
 */
class FormHooks {

  /**
   * Constructs a new FormHooks object.
   */
  public function __construct(
    readonly protected TriggerManagerInterface $triggerManager,
    readonly protected AccountProxyInterface $currentUser,
  ) {}

  /**
   * Implements hook_form_alter().
   *
   * Adds form submit and validate handlers to all forms.
   */
  #[Hook("form_alter")]
  public function formAlter(array &$form, FormStateInterface $form_state, string $form_id): void {
    // Add our submit handler to all forms.
    // We use a high weight to ensure it runs after other handlers.
    $form["#submit"][] = [
      $this,
      "handleFormSubmit",
    ];

    // Add our validate handler to all forms.
    $form["#validate"][] = [
      $this,
      "handleFormValidate",
    ];
  }

  /**
   * Form submit handler that triggers workflows.
   *
   * @param array $form
   *   The form array.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   */
  public function handleFormSubmit(array &$form, FormStateInterface $form_state): void {
    $formId = $form_state->getFormObject()->getFormId();

    // Build context for form event.
    $context = $this->buildFormContext($form, $form_state, $formId);

    // Trigger form.submit event.
    $this->triggerManager->processFormEvent("form.submit", $formId, $context);
  }

  /**
   * Form validate handler that triggers workflows.
   *
   * @param array $form
   *   The form array.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   */
  public function handleFormValidate(array &$form, FormStateInterface $form_state): void {
    $formId = $form_state->getFormObject()->getFormId();

    // Build context for form event.
    $context = $this->buildFormContext($form, $form_state, $formId);

    // Trigger form.validate event.
    $this->triggerManager->processFormEvent("form.validate", $formId, $context);
  }

  /**
   * Builds context array from form and form state.
   *
   * @param array $form
   *   The form array.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   * @param string $formId
   *   The form ID.
   *
   * @return array<string, mixed>
   *   Context array with form data.
   */
  protected function buildFormContext(array $form, FormStateInterface $form_state, string $formId): array {
    $context = [
      "form_id" => $formId,
      "form" => $form,
      "form_values" => $form_state->getValues(),
      "form_state" => $this->serializeFormState($form_state),
      "user" => $this->getCurrentUserData(),
    ];

    // Try to extract entity from form if it's an entity form.
    $formObject = $form_state->getFormObject();
    if (method_exists($formObject, "getEntity")) {
      try {
        $entity = $formObject->getEntity();
        if ($entity !== NULL) {
          $context["entity"] = $entity;
          $context["entity_type"] = $entity->getEntityTypeId();
          $context["entity_id"] = $entity->id();
          if (method_exists($entity, "bundle")) {
            $context["bundle"] = $entity->bundle();
          }
        }
      }
      catch (\Exception $e) {
        // Entity not available, continue without it.
      }
    }

    return $context;
  }

  /**
   * Serializes form state to array (basic fields only).
   *
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   *
   * @return array<string, mixed>
   *   Serialized form state data.
   */
  protected function serializeFormState(FormStateInterface $form_state): array {
    // Only serialize safe, non-object data from form state.
    return [
      "values" => $form_state->getValues(),
      "user_input" => $form_state->getUserInput(),
      "triggering_element" => $form_state->getTriggeringElement() !== NULL
        ? [
          "#name" => $form_state->getTriggeringElement()["#name"] ?? NULL,
          "#type" => $form_state->getTriggeringElement()["#type"] ?? NULL,
        ]
        : NULL,
      "has_file_element" => $form_state->hasFileElement(),
      "is_programmed" => $form_state->isProgrammed(),
      "is_rebuilding" => $form_state->isRebuilding(),
      "is_executed" => $form_state->isExecuted(),
      "is_processing_input" => $form_state->isProcessingInput(),
      "cache" => $form_state->get("cache") ?? FALSE,
    ];
  }

  /**
   * Gets current user data as array.
   *
   * @return array<string, mixed>|null
   *   User data array or NULL if anonymous.
   */
  protected function getCurrentUserData(): ?array {
    $account = $this->currentUser->getAccount();

    if ($account->isAnonymous()) {
      return NULL;
    }

    return [
      "uid" => $account->id(),
      "username" => $account->getAccountName(),
      "email" => $account->getEmail(),
    ];
  }

}
