<?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\DisplayConfigurationService;
use Drupal\eb\Service\FieldManagementServiceInterface;
use Drupal\eb\Service\Traits\FieldValidationTrait;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Operation for updating existing field configurations.
 */
#[EbOperation(
  id: 'update_field',
  label: new TranslatableMarkup('Update Field'),
  description: new TranslatableMarkup('Updates an existing field instance configuration'),
  operationType: 'update',
)]
class UpdateFieldOperation 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.
   * @param \Drupal\eb\Service\DisplayConfigurationService $displayConfigurationService
   *   Display configuration service.
   */
  public function __construct(
    array $configuration,
    string $plugin_id,
    mixed $plugin_definition,
    EntityTypeManagerInterface $entityTypeManager,
    LoggerInterface $logger,
    ConfigFactoryInterface $configFactory,
    protected DiscoveryServiceInterface $discoveryService,
    protected FieldManagementServiceInterface $fieldManagementService,
    protected DisplayConfigurationService $displayConfigurationService,
  ) {
    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'),
      $container->get('eb.display_configuration'),
    );
  }

  /**
   * {@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) {
      return $result;
    }

    // Validate widget compatibility if provided using trait method.
    if ($widget = $this->getDataValue('widget')) {
      $this->validateWidgetCompatibility($widget, $field->getType(), $result);
    }

    // Validate formatter compatibility if provided using trait method.
    if ($formatter = $this->getDataValue('formatter')) {
      $this->validateFormatterCompatibility($formatter, $field->getType(), $result);
    }

    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(
        'update',
        'field',
        $field_name,
        $this->t('ERROR: Field not found')
      );
      return $preview;
    }

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

    // Show before/after comparison.
    $changes = [];

    if ($this->data['label'] ?? NULL) {
      $changes['Label'] = [
        'before' => $field->getLabel(),
        'after' => $this->getDataValue('label'),
      ];
    }

    if ($this->data['description'] ?? NULL) {
      $changes['Description'] = [
        'before' => $field->getDescription(),
        'after' => $this->getDataValue('description'),
      ];
    }

    if (isset($this->data['required'])) {
      $changes['Required'] = [
        'before' => $field->isRequired() ? 'Yes' : 'No',
        'after' => $this->getDataValue('required') ? 'Yes' : 'No',
      ];
    }

    if ($this->data['default_value'] ?? NULL) {
      $changes['Default Value'] = [
        'before' => 'Current value',
        'after' => 'New value',
      ];
    }

    $details = ['Changes' => count($changes) . ' field setting(s)'];
    foreach ($changes as $setting => $values) {
      $details[$setting] = "{$values['before']} → {$values['after']}";
    }

    if ($this->getDataValue('widget')) {
      $details['Widget Update'] = $this->getDataValue('widget');
    }

    if ($this->getDataValue('formatter')) {
      $details['Formatter Update'] = $this->getDataValue('formatter');
    }

    $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');

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

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

      // Store original configuration for rollback.
      $original_config = [
        'label' => $field->getLabel(),
        'description' => $field->getDescription(),
        'required' => $field->isRequired(),
        'default_value' => $field->getDefaultValueLiteral(),
        'settings' => $field->getSettings(),
      ];

      // Update field configuration.
      if ($label = $this->getDataValue('label')) {
        $field->setLabel($label);
      }

      if ($description = $this->getDataValue('description')) {
        $field->setDescription($description);
      }

      if (isset($this->data['required'])) {
        $field->setRequired((bool) $this->getDataValue('required'));
      }

      if ($default_value = $this->getDataValue('default_value')) {
        $field->setDefaultValue($default_value);
      }

      if ($config_settings = $this->getDataValue('field_config_settings')) {
        $field->setSettings(array_merge($field->getSettings(), $config_settings));
      }

      $field->save();

      // Update widget if provided.
      $originalFormComponent = NULL;
      if ($widget = $this->getDataValue('widget')) {
        $originalFormComponent = $this->displayConfigurationService->updateFormDisplay(
          $entity_type,
          $bundle,
          $field_name,
          $widget,
          $this->getDataValue('widget_settings', [])
        );
      }

      // Update formatter if provided.
      $originalViewComponent = NULL;
      if ($formatter = $this->getDataValue('formatter')) {
        $originalViewComponent = $this->displayConfigurationService->updateViewDisplay(
          $entity_type,
          $bundle,
          $field_name,
          $formatter,
          $this->getDataValue('formatter_settings', [])
        );
      }

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

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

      $result->setRollbackData([
        'field_config_id' => $field->id(),
        'original_config' => $original_config,
        'entity_type' => $entity_type,
        'bundle' => $bundle,
        'field_name' => $field_name,
        'original_form_component' => $originalFormComponent,
        'original_view_component' => $originalViewComponent,
      ]);

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

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

    return $this->rollbackWithErrorHandling(function () use ($rollback_data) {
      $field = $this->fieldManagementService->loadFieldConfigById($rollback_data['field_config_id']);

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

      $original = $rollback_data['original_config'];

      // Restore original configuration.
      $field->setLabel($original['label']);
      $field->setDescription($original['description']);
      $field->setRequired($original['required']);
      $field->setDefaultValue($original['default_value']);
      $field->setSettings($original['settings']);
      $field->save();

      // Restore form display if it was modified.
      if (isset($rollback_data['original_form_component'])) {
        $this->displayConfigurationService->restoreFormDisplay(
          $rollback_data['entity_type'],
          $rollback_data['bundle'],
          $rollback_data['field_name'],
          $rollback_data['original_form_component']
        );
      }

      // Restore view display if it was modified.
      if (isset($rollback_data['original_view_component'])) {
        $this->displayConfigurationService->restoreViewDisplay(
          $rollback_data['entity_type'],
          $rollback_data['bundle'],
          $rollback_data['field_name'],
          $rollback_data['original_view_component']
        );
      }

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

      $this->logInfo('Rolled back field update for @id', [
        '@id' => $rollback_data['field_config_id'],
      ]);

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

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

}
