<?php

namespace Drupal\castorcito\Form;

use Drupal\castorcito\Entity\CastorcitoCategory;
use Drupal\Core\Entity\EntityForm;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Component\Serialization\Json;

/**
 * Base form for castorcito component add and edit forms.
 */
class CastorcitoComponentCloneForm extends EntityForm {

  /**
   * The entity being used by this form.
   *
   * @var \Drupal\castorcito\CastorcitoComponentInterface
   */
  protected $entity;

  /**
   * The castorcito component entity storage.
   *
   * @var \Drupal\Core\Entity\EntityStorageInterface
   */
  protected $componentStorage;

  /**
   * Constructs a base class for castorcito component add and edit forms.
   *
   * @param \Drupal\Core\Entity\EntityStorageInterface $component_storage
   *   The castorcito component entity storage.
   */
  public function __construct(EntityStorageInterface $component_storage) {
    $this->componentStorage = $component_storage;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('entity_type.manager')->getStorage('castorcito_component')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    $title = $this->t('Duplicate @label component.', ['@label' => $this->entity->label()]);
    $form['title']['#markup'] = '<h2>' . $title . '</h2>';

    $form['entity_label'] = [
      '#type' => 'textfield',
      '#title' => $this->t('New component label'),
      '#default_value' => $this->entity->label() . ' - Cloned',
      '#required' => TRUE,
    ];

    $form['entity_id'] = [
      '#type' => 'machine_name',
      '#title' => $this->t('New component machine name'),
      '#default_value' => $this->defaultCloneEntityId(),
      '#element_validate' => [
        [$this, 'validateId'],
      ],
      '#required' => TRUE,
    ];

    $categories = CastorcitoCategory::loadMultiple();
    $category_options = [];
    if (!empty($categories)) {
      foreach ($categories as $category) {
        $category_options[$category->id()] = $category->label();
      }
    }

    $form['category'] = [
      '#type' => 'select',
      '#title' => $this->t('Category'),
      '#options' => $category_options,
      '#default_value' => $this->entity->get('category'),
      '#required' => TRUE,
      '#description' => $this->t('Select the category to which this component belongs.'),
      '#empty_option' => $this->t('- Select a category -'),
    ];

    $form['actions'] = ['#type' => 'actions'];
    $form['actions']['submit'] = [
      '#type'        => 'submit',
      '#value'       => $this->t('Duplicate'),
      '#button_type' => 'primary',
    ];

    $form['actions']['cancel'] = [
      '#type' => 'link',
      '#title' => $this->t('Cancel'),
      '#url' => $this->entity->toUrl('collection'),
      '#attributes' => ['class' => ['button']],
    ];

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    $id = $form_state->getValue('entity_id');
    $label = $form_state->getValue('entity_label');
    $model = $this->entity->getModel();
    $model['type'] = $id;
    $model['label'] = $label;
    $new_bundle = $this->entity->createDuplicate();
    $new_bundle->set('id', $id);
    $new_bundle->set('label', $label);
    $new_bundle->set('model', Json::encode($model));
    $new_bundle->set('category', $form_state->getValue('category'));
    $new_bundle->save();
    $this->messenger()->addStatus($this->t('The component was cloned.'));
    $form_state->setRedirectUrl($this->entity->toUrl('collection'));
  }

  /**
   * Validate the availability of the id.
   *
   * @param array $element
   *   THe element.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   THe formstate.
   */
  public function validateId(array $element, FormStateInterface $form_state) {
    $new_bundle = $form_state->getValue('entity_id');
    $entity_type = $this->componentStorage->getEntityType();

    // Validate machine name format.
    if (!preg_match('/^[a-z0-9_]+$/', $new_bundle)) {
      $form_state->setError($element, $this->t('The machine-readable name must contain only lowercase letters, numbers, and underscores.'));
    }

    $existing = call_user_func($entity_type->getClass() . '::load', $new_bundle);
    if (!empty($existing)) {
      $form_state->setError($element, $this->t('@bundle component name already exists', ['@bundle' => $new_bundle]));
    }
  }

  /**
   * Generates a default ID for cloning an entity.
   *
   * @return string
   *   The new entity id.
   */
  protected function defaultCloneEntityId() {
    $from = 1;
    $base_id = $this->entity->id();
    $entity_type = $this->componentStorage->getEntityType();

    do {
      $base_id = $this->nextDefaultElement($base_id, $from++, '_');
      $continue = !is_null(call_user_func($entity_type->getClass() . '::load', $base_id));
    } while ($continue);

    return $base_id;
  }

  /**
   * Return the next default supposed name of an element.
   *
   * @param string $id
   *   The base name.
   * @param int $from
   *   The default number.
   * @param string $separator
   *   The separator.
   *
   * @return string
   *   The next default name.
   */
  protected function nextDefaultElement($id, $from, $separator) {
    $id_data = explode($separator, $id);
    if (is_numeric(end($id_data))) {
      $from = array_pop($id_data);
      $id = implode($separator, $id_data);
    }

    return $id . $separator . ($from + 1);
  }

}
