<?php

namespace Drupal\pagedesigner\Service\BatchProcessing;

use Drupal\Core\Batch\BatchBuilder;
use Drupal\field\Entity\FieldConfig;

/**
 * Provides batch processing for pagedesigner.
 */
class FieldChangeBatch {

  /**
   * The title of the batch.
   *
   * @var string
   */
  protected $title = '';

  /**
   * The procedure to call on the pagedesigner service.
   *
   * @var string
   */
  protected $procedure = '';

  /**
   * Prepare a new batch process.
   *
   * @param string $procedure
   *   The name of the procedure.
   * @param string $title
   *   The title of the batch.
   */
  public function __construct(string $procedure, string $title = 'Pagedesigner batch process') {
    $this->procedure = $procedure;
    $this->title = $title;
  }

  /**
   * Add container translation to entities with the field in a batch process.
   *
   * @param \Drupal\field\Entity\FieldConfig $field
   *   The field to enable translation on.
   * @param string $destination
   *   The destination url after the cron run.
   */
  public function run(FieldConfig $field, ?string $destination = NULL) {
    if (empty($this->procedure)) {
      throw new \UnexpectedValueException('The procedure needs to be set.');
    }
    $target_entity_type = $field->getTargetEntityTypeId();
    $target_bundle = $field->getTargetBundle();
    $batch = new BatchBuilder();
    $batch->setTitle($this->title);
    $entities = \Drupal::entityQuery($target_entity_type)->condition('type', $target_bundle)->accessCheck(FALSE)->execute();
    $total = count($entities);
    if ($total > 0) {
      $chunk_size = 25;
      for ($i = 0; $i < ceil($total / $chunk_size); $i++) {
        $batch->addOperation(
        [$this, 'doRun'],
        [$field->getName(), $target_entity_type, $target_bundle]
        );
      }
      if ($destination) {
        $batch->setUrlOptions(['destination' => $destination]);
      }
      batch_set($batch->toArray());
      if (PHP_SAPI == 'cli') {
        drush_backend_batch_process();
      }
    }
  }

  /**
   * Enable translation in batches.
   *
   * @param string $field_name
   *   The id of the field.
   * @param string $target_entity_type
   *   The target entity type of the field.
   * @param string $target_bundle
   *   The target bundle of the field.
   * @param mixed $context
   *   The context.
   */
  public function doRun(string $field_name, string $target_entity_type, string $target_bundle, mixed &$context) {
    if (!isset($context['results']['total'])) {
      $eids = \Drupal::entityQuery($target_entity_type)->condition('type', $target_bundle)
        ->accessCheck(FALSE)
        ->execute();
      $context['results']['total'] = count($eids);
      $context['results']['current'] = 0;
      if (empty($context['results']['total'])) {
        $context['results']['#finished'] = 1;
        return;
      }
    }
    $chunk_size = 25;
    $eids = \Drupal::entityQuery($target_entity_type)
      ->condition('type', $target_bundle)
      ->range($context['results']['current'], $chunk_size)
      ->accessCheck(FALSE)
      ->execute();
    $storageManager = \Drupal::entityTypeManager()->getStorage($target_entity_type);
    foreach ($eids as $eid) {
      $entity = $storageManager->load($eid);
      \Drupal::service('pagedesigner.service')->{$this->procedure}($entity, $field_name);
      $context['results']['current']++;
    }
    if (PHP_SAPI == 'cli') {
      \Drupal::messenger()->addMessage($context['results']['current'] . ' entities processed.');
    }
    if ($context['results']['current'] >= $context['results']['total']) {
      $context['results']['#finished'] = 1;
    }
    else {
      $context['results']['#finished'] = ($context['results']['current'] / $context['results']['total']);
    }
  }

}
