<?php

namespace Drupal\eb\Form;

use Drupal\Component\Datetime\TimeInterface;
use Drupal\Core\Entity\EntityConfirmFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\eb\Entity\EbDefinition;
use Drupal\eb\Service\EbLogManagerInterface;
use Drupal\eb\Service\OperationBuilder;
use Drupal\eb\Service\OperationDataBuilderInterface;
use Drupal\eb\Service\OperationProcessor;
use Drupal\eb\Service\ValidationManager;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Confirmation form for applying an entity builder definition.
 */
class EbDefinitionApplyForm extends EntityConfirmFormBase {

  /**
   * The operation builder service.
   *
   * @var \Drupal\eb\Service\OperationBuilder
   */
  protected OperationBuilder $operationBuilder;

  /**
   * The operation processor service.
   *
   * @var \Drupal\eb\Service\OperationProcessor
   */
  protected OperationProcessor $operationProcessor;

  /**
   * The validation manager service.
   *
   * @var \Drupal\eb\Service\ValidationManager
   */
  protected ValidationManager $validationManager;

  /**
   * The EbLog manager service.
   *
   * @var \Drupal\eb\Service\EbLogManagerInterface
   */
  protected EbLogManagerInterface $ebLogManager;

  /**
   * The time service.
   *
   * @var \Drupal\Component\Datetime\TimeInterface
   */
  protected TimeInterface $time;

  /**
   * The operation data builder service.
   *
   * @var \Drupal\eb\Service\OperationDataBuilderInterface
   */
  protected OperationDataBuilderInterface $operationDataBuilder;

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container): static {
    $instance = parent::create($container);
    $instance->operationBuilder = $container->get('eb.operation_builder');
    $instance->operationProcessor = $container->get('eb.operation_processor');
    $instance->validationManager = $container->get('eb.validation_manager');
    $instance->ebLogManager = $container->get('eb.eb_log_manager');
    $instance->time = $container->get('datetime.time');
    $instance->operationDataBuilder = $container->get('eb.operation_data_builder');
    return $instance;
  }

  /**
   * {@inheritdoc}
   */
  public function getQuestion() {
    return $this->t('Apply definition %name?', ['%name' => $this->entity->label()]);
  }

  /**
   * {@inheritdoc}
   */
  public function getDescription() {
    /**
* @var \Drupal\eb\Entity\EbDefinition $definition
*/
    $definition = $this->entity;

    $bundle_count = $definition->getBundleCount();
    $field_count = $definition->getFieldCount();
    $entity_types = $definition->getEntityTypes();
    $entity_type_label = !empty($entity_types) ? implode(', ', $entity_types) : $this->t('not specified');

    $description = $this->t(
          'This will create or update @bundle_count bundle(s) and @field_count field(s) for the %entity_type entity type(s). A rollback point will be created so you can undo these changes if needed.', [
            '@bundle_count' => $bundle_count,
            '@field_count' => $field_count,
            '%entity_type' => $entity_type_label,
          ]
      );

    if ($definition->isApplied()) {
      $description .= ' ' . $this->t('Note: This definition has already been applied. Re-applying will update any changed configurations.');
    }

    return $description;
  }

  /**
   * {@inheritdoc}
   */
  public function getCancelUrl() {
    return new Url('entity.eb_definition.collection');
  }

  /**
   * {@inheritdoc}
   */
  public function getConfirmText() {
    return $this->t('Apply');
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state): void {
    /**
* @var \Drupal\eb\Entity\EbDefinition $definition
*/
    $definition = $this->entity;

    try {
      // Convert definition data to operation array format.
      $operation_data = $this->buildOperationData($definition);

      // Build operation instances.
      $operations = $this->operationBuilder->buildBatch($operation_data);

      if (empty($operations)) {
        $this->messenger()->addWarning(
              $this->t(
                  'No operations to execute for definition %name.', [
                    '%name' => $definition->label(),
                  ]
              )
          );
        $form_state->setRedirectUrl($this->getCancelUrl());
        return;
      }

      // Validate all operations using batch validation with context awareness.
      // This allows operations to reference bundles/fields created earlier.
      $batch_result = $this->validationManager->validateBatch($operations);

      if (!$batch_result->isValid()) {
        // Collect error messages.
        $error_messages = [];
        foreach ($batch_result->getErrors() as $error) {
          $error_messages[] = $error['message'] ?? '';
        }

        // Log validation failure in history.
        $eb_log = $this->ebLogManager->log([
          'label' => $definition->id() . ' (apply)',
          'definition_id' => $definition->id(),
          'action' => 'apply',
          'operation_count' => count($operations),
          'status' => 'failed',
        ]);
        $this->ebLogManager->complete($eb_log, 'failed', 0, count($error_messages));

        $this->messenger()->addError(
              $this->t(
                  'Validation failed for definition %name:', [
                    '%name' => $definition->label(),
                  ]
              )
          );
        foreach ($error_messages as $error_message) {
          $this->messenger()->addError($error_message);
        }
        $form_state->setRedirectUrl($this->getCancelUrl());
        return;
      }

      // Log apply start in history.
      $eb_log = $this->ebLogManager->log([
        'label' => $definition->id() . ' (apply)',
        'definition_id' => $definition->id(),
        'action' => 'apply',
        'operation_count' => count($operations),
        'status' => 'pending',
      ]);

      // Execute operations using the standard processor.
      // This provides events, rollback support, and consistent behavior.
      // Pass definition context for rollback grouping.
      $context = ['definition_id' => $definition->id()];
      $results = $this->operationProcessor->executeBatch($operations, FALSE, $context);

      // Count successes and failures.
      $success_count = 0;
      $failure_count = 0;
      $errors = [];

      foreach ($results as $result) {
        if ($result->isSuccess()) {
          $success_count++;
        }
        else {
          $failure_count++;
          foreach ($result->getMessages() as $message) {
            $errors[] = $message;
          }
        }
      }

      // Determine final status.
      $status = 'success';
      if ($failure_count > 0 && $success_count === 0) {
        $status = 'failed';
      }
      elseif ($failure_count > 0 && $success_count > 0) {
        $status = 'partial';
      }

      // Update history record.
      $this->ebLogManager->complete($eb_log, $status, $success_count, $failure_count);

      if ($failure_count === 0) {
        // Mark definition as applied on full success.
        $definition->markAsApplied();
        $definition->save();

        $this->messenger()->addStatus(
          $this->t(
              'Definition %name has been applied successfully. @count operation(s) executed.', [
                '%name' => $definition->label(),
                '@count' => $success_count,
              ]
          )
          );
      }
      else {
        $this->messenger()->addWarning(
          $this->t(
              'Definition %name was partially applied. @success succeeded, @failed failed.', [
                '%name' => $definition->label(),
                '@success' => $success_count,
                '@failed' => $failure_count,
              ]
          )
          );

        foreach ($errors as $error) {
          $this->messenger()->addError($error);
        }
      }
    }
    catch (\Exception $e) {
      $this->messenger()->addError(
            $this->t(
                'Failed to apply definition: @error', [
                  '@error' => $e->getMessage(),
                ]
            )
        );
    }

    $form_state->setRedirectUrl($this->getCancelUrl());
  }

  /**
   * Converts EbDefinition data to operation array format.
   *
   * Delegates to OperationDataBuilder which handles core definitions and
   * calls extension plugins to build operations for extension-owned types.
   *
   * @param \Drupal\eb\Entity\EbDefinition $definition
   *   The definition entity.
   *
   * @return array<int, array<string, mixed>>
   *   Array of operation data arrays.
   */
  protected function buildOperationData(EbDefinition $definition): array {
    return $this->operationDataBuilder->build([
      'bundle_definitions' => $definition->getBundleDefinitions(),
      'field_definitions' => $definition->getFieldDefinitions(),
      'field_group_definitions' => $definition->getFieldGroupDefinitions(),
      'display_field_definitions' => $definition->getDisplayFieldDefinitions(),
      'menu_definitions' => $definition->getMenuDefinitions(),
    ]);
  }

}
