<?php

namespace Drupal\eb\Plugin\EbOperation;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\eb\Attribute\EbOperation;
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\Traits\FieldValidationTrait;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Operation for configuring field display in view modes.
 */
#[EbOperation(
  id: 'configure_view_mode',
  label: new TranslatableMarkup('Configure View Mode'),
  description: new TranslatableMarkup('Configures field formatter for a view mode'),
  operationType: 'update',
)]
class ConfigureViewModeOperation extends OperationBase {

  use FieldValidationTrait;

  /**
   * Constructor.
   */
  public function __construct(
    array $configuration,
    string $plugin_id,
    mixed $plugin_definition,
    EntityTypeManagerInterface $entityTypeManager,
    LoggerInterface $logger,
    ConfigFactoryInterface $configFactory,
    protected DiscoveryServiceInterface $discoveryService,
    protected EntityFieldManagerInterface $entityFieldManager,
    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('entity_field.manager'),
      $container->get('eb.display_configuration'),
    );
  }

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

    $this->validateRequiredFields(
      ['entity_type', 'bundle', 'field_name', 'formatter'],
      $result
    );

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

    $formatter = $this->getDataValue('formatter');

    // Note: Bundle and field existence validation is delegated to the
    // DependencyValidator plugin which has batch context awareness.
    // This allows display operations to reference bundles/fields that will
    // be created earlier in the same batch.
    // Validate formatter exists.
    if (!$this->discoveryService->formatterExists($formatter)) {
      $result->addError(
        $this->t('Formatter "@formatter" does not exist.', ['@formatter' => $formatter]),
        'formatter',
        'formatter_not_found'
      );
    }

    return $result;
  }

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

    $entity_type = $this->getDataValue('entity_type');
    $bundle = $this->getDataValue('bundle');
    $field_name = $this->getDataValue('field_name');
    $formatter = $this->getDataValue('formatter');
    $view_mode = $this->getDataValue('view_mode', 'default');

    $preview->addOperation(
      'update',
      'view_display',
      $field_name,
      $this->t('Configure field "@field" display for @type:@bundle (@mode)', [
        '@field' => $field_name,
        '@type' => $entity_type,
        '@bundle' => $bundle,
        '@mode' => $view_mode,
      ])
    );

    $details = [
      'Field' => $field_name,
      'Formatter' => $formatter,
      'View Mode' => $view_mode,
    ];

    if ($weight = $this->getDataValue('weight')) {
      $details['Weight'] = (string) $weight;
    }

    if ($this->getDataValue('hidden')) {
      $details['Visibility'] = 'Hidden';
    }

    $preview->addDetails($details);

    return $preview;
  }

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

    return $this->executeWithErrorHandling(function () use ($entity_type, $bundle, $field_name, $view_mode, $display_id) {
      if ($this->getDataValue('hidden')) {
        $original_component = $this->displayConfigurationService->hideInViewDisplay(
          $entity_type,
          $bundle,
          $field_name,
          $view_mode
        );
      }
      else {
        $original_component = $this->displayConfigurationService->configureViewDisplay(
          $entity_type,
          $bundle,
          $field_name,
          $this->getDataValue('formatter'),
          $this->getDataValue('formatter_settings', []),
          (int) $this->getDataValue('weight', 0),
          $this->getDataValue('label_display'),
          $view_mode
        );
      }

      $result = new ExecutionResult(TRUE);
      $result->addAffectedEntityById('entity_view_display', $display_id);

      $result->setRollbackData([
        'entity_type' => $entity_type,
        'bundle' => $bundle,
        'field_name' => $field_name,
        'view_mode' => $view_mode,
        'original_component' => $original_component,
      ]);

      $this->logInfo('Configured view display: @display', ['@display' => $display_id]);

      return $result;
    }, 'configure view mode');
  }

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

    return $this->rollbackWithErrorHandling(function () use ($rollback_data) {
      $this->displayConfigurationService->restoreViewDisplay(
        $rollback_data['entity_type'],
        $rollback_data['bundle'],
        $rollback_data['field_name'],
        $rollback_data['original_component'],
        $rollback_data['view_mode'] ?? 'default'
      );

      $display_id = $rollback_data['entity_type'] . '.' . $rollback_data['bundle'] . '.' . ($rollback_data['view_mode'] ?? 'default');
      $this->logInfo('Rolled back view display configuration: @display', ['@display' => $display_id]);

      $result = new RollbackResult(TRUE);
      $result->addMessage($this->t('Successfully rolled back view mode configuration'));

      return $result;
    }, 'view mode configuration');
  }

}
