<?php
declare(strict_types=1);

namespace AuxCF\Entities\Traits;

use AuxCF\Entities\FieldEntity;
use AuxCF\Entities\FieldsCollectionEntity;
use AuxCF\Entities\FieldsSetEntity;

/**
 * Provides functionalities to manage a collection of fields, including initialization,
 * addition, removal, and retrieval of fields within a field set. This trait is designed
 * to work with entities that utilize the FieldsSetEntity for organizing fields
 * (eg: Field Groups, Repeater Fields, and Group Fields).
 *
 * It includes support for deprecated fields and field exclusions during initialization.
 * Fields are either individual FieldEntity instances or can be grouped into
 * FieldsCollectionEntity instances.
 *
 * @since 1.0.0
 */
trait FieldsSetTraits
{
  /**
   * @var string[] List of namespaced classes of fields and fields collections to initialize
   */
  protected array $initFields = [];

  /**
   * @var string[] List of namespaced classes of fields and fields collections to exclude (for fields with inherited "initFields")
   */
  protected array $initExcludeFields = [];

  /**
   * Array of deprecated fields in the set that have been replaced or upgraded.
   *
   * <code>
   * [
   *   'old_field_name' => [
   *     'label' => 'Old Field Label',
   *     'replaced_with' => NewFieldEntity::class
   *   ]
   * ]
   * </code>
   *
   * @var array<string,array{label:string,replaced_with:class-string}>
   */
  protected array $deprecatedFields = [];

  /**
   * @var FieldsSetEntity The FieldsSet containing all the fields within the group
   */
  protected FieldsSetEntity $fieldsSet;

  /**
   * Fixed string used to replace patterns in field names.
   * @var string
   */
  public string $fieldsWildcard = '';

  /**
   * Set a string to replace into any subfields that have a patterned name
   *
   * Top-level fields in ACF must have unique names for options pages. To "reuse"
   * top-level fields that are registered dynamically (as with post-type options pages),
   * the fields are renamed with the unique "wildcard" of the root field group.
   *
   * @since 1.0.0
   */
  public function setFieldsWildcard(string $fieldsWildcard): static
  {
    $this->fieldsWildcard = $fieldsWildcard;
    return $this;
  }

  /**
   * Get the wildcard for subfields with a patterned name.
   *
   * @return string The current wildcard string
   * @since 1.0.0
   */
  public function getFieldsWildcard(): string
  {
    return $this->fieldsWildcard;
  }

  /**
   * Initializes the fields for the current entity. This method sets up the field set,
   * processes the specified initial fields, and optionally excludes certain fields.
   * It creates and adds instances of FieldEntity or FieldsCollectionEntity to the entity.
   *
   * @since 1.0.0
   */
  protected final function initFields(): void
  {
    $this->fieldsSet = new FieldsSetEntity($this);

    if (!empty($this->initFields)) {
      $initFields = $this->initFields;

      if (!empty($this->initExcludeFields)) {
        $initFields = array_diff($initFields, $this->initExcludeFields);
      }

      foreach ($initFields as $fieldKey => $fieldValue) {
        /* @var FieldEntity|FieldsCollectionEntity $fieldEntity */
        $fieldEntity = $fieldValue::create($this);

        if ($fieldEntity instanceof FieldsCollectionEntity) {
          $fieldEntity->addFieldsToGroup($this, $fieldEntity);
        } else {
          $this->addField($fieldEntity);
        }
      }
    }
  }

  /**
   * Perform additional operations or modifications before fields are initialized.
   * @since 1.0.0
   */
  protected function beforeInitFields(): void {}

  /**
   * Perform additional operations or modifications after fields are initialized.
   * @since 1.0.0
   */
  protected function afterInitFields(): void {}

  /**
   * Get all of the fields in the group or collections
   * @return \AuxCF\Entities\FieldEntity[]
   * @since 1.0.0
   */
  public function getFields(): array
  {
    return $this->fieldsSet->getFields();
  }

  /**
   * Get field by name
   *
   * @since 1.0.0
   * @uses \AuxCF\Entities\FieldsSetEntity::getFieldByName()
   * @see \AuxCF\Entities\FieldEntity::$name
   */
  public function getField(string $name): ?FieldEntity
  {
    return $this->fieldsSet->getFieldByName($name);
  }

  /**
   * Add field to the group or collection
   * @since 1.0.0
   * @uses \AuxCF\Entities\FieldsSetEntity::addField()
   */
  public function addField(
    string|FieldEntity $field,
    string $where = 'bottom',
    ?string $whereRelativeTo = null
  ): FieldEntity|null
  {
    return $this->fieldsSet->addField($field, $where, $whereRelativeTo);
  }

  /**
   * Remove field by name from the group or collection
   * @since 1.0.0
   * @uses \AuxCF\Entities\FieldsSetEntity::removeField()
   * @see \AuxCF\Entities\FieldEntity::$name
   */
  public function removeField(string $fieldName): static
  {
    $this->fieldsSet->removeField($fieldName);

    return $this;
  }

  /**
   * Get all of the deprecated fields defined for the group or collection
   * @since 1.0.0
   * @uses \AuxCF\Entities\FieldGroupEntity::$deprecatedFields
   */
  public function getDeprecatedFields(): array
  {
    return $this->deprecatedFields;
  }
}
