<?php
declare(strict_types=1);

namespace AuxCF\Entities;

use AuxCF\Entities\FieldGroup\Enum\FieldGroupInstructionPlacementEnum;
use AuxCF\Entities\FieldGroup\Enum\FieldGroupLabelPlacementEnum;
use AuxCF\Entities\FieldGroup\Enum\FieldGroupPositionEnum;
use AuxCF\Entities\FieldGroup\Enum\FieldGroupStyleEnum;
use AuxCF\Entities\FieldGroup\Traits\FieldGroupArrayTraits;
use AuxCF\Entities\Settings\Enum\OperatorEnum;
use AuxCF\Entities\Settings\Traits\EnumPropertyTraits;
use AuxCF\Entities\Traits\FieldsSetTraits;
use AuxCF\Entities\Traits\LocationsSetTraits;

/**
 * ACF Field Group
 *
 * All field groups are based off of and extend this class.
 *
 * @since 1.0.0
 * @link https://www.advancedcustomfields.com/resources/register-fields-via-php/#group-settings
 */
class FieldGroupEntity
{
  use FieldGroupArrayTraits, FieldsSetTraits, LocationsSetTraits, EnumPropertyTraits;

  /**
   * @var string Unique key for the field group. Used for the directory name for the component in '/blocks' directory.
   */
  public static string $key = '';
  /**
   * @var string Relative path to the AUX documentation for the component
   * @see \AuxCF::DOCUMENTATION_BASE
   */
  public static string $documentationPath = '';
  /**
   * @var string Development state of the component
   */
  public static string $state = 'active';

  /**
   * @var string Title of the field group, set from the component's block.json
   */
  protected string $title = '';
  /**
   * @var string Description of the field group, set from the component's block.json if not defined
   */
  protected string $description = '';

  /**
   * @var string Method of local definition of the component (always use 'php')
   */
  protected string $local = 'php';
  /**
   * @var string Override the key for the field group, for dynamically created field groups
   */
  protected string $overrideKey = '';
  protected int $menuOrder = 0;
  protected FieldGroupPositionEnum $position = FieldGroupPositionEnum::DEFAULT;
  protected FieldGroupStyleEnum $style = FieldGroupStyleEnum::DEFAULT;
  protected FieldGroupLabelPlacementEnum $labelPlacement = FieldGroupLabelPlacementEnum::DEFAULT;
  protected FieldGroupInstructionPlacementEnum $instructionPlacement = FieldGroupInstructionPlacementEnum::DEFAULT;
  protected array $hideOnScreen = [];
  protected bool $active = true;
  /**
   * @var int Whether to show the field group in the REST API
   */
  protected int $showInRest = 0;
  /**
   * @var object|null Configuration of the block for the component populated from block.json
   * @see https://developer.wordpress.org/block-editor/developers/block-api/block-registration/#block-configuration
   */
  protected ?object $blockConfig;

  public function __construct(bool $doInit = true) {
    if ($doInit) {
      $this->init();
    }
  }

  /**
   * Initialize the field group
   *
   * This method will initialize the field group by setting the title and description
   * from the block configuration, if available. It will also initialize the fields
   * and locations.
   *
   * @since 1.0.0
   */
  public final function init(): void
  {
    $this->blockConfig = $this->getBlockConfig();

    if ($this->blockConfig) {
      $this->title = $this->blockConfig->title;

      if (empty($this->description)) {
        $this->description = $this->blockConfig->description;
      }
    }

    $this->initFields();
    $this->initLocations();
  }

  /**
   * Get if the field group is active
   *
   * If the state of the field group is 'active', the field group is active. If the state
   * is 'dev' and the current user can edit posts, the field group is active..
   *
   * @since 1.0.0
   */
  public static function isActive(): bool
  {
    return (
      'active' === static::$state ||
      ('dev' === static::$state && current_user_can('edit_posts'))
    );
  }

  /**
   * Get the block configuration for the field group
   *
   * The block configuration is read from the block.json file in the '/blocks' directory
   * of the theme. If the file does not exist, null is returned.
   *
   * @since 1.0.0
   */
  public function getBlockConfig(): object|null
  {
    $blockFile = get_theme_file_path('blocks/' . static::$key . '/block.json');

    if (file_exists($blockFile)) {
      return json_decode(file_get_contents($blockFile));
    }

    return null;
  }

  /**
   * Sets the override key for the current instance.
   * @since 1.0.0
   */
  public function setOverrideKey(string $overrideKey): static
  {
    $this->overrideKey = $overrideKey;
    return $this;
  }

  /**
   * Retrieves the effective key, using the static key if no override key is set.
   * @since 1.0.0
   */
  public function getEffectiveKey(): string
  {
    return !empty($this->overrideKey) ? $this->overrideKey : static::$key;
  }

  /**
   * Get the field group title
   * @since 1.0.0
   */
  public function getTitle(): string
  {
    return $this->title;
  }

  /**
   * Get the field group description
   * @since 1.0.0
   */
  public function getDescription(): string
  {
    return $this->description;
  }

  /**
   * Manually set the description of the field group
   *
   * If the desciprtion is not set, the description from the block
   * configuration will be used
   *
   * @since 1.0.0
   */
  public function setDescription(string $description): self
  {
    $this->description = $description;

    return $this;
  }

  /**
   * Set the placement of the field group on the edition screen
   * @since 1.0.0
   * @see FieldGroupPositionEnum
   */
  public function setPosition(string|FieldGroupPositionEnum $position): self
  {
    $this->setEnumProperty(FieldGroupPositionEnum::class, $position, 'position');

    return $this;
  }

  /**
   * Set the display style of the field group
   * @since 1.0.0
   * @see FieldGroupStyleEnum
   */
  public function setStyle(string|FieldGroupStyleEnum $style): self
  {
    $this->setEnumProperty(FieldGroupStyleEnum::class, $style, 'style');

    return $this;
  }

  /**
   * Set the placement of the label for the field group
   * @since 1.0.0
   * @see FieldGroupLabelPlacementEnum
   */
  public function setLabelPlacement(string|FieldGroupLabelPlacementEnum $labelPlacement): self
  {
    $this->setEnumProperty(FieldGroupLabelPlacementEnum::class, $labelPlacement, 'labelPlacement');

    return $this;
  }

  /**
   * Set the position of the instructions for the field group
   * @since 1.0.0
   * @see FieldGroupInstructionPlacementEnum
   */
  public function setInstructionPlacement(string|FieldGroupInstructionPlacementEnum $instructionPlacement): self
  {
    $this->setEnumProperty(FieldGroupInstructionPlacementEnum::class, $instructionPlacement, 'instructionPlacement');

    return $this;
  }

  /**
   *  Set the field group's position on the aux-global-settings page. Position 0 is at the top of the page.
   * @since 1.0.0
   */
  public function setMenuOrder(int $menuOrder): self
  {
    $this->menuOrder = $menuOrder;

    return $this;
  }

  /**
   * Register the field group with ACF
   *
   * Before the field group is registered, the 'aux/register_field_group/{$key}'
   * is fired, passing the field group as the only argument to the callable function
   *
   * @since 1.0.0
   * @uses acf_add_local_field_group()
   */
  public function register(): void
  {
    if (!\WP_AUX::instance()->hasRequirementsMet()) {
      return;
    }

    do_action('aux/register_field_group/' . $this->getEffectiveKey(), $this);

    acf_add_local_field_group($this->getArray());
  }

  /**
   * Create an initialized field group
   * @since 1.0.0
   */
  public static function create(): static
  {
    return new static(true);
  }

  /**
   * Create an uninitialized field group
   *
   * Use this method to create a field group used in multiple places requiring different
   * a different name or other properties wherever it is used.
   *
   * @since 1.0.0
   */
  public static function createToConfigure(): static
  {
    return new static(false);
  }
}
