<?php

namespace Drupal\nodehive_core\Form;

use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Url;
use Drupal\user\UserInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Provides a list builder for users.
 */
class UserListBuilderForm extends FormBase {

  /**
   * The entity type manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * The route match.
   *
   * @var \Drupal\Core\Routing\RouteMatchInterface
   */
  protected $routeMatch;

  /**
   * The module_handler service.
   *
   * @var \Drupal\Core\Extension\ModuleHandlerInterface
   */
  protected $moduleHandler;

  /**
   * Constructs a new SpaceUserListBuilderForm.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager.
   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
   *   The route match.
   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
   *   The module_handler service.
   */
  public function __construct(
    EntityTypeManagerInterface $entity_type_manager,
    RouteMatchInterface $route_match,
    ModuleHandlerInterface $module_handler,
  ) {
    $this->entityTypeManager = $entity_type_manager;
    $this->routeMatch = $route_match;
    $this->moduleHandler = $module_handler;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('entity_type.manager'),
      $container->get('current_route_match'),
      $container->get('module_handler')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'nodehive_core_space_dashboard_user_list_builder';
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    $form = [];

    // List builder.
    $header = [
      $this->t("Status"),
      $this->t("Picture"),
      $this->t("Username"),
      $this->t("E-mail"),
      $this->t("Last Login"),
      $this->t("Spaces"),
      $this->t("Contents"),
      $this->t("Role"),
      $this->t("Operations"),
    ];

    $users = $this->entityTypeManager
      ->getStorage('user')
      ->loadMultiple();

    $active_users = 0;
    $blocked_users = 0;

    /** @var \Drupal\user\UserInterface $user */
    foreach ($users as $user) {
      if ($user->isAnonymous() || $user->id() == 1) {
        continue;
      }

      if ($user->isBlocked()) {
        $blocked_users++;
      }
      else {
        $active_users++;
      }
    }

    $form['number_of_users'] = [
      '#type' => 'label',
      '#title' => $this->t("Number of all users: @number", ['@number' => ($active_users + $blocked_users)]),
      '#attributes' => [
        'class' => [
          'number-of-users',
        ],
      ],
    ];

    $form['number_of_users_active'] = [
      '#type' => 'label',
      '#title' => $this->t("Number of active users: @number", ['@number' => $active_users]),
      '#attributes' => [
        'class' => [
          'number-of-users',
        ],
      ],
    ];

    $form['number_of_users_blocked'] = [
      '#type' => 'label',
      '#title' => $this->t("Number of blocked users: @number", ['@number' => $blocked_users]),
      '#attributes' => [
        'class' => [
          'number-of-users',
        ],
      ],
    ];

    $form['users'] = [
      '#type' => 'table',
      '#header' => $header,
    ];

    $rows = [];

    /** @var \Drupal\user\UserInterface $user */
    foreach ($users as $user) {
      if ($user->isAnonymous() || $user->id() == 1) {
        continue;
      }

      $row = [];

      $row[] = $user->isBlocked() ? $this->t("Blocked") : $this->t("Active");

      $spaces = $this->entityTypeManager
        ->getStorage('nodehive_space')
        ->loadByProperties([
          'editors' => $user->id(),
        ]);

      $role_labels = [];
      $roles = $this->entityTypeManager->getStorage('user_role')->loadMultiple($user->getRoles());

      // We don't need to show this.
      if (isset($roles['authenticated'])) {
        unset($roles['authenticated']);
      }

      foreach ($roles as $role) {
        $role_labels[] = $role->label();
      }

      if (!$user->user_picture->isEmpty()) {
        $picture = $user->user_picture->entity->getFileUri();
      }
      else {
        $picture = '';
      }

      $row[] = [
        'data' => [
          '#theme' => 'image_style',
          '#style_name' => 'thumbnail',
          '#uri' => $picture,
        ],
      ];

      $row[] = $user->getAccountName();
      $row[] = $user->getEmail();
      $row[] = $this->timeElapsedString($user->getLastLoginTime());

      $space_labels = [];
      $i = 0;
      foreach ($spaces as $space) {
        $link = $space->toLink()->toRenderable();

        if ($i != 0) {
          $link['#prefix'] = ", ";
        }
        $space_labels[] = $link;
        $i++;
      }
      $row[] = ['data' => $space_labels];

      if ($this->moduleHandler->moduleExists('content_kanban')) {
        $user_id = $user->id();
        $kanban_content_url = Url::fromUserInput(
          "/admin/content-kanban?author=$user_id"
        );

        $row[] = [
          'data' => [
            '#type' => 'link',
            '#title' => $this->getNumberOfContent($user),
            '#url' => $kanban_content_url,
          ],
        ];
      }
      else {
        $row[] = $this->getNumberOfContent($user);
      }

      $row[] = implode(", ", $role_labels);
      $row[] = [
        'data' => [
          '#type' => 'operations',
          '#links' => $this->getDefaultOperations($user),
          '#attached' => [
            'library' => ['core/drupal.dialog.ajax'],
          ],
        ],
      ];

      $rows[] = $row;
    }

    $form['users']['#rows'] = $rows;

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
  }

  /**
   * Transforms a timestamp to time elapsed string.
   *
   * @param \DateTime|int $datetime
   *   The datetime or timestamp.
   *
   * @return string
   *   Time elapsed string.
   */
  public function timeElapsedString($datetime) {
    $now = new \DateTime();
    $datetime = (is_numeric($datetime) && $datetime > 0) ? $datetime : time();
    $ago = new \DateTime('@' . $datetime);
    $diff = $now->diff($ago);

    $weeks = floor($diff->d / 7);

    $timeUnits = [
      'y' => 'year',
      'm' => 'month',
      'w' => 'week',
      'd' => 'day',
      'h' => 'hour',
      'i' => 'minute',
      's' => 'second',
    ];

    foreach ($timeUnits as $unit => &$label) {
      if ($unit === 'w' && $weeks > 0) {
        $label = $weeks . ' ' . $label . ($weeks > 1 ? 's' : '');
      }
      elseif (@$diff->$unit) {
        $label = $diff->$unit . ' ' . $label . ($diff->$unit > 1 ? 's' : '');
      }
      else {
        unset($timeUnits[$unit]);
      }
    }

    $timeUnits = array_slice($timeUnits, 0, 1);

    return $timeUnits ? implode(', ', $timeUnits) . ' ago' : 'just now';
  }

  /**
   * Gets this number of content created by user.
   *
   * @param \Drupal\user\UserInterface $user
   *   The user entity.
   *
   * @return int
   *   Number of content.
   */
  public function getNumberOfContent(UserInterface $user) {
    $nodes = $this->entityTypeManager
      ->getStorage('node')
      ->loadByProperties([
        'uid' => $user->id(),
      ]);

    return count($nodes);
  }

  /**
   * Gets this list's default operations.
   *
   * @param \Drupal\user\UserInterface $user
   *   The user entity.
   *
   * @return array
   *   Array of operations.
   */
  protected function getDefaultOperations(UserInterface $user) {
    $operations = [];

    if ($this->currentUser()->hasPermission('administer nodehive users')) {
      $url = Url::fromRoute(
        'nodehive_core.edit_user_form',
        [
          'user' => $user->id(),
        ]
      );

      $operations['edit'] = [
        'title' => $this->t('Edit'),
        'weight' => 10,
        'url' => $this->ensureDestination($url),
      ];
    }

    $operations += $this->moduleHandler->invokeAll('entity_operation', [$user]);
    $this->moduleHandler->alter('entity_operation', $operations, $user);
    uasort($operations, '\Drupal\Component\Utility\SortArray::sortByWeightElement');

    // Add the space entity query to all operations.
    foreach ($operations as $key => $operation) {
      /** @var \Drupal\Core\Url $url */
      $url = $operation['url'];
      $url = $this->ensureDestination($url);
      $operations[$key]['url'] = $url;
    }

    return $operations;
  }

  /**
   * Ensures that a destination is present on the given URL.
   *
   * @param \Drupal\Core\Url $url
   *   The URL object to which the destination should be added.
   *
   * @return \Drupal\Core\Url
   *   The updated URL object.
   */
  protected function ensureDestination(Url $url) {
    return $url->mergeOptions(['query' => $this->getRedirectDestination()->getAsArray()]);
  }

}
