<?php

namespace Drupal\eb\Plugin\EbOperation;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Menu\MenuLinkManagerInterface;
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\menu_link_content\Entity\MenuLinkContent;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Operation for creating menu links.
 */
#[EbOperation(
  id: 'create_menu_link',
  label: new TranslatableMarkup('Create Menu Link'),
  description: new TranslatableMarkup('Creates a new menu link in a menu'),
  operationType: 'create',
)]
class CreateMenuLinkOperation extends OperationBase {

  /**
   * Constructor.
   */
  public function __construct(
    array $configuration,
    string $plugin_id,
    mixed $plugin_definition,
    EntityTypeManagerInterface $entityTypeManager,
    LoggerInterface $logger,
    ConfigFactoryInterface $configFactory,
    protected MenuLinkManagerInterface $menuLinkManager,
  ) {
    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('plugin.manager.menu.link'),
    );
  }

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

    // Validate required fields.
    $this->validateRequiredFields(['menu_id', 'title', 'link'], $result);

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

    // Note: Menu existence is validated by DependencyValidator plugin,
    // which has batch context awareness to handle dependencies within
    // the same batch. We don't validate it here to avoid duplication.
    // Validate link array has uri.
    $link = $this->getDataValue('link', []);
    if (!is_array($link) || empty($link['uri'])) {
      $result->addError(
        $this->t('Link must be an array with a "uri" key.'),
        'invalid_link'
      );
    }

    return $result;
  }

  /**
   * {@inheritdoc}
   */
  public function preview(): PreviewResult {
    $menu_id = $this->getDataValue('menu_id');
    $title = $this->getDataValue('title');
    $link = $this->getDataValue('link', []);
    $description = $this->getDataValue('description', '');
    $parent = $this->getDataValue('parent', '');
    $weight = $this->getDataValue('weight', 0);
    $expanded = $this->getDataValue('expanded', FALSE);

    $preview = new PreviewResult();
    $preview->addOperation(
      'create',
      'menu_link_content',
      $title,
      $this->t('Create menu link "@title" in menu "@menu"', [
        '@title' => $title,
        '@menu' => $menu_id,
      ])
    );

    $details = [
      'Menu' => $menu_id,
      'Title' => $title,
      'URL' => $link['uri'] ?? '',
    ];

    if (!empty($description)) {
      $details['Description'] = $description;
    }

    if (!empty($parent)) {
      $details['Parent'] = $parent;
    }

    if ($weight !== 0) {
      $details['Weight'] = $weight;
    }

    if ($expanded) {
      $details['Expanded'] = 'Yes';
    }

    $preview->addDetails($details);

    return $preview;
  }

  /**
   * {@inheritdoc}
   */
  public function execute(): ExecutionResult {
    $menu_id = $this->getDataValue('menu_id');
    $title = $this->getDataValue('title');
    $link = $this->getDataValue('link', []);
    $description = $this->getDataValue('description', '');
    $parent = $this->getDataValue('parent', '');
    $weight = $this->getDataValue('weight', 0);
    $expanded = $this->getDataValue('expanded', FALSE);
    $enabled = $this->getDataValue('enabled', TRUE);

    return $this->executeWithErrorHandling(function () use ($menu_id, $title, $link, $description, $parent, $weight, $expanded, $enabled) {
      // Create menu link values.
      $values = [
        'title' => $title,
        'link' => $link,
        'menu_name' => $menu_id,
        'weight' => $weight,
        'expanded' => $expanded,
        'enabled' => $enabled,
      ];

      if (!empty($description)) {
        $values['description'] = $description;
      }

      if (!empty($parent)) {
        $values['parent'] = $parent;
      }

      // Create the menu link.
      $menu_link = MenuLinkContent::create($values);
      $menu_link->save();

      $result = new ExecutionResult(TRUE);
      $result->addMessage($this->t('Menu link "@title" created successfully.', [
        '@title' => $title,
      ]));

      $result->addAffectedEntity([
        'type' => 'menu_link_content',
        'id' => $menu_link->id(),
        'label' => $title,
      ]);

      $result->setRollbackData([
        'menu_link_id' => $menu_link->id(),
      ]);

      return $result;
    }, 'create menu link');
  }

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

    return $this->rollbackWithErrorHandling(function () use ($rollback_data) {
      $menu_link_id = $rollback_data['menu_link_id'] ?? NULL;

      if (!$menu_link_id) {
        $result = new RollbackResult(FALSE);
        $result->addMessage($this->t('No menu link ID found for rollback.'));
        return $result;
      }

      // Load and delete the menu link.
      $menu_link = $this->entityTypeManager
        ->getStorage('menu_link_content')
        ->load($menu_link_id);

      if ($menu_link) {
        $title = $menu_link->getTitle();
        $menu_link->delete();

        $result = new RollbackResult(TRUE);
        $result->addMessage($this->t('Menu link "@title" deleted successfully.', [
          '@title' => $title,
        ]));
        $result->addRestoredEntity([
          'type' => 'menu_link_content',
          'id' => $menu_link_id,
        ]);
        $this->logInfo('Rolled back menu link creation: @id', ['@id' => $menu_link_id]);
      }
      else {
        $result = new RollbackResult(FALSE);
        $result->addMessage($this->t('Menu link @id not found for rollback.', [
          '@id' => $menu_link_id,
        ]));
      }

      return $result;
    }, 'menu link creation');
  }

}
