<?php

namespace Drupal\eb\Service;

use Drupal\Core\Entity\Display\EntityDisplayInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Psr\Log\LoggerInterface;

/**
 * Service for managing entity display configurations.
 *
 * Provides unified methods for configuring form and view displays,
 * reducing code duplication across operation plugins.
 */
class DisplayConfigurationService {

  /**
   * Constructor.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
   *   The entity type manager.
   * @param \Psr\Log\LoggerInterface $logger
   *   The logger.
   */
  public function __construct(
    protected EntityTypeManagerInterface $entityTypeManager,
    protected LoggerInterface $logger,
  ) {}

  /**
   * Build display ID from entity type, bundle and mode.
   *
   * @param string $entityType
   *   The entity type ID.
   * @param string $bundle
   *   The bundle ID.
   * @param string $mode
   *   The display mode.
   *
   * @return string
   *   The display ID.
   */
  protected function buildDisplayId(string $entityType, string $bundle, string $mode): string {
    return $entityType . '.' . $bundle . '.' . $mode;
  }

  /**
   * Load or create a display entity.
   *
   * @param string $storageType
   *   The storage type ('entity_form_display' or 'entity_view_display').
   * @param string $entityType
   *   The entity type ID.
   * @param string $bundle
   *   The bundle ID.
   * @param string $mode
   *   The display mode.
   *
   * @return \Drupal\Core\Entity\Display\EntityDisplayInterface
   *   The display entity.
   */
  protected function loadOrCreateDisplay(
    string $storageType,
    string $entityType,
    string $bundle,
    string $mode,
  ): EntityDisplayInterface {
    $displayId = $this->buildDisplayId($entityType, $bundle, $mode);
    $storage = $this->entityTypeManager->getStorage($storageType);

    /** @var \Drupal\Core\Entity\Display\EntityDisplayInterface|null $display */
    $display = $storage->load($displayId);

    if (!$display) {
      /** @var \Drupal\Core\Entity\Display\EntityDisplayInterface $display */
      $display = $storage->create([
        'targetEntityType' => $entityType,
        'bundle' => $bundle,
        'mode' => $mode,
        'status' => TRUE,
      ]);
    }

    return $display;
  }

  /**
   * Load a display entity if it exists.
   *
   * @param string $storageType
   *   The storage type ('entity_form_display' or 'entity_view_display').
   * @param string $entityType
   *   The entity type ID.
   * @param string $bundle
   *   The bundle ID.
   * @param string $mode
   *   The display mode.
   *
   * @return \Drupal\Core\Entity\Display\EntityDisplayInterface|null
   *   The display entity, or NULL if not found.
   */
  protected function loadDisplay(
    string $storageType,
    string $entityType,
    string $bundle,
    string $mode,
  ): ?EntityDisplayInterface {
    $displayId = $this->buildDisplayId($entityType, $bundle, $mode);
    /** @var \Drupal\Core\Entity\Display\EntityDisplayInterface|null $display */
    $display = $this->entityTypeManager->getStorage($storageType)->load($displayId);
    return $display;
  }

  /**
   * Configures form display for a field.
   *
   * @param string $entityType
   *   The entity type ID.
   * @param string $bundle
   *   The bundle ID.
   * @param string $fieldName
   *   The field name.
   * @param string $widget
   *   The widget plugin ID.
   * @param array<string, mixed> $settings
   *   Widget settings.
   * @param int $weight
   *   Field weight in the form.
   * @param string $mode
   *   The form mode (default: 'default').
   *
   * @return array<string, mixed>|null
   *   The original component configuration for rollback, or NULL if none.
   */
  public function configureFormDisplay(
    string $entityType,
    string $bundle,
    string $fieldName,
    string $widget,
    array $settings = [],
    int $weight = 0,
    string $mode = 'default',
  ): ?array {
    $display = $this->loadOrCreateDisplay('entity_form_display', $entityType, $bundle, $mode);

    $originalComponent = $display->getComponent($fieldName);

    $display->setComponent($fieldName, [
      'type' => $widget,
      'settings' => $settings,
      'weight' => $weight,
      'third_party_settings' => [],
    ]);
    $display->save();

    return $originalComponent;
  }

  /**
   * Configures view display for a field.
   *
   * @param string $entityType
   *   The entity type ID.
   * @param string $bundle
   *   The bundle ID.
   * @param string $fieldName
   *   The field name.
   * @param string $formatter
   *   The formatter plugin ID.
   * @param array<string, mixed> $settings
   *   Formatter settings.
   * @param int $weight
   *   Field weight in the display.
   * @param string|null $labelDisplay
   *   Label display option (above, inline, hidden, visually_hidden).
   * @param string $mode
   *   The view mode (default: 'default').
   *
   * @return array<string, mixed>|null
   *   The original component configuration for rollback, or NULL if none.
   */
  public function configureViewDisplay(
    string $entityType,
    string $bundle,
    string $fieldName,
    string $formatter,
    array $settings = [],
    int $weight = 0,
    ?string $labelDisplay = NULL,
    string $mode = 'default',
  ): ?array {
    $display = $this->loadOrCreateDisplay('entity_view_display', $entityType, $bundle, $mode);

    $originalComponent = $display->getComponent($fieldName);

    $component = [
      'type' => $formatter,
      'settings' => $settings,
      'weight' => $weight,
      'third_party_settings' => [],
    ];

    if ($labelDisplay !== NULL) {
      $component['label'] = $labelDisplay;
    }

    $display->setComponent($fieldName, $component);
    $display->save();

    return $originalComponent;
  }

  /**
   * Hides a field in form display.
   *
   * @param string $entityType
   *   The entity type ID.
   * @param string $bundle
   *   The bundle ID.
   * @param string $fieldName
   *   The field name.
   * @param string $mode
   *   The form mode (default: 'default').
   *
   * @return array<string, mixed>|null
   *   The original component configuration for rollback, or NULL if none.
   */
  public function hideInFormDisplay(
    string $entityType,
    string $bundle,
    string $fieldName,
    string $mode = 'default',
  ): ?array {
    $display = $this->loadDisplay('entity_form_display', $entityType, $bundle, $mode);

    if (!$display) {
      return NULL;
    }

    $originalComponent = $display->getComponent($fieldName);
    $display->removeComponent($fieldName);
    $display->save();

    return $originalComponent;
  }

  /**
   * Hides a field in view display.
   *
   * @param string $entityType
   *   The entity type ID.
   * @param string $bundle
   *   The bundle ID.
   * @param string $fieldName
   *   The field name.
   * @param string $mode
   *   The view mode (default: 'default').
   *
   * @return array<string, mixed>|null
   *   The original component configuration for rollback, or NULL if none.
   */
  public function hideInViewDisplay(
    string $entityType,
    string $bundle,
    string $fieldName,
    string $mode = 'default',
  ): ?array {
    $display = $this->loadDisplay('entity_view_display', $entityType, $bundle, $mode);

    if (!$display) {
      return NULL;
    }

    $originalComponent = $display->getComponent($fieldName);
    $display->removeComponent($fieldName);
    $display->save();

    return $originalComponent;
  }

  /**
   * Restores form display component from rollback data.
   *
   * @param string $entityType
   *   The entity type ID.
   * @param string $bundle
   *   The bundle ID.
   * @param string $fieldName
   *   The field name.
   * @param array<string, mixed>|null $originalComponent
   *   The original component configuration, or NULL to remove.
   * @param string $mode
   *   The form mode (default: 'default').
   */
  public function restoreFormDisplay(
    string $entityType,
    string $bundle,
    string $fieldName,
    ?array $originalComponent,
    string $mode = 'default',
  ): void {
    $display = $this->loadDisplay('entity_form_display', $entityType, $bundle, $mode);

    if (!$display) {
      return;
    }

    if ($originalComponent) {
      $display->setComponent($fieldName, $originalComponent);
    }
    else {
      $display->removeComponent($fieldName);
    }
    $display->save();
  }

  /**
   * Restores view display component from rollback data.
   *
   * @param string $entityType
   *   The entity type ID.
   * @param string $bundle
   *   The bundle ID.
   * @param string $fieldName
   *   The field name.
   * @param array<string, mixed>|null $originalComponent
   *   The original component configuration, or NULL to remove.
   * @param string $mode
   *   The view mode (default: 'default').
   */
  public function restoreViewDisplay(
    string $entityType,
    string $bundle,
    string $fieldName,
    ?array $originalComponent,
    string $mode = 'default',
  ): void {
    $display = $this->loadDisplay('entity_view_display', $entityType, $bundle, $mode);

    if (!$display) {
      return;
    }

    if ($originalComponent) {
      $display->setComponent($fieldName, $originalComponent);
    }
    else {
      $display->removeComponent($fieldName);
    }
    $display->save();
  }

  /**
   * Updates an existing form display component.
   *
   * Merges new settings with existing configuration.
   *
   * @param string $entityType
   *   The entity type ID.
   * @param string $bundle
   *   The bundle ID.
   * @param string $fieldName
   *   The field name.
   * @param string|null $widget
   *   The widget plugin ID, or NULL to keep current.
   * @param array<string, mixed> $settings
   *   Widget settings to merge.
   * @param string $mode
   *   The form mode (default: 'default').
   *
   * @return array<string, mixed>|null
   *   The original component configuration for rollback, or NULL if not found.
   */
  public function updateFormDisplay(
    string $entityType,
    string $bundle,
    string $fieldName,
    ?string $widget = NULL,
    array $settings = [],
    string $mode = 'default',
  ): ?array {
    $display = $this->loadDisplay('entity_form_display', $entityType, $bundle, $mode);

    if (!$display) {
      return NULL;
    }

    $originalComponent = $display->getComponent($fieldName);

    if (!$originalComponent) {
      return NULL;
    }

    $component = $originalComponent;
    if ($widget) {
      $component['type'] = $widget;
    }
    if (!empty($settings)) {
      $component['settings'] = array_merge($component['settings'] ?? [], $settings);
    }

    $display->setComponent($fieldName, $component);
    $display->save();

    return $originalComponent;
  }

  /**
   * Updates an existing view display component.
   *
   * Merges new settings with existing configuration.
   *
   * @param string $entityType
   *   The entity type ID.
   * @param string $bundle
   *   The bundle ID.
   * @param string $fieldName
   *   The field name.
   * @param string|null $formatter
   *   The formatter plugin ID, or NULL to keep current.
   * @param array<string, mixed> $settings
   *   Formatter settings to merge.
   * @param string $mode
   *   The view mode (default: 'default').
   *
   * @return array<string, mixed>|null
   *   The original component configuration for rollback, or NULL if not found.
   */
  public function updateViewDisplay(
    string $entityType,
    string $bundle,
    string $fieldName,
    ?string $formatter = NULL,
    array $settings = [],
    string $mode = 'default',
  ): ?array {
    $display = $this->loadDisplay('entity_view_display', $entityType, $bundle, $mode);

    if (!$display) {
      return NULL;
    }

    $originalComponent = $display->getComponent($fieldName);

    if (!$originalComponent) {
      return NULL;
    }

    $component = $originalComponent;
    if ($formatter) {
      $component['type'] = $formatter;
    }
    if (!empty($settings)) {
      $component['settings'] = array_merge($component['settings'] ?? [], $settings);
    }

    $display->setComponent($fieldName, $component);
    $display->save();

    return $originalComponent;
  }

}
