<?php

namespace Drupal\nodehive_core_space_dashboard\Form;

use Drupal\Component\Serialization\Json;
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 in space.
 */
class SpaceUserListBuilderForm extends FormBase {

  /**
   * The space entity.
   *
   * @var \Drupal\nodehive_core\SpaceInterface
   */
  protected $space;

  /**
   * 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;

    $space = NULL;
    if ($space_id = $this->routeMatch->getParameter('nodehive_space')) {
      /** @var \Drupal\nodehive_core\SpaceInterface|null $space */
      $space = $this->entityTypeManager
        ->getStorage('nodehive_space')
        ->load($space_id);
    }

    if (!$space) {
      throw new \Exception("Missing nodehive_space entity.");
    }

    $this->space = $space;
  }

  /**
   * {@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 = [];

    // Add form.
    if ($this->currentUser()->hasPermission('administer nodehive users')) {
      $form['add_users'] = [
        '#type' => 'fieldset',
        '#title' => $this->t('Add existing users'),
      ];

      $form['add_users']['user'] = [
        '#type' => 'entity_autocomplete',
        '#target_type' => 'user',
        '#title' => $this->t('User'),
        '#description' => $this->t('Select a user to add to a space, separate multiple ones with comma.'),
        '#default_value' => NULL,
        '#selection_settings' => [
          'include_anonymous' => FALSE,
        ],
        '#required' => TRUE,
        '#tags' => TRUE,
      ];

      $form['add_users']['submit'] = [
        '#type' => 'submit',
        '#value' => $this->t('Add'),
        '#submit' => ['::addUserSubmit'],
      ];

      $invite_url = Url::fromRoute("nodehive_core.add_user_form");
      $form['invite_user_container'] = [
        '#type' => 'container',
        '#attributes' => [
          'class' => [
            'invite-user',
          ],
        ],
      ];

      $form['invite_user_container']['invite_user'] = [
        '#type' => 'link',
        '#title' => $this->t('Invite a new user'),
        '#url' => $this->ensureDestination($invite_url),
        '#attributes' => [
          'class' => [
            'button',
            'invite-user-btn',
          ],
        ],
      ];
    }

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

    $saved_users_values = $this->space->editors->getValue();
    $saved_users = [];
    foreach ($saved_users_values as $item) {
      $saved_users[$item['target_id']] = $item['target_id'];
    }

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

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

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

    $rows = [];

    /** @var \Drupal\user\UserInterface $user */
    foreach ($users as $user) {
      $row = [];

      $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());

      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;

    $form['#attached'] = [
      'library' => [
        'nodehive_core_space_dashboard/space_user_list_builder',
      ],
    ];

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function addUserSubmit(array &$form, FormStateInterface $form_state) {
    $user_value = $form_state->getValue('user');

    $saved_users_values = $this->space->editors->getValue();
    $saved_users = [];
    foreach ($saved_users_values as $item) {
      $saved_users[$item['target_id']] = $item['target_id'];
    }

    $users = [];
    foreach ($user_value as $item) {
      /** @var \Drupal\user\UserInterface $user */
      $user = $this->entityTypeManager
        ->getStorage('user')
        ->load($item['target_id']);

      if ($user->isAnonymous()) {
        continue;
      }

      if (in_array($user->id(), $saved_users)) {
        $this->messenger()->addWarning(
          $this->t(
            "User %name is already part of the space.",
            [
              '%name' => $user->getAccountName(),
            ]
          ),
        );
        return;
      }

      $users[$user->id()] = $user;
    }

    foreach ($users as $id => $user) {
      if (in_array($id, $saved_users)) {
        continue;
      }

      $saved_users[$id] = $id;
    }

    $this->space->editors = $saved_users;
    $this->space->save();

    $this->messenger()->addMessage(
      $this->t(
        "Users has been added successfully."
      ),
    );
  }

  /**
   * {@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(),
        'nodehive_space' => $this->space->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),
      ];
    }

    $is_nodehive_admin = FALSE;
    if (in_array('nodehive_content_admin', $this->currentUser()->getRoles())) {
      $is_nodehive_admin = TRUE;
    }

    if ($is_nodehive_admin || $this->currentUser()->id() == 1) {
      $url = Url::fromRoute(
        'nodehive_core_space_dashboard.remove_user_from_space_form',
        [
          'nodehive_space' => $this->space->id(),
          'user' => $user->id(),
        ]
      );
      $operations['remove'] = [
        'title' => $this->t('Remove from space'),
        'weight' => 100,
        'attributes' => [
          'class' => ['use-ajax'],
          'data-dialog-type' => 'modal',
          'data-dialog-options' => Json::encode([
            'width' => 880,
          ]),
        ],
        '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()]);
  }

}
