<?php

namespace Drupal\eb\Plugin\EbOperation;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\eb\Attribute\EbOperation;
use Drupal\eb\Exception\ExecutionException;
use Drupal\eb\PluginBase\OperationBase;
use Drupal\eb\Result\ExecutionResult;
use Drupal\eb\Result\PreviewResult;
use Drupal\eb\Result\RollbackResult;
use Drupal\eb\Result\ValidationResult;
use Drupal\eb\Service\DiscoveryServiceInterface;
use Drupal\eb\Service\FieldManagementServiceInterface;
use Drupal\eb\Service\Traits\FieldValidationTrait;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Operation for deleting fields from entity bundles.
 */
#[EbOperation(
  id: 'delete_field',
  label: new TranslatableMarkup('Delete Field'),
  description: new TranslatableMarkup('Deletes a field instance and optionally its storage'),
  operationType: 'delete',
)]
class DeleteFieldOperation extends OperationBase {

  use FieldValidationTrait;

  /**
   * Constructor.
   *
   * @param array<string, mixed> $configuration
   *   Plugin configuration.
   * @param string $plugin_id
   *   Plugin ID.
   * @param mixed $plugin_definition
   *   Plugin definition.
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
   *   Entity type manager.
   * @param \Psr\Log\LoggerInterface $logger
   *   Logger channel.
   * @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
   *   Config factory.
   * @param \Drupal\eb\Service\DiscoveryServiceInterface $discoveryService
   *   Discovery service.
   * @param \Drupal\eb\Service\FieldManagementServiceInterface $fieldManagementService
   *   Field management service.
   */
  public function __construct(
    array $configuration,
    string $plugin_id,
    mixed $plugin_definition,
    EntityTypeManagerInterface $entityTypeManager,
    LoggerInterface $logger,
    ConfigFactoryInterface $configFactory,
    protected DiscoveryServiceInterface $discoveryService,
    protected FieldManagementServiceInterface $fieldManagementService,
  ) {
    parent::__construct($configuration, $plugin_id, $plugin_definition, $entityTypeManager, $logger, $configFactory);
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static {
    /** @var \Psr\Log\LoggerInterface $logger */
    $logger = $container->get('logger.channel.eb');
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('entity_type.manager'),
      $logger,
      $container->get('config.factory'),
      $container->get('eb.discovery_service'),
      $container->get('eb.field_management'),
    );
  }

  /**
   * {@inheritdoc}
   */
  public function validate(): ValidationResult {
    $result = new ValidationResult();

    // Validate required fields.
    $this->validateRequiredFields(
      ['field_name', 'entity_type', 'bundle'],
      $result
    );

    if (!$result->isValid()) {
      return $result;
    }

    $field_name = $this->getDataValue('field_name');
    $entity_type = $this->getDataValue('entity_type');
    $bundle = $this->getDataValue('bundle');

    // Validate entity type and bundle using trait method.
    if (!$this->validateEntityBundle($entity_type, $bundle, $result)) {
      return $result;
    }

    // Validate field exists using trait method.
    $field = $this->validateFieldExists($entity_type, $bundle, $field_name, $result);
    if ($field) {
      // Warn about data loss.
      $result->addWarning(
        $this->t('Deleting this field will permanently remove all data stored in it.')
      );
    }

    return $result;
  }

  /**
   * {@inheritdoc}
   */
  public function preview(): PreviewResult {
    $preview = new PreviewResult();

    $field_name = $this->getDataValue('field_name');
    $entity_type = $this->getDataValue('entity_type');
    $bundle = $this->getDataValue('bundle');

    $field = $this->fieldManagementService->loadFieldConfig($entity_type, $bundle, $field_name);

    if (!$field) {
      $preview->addOperation(
        'delete',
        'field',
        $field_name,
        $this->t('ERROR: Field not found')
      );
      return $preview;
    }

    $field_storage = $this->fieldManagementService->loadFieldStorage($entity_type, $field_name);
    $delete_storage = $this->getDataValue('delete_storage', FALSE);

    // Check if storage is used by other bundles.
    $storage_shared = FALSE;
    if ($field_storage) {
      $bundles_using_field = $this->fieldManagementService->getBundlesUsingField($entity_type, $field_name);
      $storage_shared = count($bundles_using_field) > 1;
    }

    $preview->addOperation(
      'delete',
      'field',
      $field_name,
      $this->t('Delete field "@label" (@name) from @type:@bundle', [
        '@label' => $field->getLabel(),
        '@name' => $field_name,
        '@type' => $entity_type,
        '@bundle' => $bundle,
      ])
    );

    $details = [
      'Field Name' => $field_name,
      'Entity Type' => $entity_type,
      'Bundle' => $bundle,
      'Label' => $field->getLabel(),
      'Type' => $field->getType(),
    ];

    if ($storage_shared && $delete_storage) {
      $details['WARNING'] = 'Storage is shared with other bundles but will be deleted';
    }
    elseif ($storage_shared) {
      $details['Storage Action'] = 'Preserve (used by other bundles)';
    }
    elseif ($delete_storage) {
      $details['Storage Action'] = 'Delete storage and all data';
    }
    else {
      $details['Storage Action'] = 'Delete instance only';
    }

    $preview->addDetails($details);

    return $preview;
  }

  /**
   * {@inheritdoc}
   */
  public function execute(): ExecutionResult {
    $field_name = $this->getDataValue('field_name');
    $entity_type = $this->getDataValue('entity_type');
    $bundle = $this->getDataValue('bundle');
    $delete_storage = $this->getDataValue('delete_storage', FALSE);

    return $this->executeWithErrorHandling(function () use ($field_name, $entity_type, $bundle, $delete_storage) {
      $field = $this->fieldManagementService->loadFieldConfig($entity_type, $bundle, $field_name);

      if (!$field) {
        throw new ExecutionException("Field '{$field_name}' not found");
      }

      // Store configuration for rollback.
      $field_config = $field->toArray();

      // Get storage before deleting field.
      $field_storage = $this->fieldManagementService->loadFieldStorage($entity_type, $field_name);
      $storage_config = $field_storage ? $field_storage->toArray() : NULL;

      // Get display configurations.
      $form_display = $this->entityTypeManager
        ->getStorage('entity_form_display')
        ->load($entity_type . '.' . $bundle . '.default');

      $form_component = NULL;
      if ($form_display) {
        $form_component = $form_display->getComponent($field_name);
      }

      $view_display = $this->entityTypeManager
        ->getStorage('entity_view_display')
        ->load($entity_type . '.' . $bundle . '.default');

      $view_component = NULL;
      if ($view_display) {
        $view_component = $view_display->getComponent($field_name);
      }

      // Delete field instance.
      $this->fieldManagementService->deleteFieldConfig($field);

      $storage_deleted = FALSE;

      // Delete storage if requested and no other bundles use it.
      if ($delete_storage && $field_storage) {
        $bundles_using_field = $this->fieldManagementService->getBundlesUsingField($entity_type, $field_name);

        if (empty($bundles_using_field)) {
          $this->fieldManagementService->deleteFieldStorage($field_storage);
          $storage_deleted = TRUE;
        }
      }

      // Clear caches.
      $this->fieldManagementService->clearCachedFieldDefinitions();

      $result = new ExecutionResult(TRUE);
      $result->addAffectedEntityById('field_config', $field_name);

      if ($storage_deleted) {
        $result->addAffectedEntityById('field_storage_config', $field_name);
      }

      $result->setRollbackData([
        'field_config' => $field_config,
        'storage_config' => $storage_config,
        'storage_deleted' => $storage_deleted,
        'form_component' => $form_component,
        'view_component' => $view_component,
      ]);

      $this->logInfo('Deleted field: @name on @type:@bundle', [
        '@name' => $field_name,
        '@type' => $entity_type,
        '@bundle' => $bundle,
      ]);

      return $result;
    }, 'delete field');
  }

  /**
   * {@inheritdoc}
   */
  public function rollback(): RollbackResult {
    $rollback_data = $this->getRequiredRollbackData();

    return $this->rollbackWithErrorHandling(function () use ($rollback_data) {
      // Recreate field storage if it was deleted.
      if (!empty($rollback_data['storage_deleted']) && $rollback_data['storage_config']) {
        $this->fieldManagementService->createFieldStorage($rollback_data['storage_config']);
      }

      // Recreate field instance.
      if ($rollback_data['field_config']) {
        $this->fieldManagementService->createFieldConfig($rollback_data['field_config']);
      }

      // Restore form display component.
      if ($rollback_data['form_component']) {
        $field_config = $rollback_data['field_config'];
        $form_display = $this->entityTypeManager
          ->getStorage('entity_form_display')
          ->load($field_config['entity_type'] . '.' . $field_config['bundle'] . '.default');

        if ($form_display) {
          $form_display->setComponent($field_config['field_name'], $rollback_data['form_component']);
          $form_display->save();
        }
      }

      // Restore view display component.
      if ($rollback_data['view_component']) {
        $field_config = $rollback_data['field_config'];
        $view_display = $this->entityTypeManager
          ->getStorage('entity_view_display')
          ->load($field_config['entity_type'] . '.' . $field_config['bundle'] . '.default');

        if ($view_display) {
          $view_display->setComponent($field_config['field_name'], $rollback_data['view_component']);
          $view_display->save();
        }
      }

      // Clear caches.
      $this->fieldManagementService->clearCachedFieldDefinitions();

      $field_name = $rollback_data['field_config']['field_name'] ?? 'unknown';
      $this->logInfo('Rolled back field deletion: @name', ['@name' => $field_name]);

      $result = new RollbackResult(TRUE);
      $result->addMessage($this->t('Successfully rolled back field deletion'));

      return $result;
    }, 'field deletion');
  }

}
