<?php

declare(strict_types=1);

namespace Drupal\node_revision_delete_protection\Controller;

use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Url;
use Drupal\node\Controller\NodeController;
use Drupal\node\NodeInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Decorates the node revision overview controller.
 */
class NodeRevisionOverviewController extends ControllerBase {

  /**
   * Constructs a NodeRevisionOverviewController object.
   *
   * @param \Drupal\Core\Database\Connection $connection
   *   The database connection.
   * @param \Drupal\node\Controller\NodeController $nodeController
   *   The original node controller.
   */
  public function __construct(
    protected Connection $connection,
    protected NodeController $nodeController,
  ) {
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container): self {
    return new self(
      $container->get('database'),
      NodeController::create($container),
    );
  }

  /**
   * Retrieves protected revisions for a given entity.
   *
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   The entity.
   *
   * @return array
   *   An array of protected revision IDs.
   */
  protected function getProtectedRevisions(EntityInterface $entity): array {
    $query = $this->connection->select('revision_protection', 'rp')
      ->fields('rp', ['revision_id'])
      ->condition('entity_type', $entity->getEntityTypeId())
      ->condition('entity_id', $entity->id());
    $result = $query->execute()->fetchCol();
    $result = array_map('intval', $result);
    return $result ?: [];
  }

  /**
   * Generates an overview table of older revisions of a node.
   *
   * @param \Drupal\node\NodeInterface $node
   *   A node object.
   *
   * @return array
   *   An array as expected by \Drupal\Core\Render\RendererInterface::render().
   */
  public function revisionOverview(NodeInterface $node): array {
    $data = $this->nodeController->revisionOverview($node);
    $protected = $this->getProtectedRevisions($node);
    foreach ($data['node_revisions_table']['#rows'] as $row_id => $row_data) {
      foreach ($row_data as $col_id => $col_data) {
        if (!empty($col_data['data']) && $col_data['data']['#type'] === 'operations') {
          // Remove the delete operation from the revision operations.
          $operations = &$data['node_revisions_table']['#rows'][$row_id][$col_id]['data']['#links'];
          if (!empty($operations['delete'])) {
            $route_params = $operations['delete']['url']->getRouteParameters();
            $nid = (int) $route_params['node'];
            $vid = (int) $route_params['node_revision'];

            if (in_array($vid, $protected, TRUE)) {
              $operations['release'] = [
                'title' => $this->t('Release protection'),
                'url' => Url::fromRoute('node_revision_delete_protection.release', [
                  'node' => $nid,
                  'node_revision' => $vid,
                ]),
              ];

              unset($operations['delete']);

              if (!empty($data['node_revisions_table']['#rows'][$row_id][0]['data']['#context'])) {
                $data['node_revisions_table']['#rows'][$row_id][0]['data']['#context']['message']['#markup'] .= $this->t('This revision is protected from deletion.');
              }
            }
            else {
              $operations['protect'] = [
                'title' => $this->t('Protect revision'),
                'url' => Url::fromRoute('node_revision_delete_protection.protect', [
                  'node' => $nid,
                  'node_revision' => $vid,
                ]),
              ];
              // Put the protect link before the delete link.
              $operations = [
                'revert' => $operations['revert'],
                'protect' => $operations['protect'],
              ] + $operations;
            }
          }
        }
      }
    }
    return $data;
  }

}
