<?php

declare(strict_types=1);

namespace Drupal\flowdrop\DTO\ConfigEdit;

/**
 * Value object for node configuration edit options.
 *
 * Encapsulates all options for how a node's configuration can be edited -
 * either dynamically via schema fetching, through an external form, or both.
 * It provides the frontend with all necessary information to render the
 * appropriate configuration UI.
 *
 * @see config-edit.md for full specification
 */
final class ConfigEdit {

  /**
   * Constructs a ConfigEdit.
   *
   * @param \Drupal\flowdrop\DTO\ConfigEdit\DynamicSchemaEndpoint|null $dynamicSchema
   *   Configuration for fetching schema dynamically from a REST endpoint.
   * @param \Drupal\flowdrop\DTO\ConfigEdit\ExternalEditLink|null $externalEditLink
   *   Configuration for opening an external edit form.
   * @param \Drupal\flowdrop\DTO\ConfigEdit\ConfigEditApiEndpoints|null $api
   *   API endpoints for CRUD operations on the configuration entity.
   * @param bool $preferDynamicSchema
   *   When both dynamicSchema and externalEditLink are present, prefer
   *   dynamic schema. Default TRUE.
   * @param bool $showRefreshButton
   *   Show button to manually refresh the schema. Default TRUE.
   * @param string $loadingMessage
   *   Message to display while loading configuration.
   * @param string $errorMessage
   *   Message to display when configuration loading fails.
   */
  public function __construct(
    public readonly ?DynamicSchemaEndpoint $dynamicSchema = NULL,
    public readonly ?ExternalEditLink $externalEditLink = NULL,
    public readonly ?ConfigEditApiEndpoints $api = NULL,
    public readonly bool $preferDynamicSchema = TRUE,
    public readonly bool $showRefreshButton = TRUE,
    public readonly string $loadingMessage = "Loading configuration...",
    public readonly string $errorMessage = "Failed to load configuration",
  ) {}

  /**
   * Creates an instance from an array.
   *
   * @param array<string, mixed> $data
   *   The array data to create the instance from.
   *
   * @return self
   *   The created instance.
   */
  public static function fromArray(array $data): self {
    $dynamicSchema = NULL;
    if (isset($data["dynamicSchema"]) && is_array($data["dynamicSchema"])) {
      $dynamicSchema = DynamicSchemaEndpoint::fromArray($data["dynamicSchema"]);
    }

    $externalEditLink = NULL;
    if (isset($data["externalEditLink"]) && is_array($data["externalEditLink"])) {
      $externalEditLink = ExternalEditLink::fromArray($data["externalEditLink"]);
    }

    $api = NULL;
    if (isset($data["api"]) && is_array($data["api"])) {
      $api = ConfigEditApiEndpoints::fromArray($data["api"]);
    }

    return new self(
      dynamicSchema: $dynamicSchema,
      externalEditLink: $externalEditLink,
      api: $api,
      preferDynamicSchema: $data["preferDynamicSchema"] ?? TRUE,
      showRefreshButton: $data["showRefreshButton"] ?? TRUE,
      loadingMessage: $data["loadingMessage"] ?? "Loading configuration...",
      errorMessage: $data["errorMessage"] ?? "Failed to load configuration",
    );
  }

  /**
   * Converts the object to an array suitable for JSON serialization.
   *
   * @return array<string, mixed>
   *   The array representation.
   */
  public function toArray(): array {
    $result = [];

    if ($this->dynamicSchema !== NULL) {
      $result["dynamicSchema"] = $this->dynamicSchema->toArray();
    }

    if ($this->externalEditLink !== NULL) {
      $result["externalEditLink"] = $this->externalEditLink->toArray();
    }

    if ($this->api !== NULL) {
      $result["api"] = $this->api->toArray();
    }

    $result["preferDynamicSchema"] = $this->preferDynamicSchema;
    $result["showRefreshButton"] = $this->showRefreshButton;
    $result["loadingMessage"] = $this->loadingMessage;
    $result["errorMessage"] = $this->errorMessage;

    return $result;
  }

  /**
   * Check if dynamic schema is available.
   *
   * @return bool
   *   TRUE if dynamic schema endpoint is configured.
   */
  public function hasDynamicSchema(): bool {
    return $this->dynamicSchema !== NULL;
  }

  /**
   * Check if external edit link is available.
   *
   * @return bool
   *   TRUE if external edit link is configured.
   */
  public function hasExternalEditLink(): bool {
    return $this->externalEditLink !== NULL;
  }

  /**
   * Check if API endpoints are available.
   *
   * @return bool
   *   TRUE if API endpoints are configured.
   */
  public function hasApi(): bool {
    return $this->api !== NULL;
  }

}
