<?php

declare(strict_types=1);

namespace Drupal\display_builder_devel\Controller;

use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Datetime\DateFormatterInterface;
use Drupal\Core\Render\Markup;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Url;
use Drupal\display_builder_devel\Helper\DisplayBuilderDevelHelper;
use Drupal\display_builder_devel\MockEntity;
use Drupal\display_builder_entity_view\Entity\DisplayBuilderEntityViewDisplay;
use Drupal\display_builder_page_layout\Entity\PageLayout;
use Drupal\display_builder_views\Plugin\views\display_extender\DisplayBuilder as DisplayExtender;
use Drupal\display_builder\StateManager\StateManagerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;

/**
 * Returns responses for Display Builder ui routes.
 */
class DisplayBuilderDevelController extends ControllerBase {

  /**
   * Seconds in a day.
   */
  private const SECONDS_IN_A_DAY = 86400;

  public function __construct(
    private readonly StateManagerInterface $stateManager,
    private readonly DateFormatterInterface $dateFormatter,
  ) {}

  /**
   * Generate a simple index of saved display.
   *
   * @return array
   *   A render array.
   */
  public function index(): array {
    $build = [];
    $build['display_builder_table'] = [
      '#theme' => 'table',
      '#header' => [
        'id' => ['data' => $this->t('Instance')],
        'type' => ['data' => $this->t('Type')],
        'display_builder_config' => ['data' => $this->t('Config')],
        'updated' => ['data' => $this->t('Updated')],
        'log' => ['data' => $this->t('Last log')],
        'operations' => ['data' => $this->t('Operations')],
      ],
    ];

    foreach (\array_keys($this->stateManager->loadAll()) as $builder_id) {
      if (!$builder_id) {
        continue;
      }
      $build['display_builder_table']['#rows'][$builder_id] = $this->buildRow($builder_id, $this->stateManager->load($builder_id));
    }

    $build['pager'] = ['#type' => 'pager'];

    return $build;
  }

  /**
   * Provides a generic title callback for a display.
   *
   * @param string $builder_id
   *   The uniq display id.
   *
   * @return \Drupal\Core\StringTranslation\TranslatableMarkup|null
   *   The title for the display page, if found.
   */
  public function title(string $builder_id): ?TranslatableMarkup {
    if (empty($builder_id)) {
      return $this->t('Display builder');
    }

    return $this->t('Display builder: @id', ['@id' => $builder_id]);
  }

  /**
   * Load a display builder instance by id.
   *
   * @param string $builder_id
   *   The display builder instance id.
   *
   * @return array
   *   The display build.
   */
  public function view(string $builder_id): array {
    // \Drupal::service('plugin.cache_clearer')->clearCachedDefinitions();
    // \Drupal::service('page_cache_kill_switch')->trigger();
    $display_builder_id = $this->stateManager->getEntityConfigId($builder_id);

    $storage = $this->entityTypeManager()->getStorage('display_builder');
    /** @var \Drupal\display_builder\DisplayBuilderInterface $displayBuilderConfig */
    $displayBuilderConfig = $storage->load($display_builder_id);

    if ($displayBuilderConfig) {
      // @todo no contexts as it's a generic loader, but can fail if calling a
      // display with context.
      // $builder = $displayBuilderConfig->build($builder_id);
      // $builder['#cache']['max-age'] = 0;
      // return $builder;
      return $displayBuilderConfig->build($builder_id);
    }

    return [
      '#plain_text' => $this->t('Missing @builder_id config.', ['@builder_id' => $display_builder_id]),
    ];
  }

  /**
   * Delete a sample entity generated by UI Patterns.
   *
   * @param string $entity_type_id
   *   The entity type id.
   * @param string $bundle
   *   The bundle id.
   *
   * @return \Symfony\Component\HttpFoundation\RedirectResponse
   *   A message and redirect.
   */
  public function deleteSample(string $entity_type_id, string $bundle): RedirectResponse {
    /** @var \Drupal\ui_patterns\Entity\SampleEntityGenerator $sample */
    $sample = \Drupal::service('ui_patterns.sample_entity_generator'); // phpcs:ignore
    $sample->delete($entity_type_id, $bundle);

    $params = ['@entity_type_id' => $entity_type_id, '@bundle' => $bundle];
    $this->messenger()->addStatus($this->t('Sample @entity_type_id @bundle deleted!', $params));

    $redirect = $this->redirect('display_builder_devel.collection');
    $redirect->send();

    return $redirect;
  }

  /**
   * Builds a table row for a display builder.
   *
   * @param string $builder_id
   *   The builder id.
   * @param array $builder
   *   An builder to display.
   *
   * @return array
   *   A table row.
   */
  protected function buildRow(string $builder_id, array $builder): array {
    $row = [];

    $type = $this->t('None');
    $extra_links = [];
    $url = MockEntity::getUrlFromInstanceId($builder_id);

    // Simple switch to url based on context.
    if (\class_exists('Drupal\display_builder_views\Plugin\views\display_extender\DisplayBuilder') && $this->stateManager->hasSaveContextsRequirement($builder_id, DisplayExtender::getContextRequirement())) {
      $url = DisplayExtender::getUrlFromInstanceId($builder_id);
      $type = $this->t('Views');
    }
    elseif (\class_exists('Drupal\display_builder_page_layout\Entity\PageLayout') && $this->stateManager->hasSaveContextsRequirement($builder_id, PageLayout::getContextRequirement())) {
      $url = PageLayout::getUrlFromInstanceId($builder_id);
      $type = $this->t('Page layout');
    }
    elseif (\class_exists('Drupal\display_builder_entity_view\Entity\DisplayBuilderEntityViewDisplay') && $this->stateManager->hasSaveContextsRequirement($builder_id, DisplayBuilderEntityViewDisplay::getContextRequirement())) {
      $url = DisplayBuilderEntityViewDisplay::getUrlFromInstanceId($builder_id);
      $type = $this->t('Entity view');

      /** @var \Drupal\Core\Entity\EntityInterface $entity */
      $entity = $builder['contexts']['entity']->getContextValue();
      if ($entity) {
        $entity_type_id = $entity->getEntityTypeId();
        $bundle = $builder['contexts']['bundle']->getContextValue();

        $extra_links['refresh_sample'] = [
          'title' => $this->t('Refresh sample'),
          'url' => Url::fromRoute('display_builder_devel.delete_sample', [
            'entity_type_id' => $entity_type_id,
            'bundle' => $bundle,
          ]),
        ];
      }
    }

    $row['id']['data'] = [
      '#type' => 'link',
      '#title' => $builder_id,
      '#url' => $url,
    ];

    $row['type']['data'] = $type;

    $row['display_builder_config']['data'] = $builder['entity_config_id'];

    $present = $builder['present'];

    if (!$present) {
      $present = ['time' => NULL, 'log' => NULL];
    }
    $row['updated']['data'] = $present['time'] ? $this->formatTime((int) $present['time']) : '-';

    if (isset($present['log']) && $present['log'] instanceof TranslatableMarkup) {
      $row['log']['data'] = $this->formatLog($present['log']);
    }
    else {
      $row['log']['data'] = '-';
    }

    $links = DisplayBuilderDevelHelper::getOperationLinks($builder_id, $extra_links);

    if (\count($links) > 0) {
      $row['operations']['data']['operations'] = [
        '#type' => 'operations',
        '#links' => $links,
      ];
    }

    return ['data' => $row];
  }

  /**
   * Format the log.
   *
   * @param \Drupal\Core\StringTranslation\TranslatableMarkup $log
   *   The log to format.
   *
   * @return array
   *   The formatted log.
   */
  private function formatLog(TranslatableMarkup $log): array {
    return ['#markup' => Markup::create($log->render())];
  }

  /**
   * Print the date for humans.
   *
   * @param int $timestamp
   *   The timestamp integer.
   *
   * @return string
   *   The formatted date.
   */
  private function formatTime(int $timestamp): string {
    $delta = \time() - $timestamp;

    if ($delta < self::SECONDS_IN_A_DAY) {
      return $this->dateFormatter->format($timestamp, 'custom', 'G:i');
    }

    return $this->dateFormatter->format($timestamp, 'short');
  }

}
