<?php

/**
 * @file
 * Contains intercept_room_reservation.module.
 */

use Drupal\Core\Entity\Display\EntityFormDisplayInterface;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\TypedData\DataDefinition;
use Drupal\Core\Link;
use Drupal\Core\Url;
use Drupal\intercept_core\EntityImageThumbnailUriField;
use Drupal\intercept_room_reservation\Controller\RoomReservationController;
use Drupal\intercept_room_reservation\Form\EventNodeFormHelper;
use Drupal\node\NodeInterface;
use Drupal\views\ViewExecutable;
use Drupal\views\Plugin\views\query\QueryPluginBase;

/**
 * Implements hook_help().
 */
function intercept_room_reservation_help($route_name, RouteMatchInterface $route_match) {
  switch ($route_name) {
    // Main module help for the intercept_room_reservation module.
    case 'help.page.intercept_room_reservation':
      $output = '';
      $output .= '<h3>' . new TranslatableMarkup('About') . '</h3>';
      $output .= '<p>' . new TranslatableMarkup('Provides room reservation functionality for Intercept.') . '</p>';
      return $output;
  }
}

/**
 * Implements hook_jsonapi_ENTITY_TYPE_filter_access().
 */
function intercept_room_reservation_jsonapi_room_reservation_filter_access(EntityTypeInterface $entity_type, AccountInterface $account) {
  return ([
    // @todo CRL-163: The 'view referenced user room_reservation' permission does not yet exist.
    // Once it has been implemented, the AMONG_ALL should be commented out and AMONG_OWN deleted.
    // JSONAPI_FILTER_AMONG_ALL => AccessResult::allowedIfHasPermissions($account, ['view room_reservation', 'view referenced user room_reservation'], 'OR'),
    JSONAPI_FILTER_AMONG_ALL => AccessResult::allowedIfHasPermissions($account, ['view room_reservation'], 'OR'),
    JSONAPI_FILTER_AMONG_OWN => AccessResult::allowedIfHasPermission($account, 'view own room_reservation'),
  ]);
}

/**
 * Implements hook_ENTITY_TYPE_delete().
 */
function intercept_room_reservation_node_delete(NodeInterface $node) {
  if (!$node->bundle() == 'event') {
    return;
  }
  if (!\Drupal::hasService('intercept_core.reservation.manager')) {
    return;
  }
  if ($reservation = \Drupal::service('intercept_core.reservation.manager')->getEventReservation($node)) {
    $reservation->delete();
  }
}

/**
 * Implements hook_theme().
 */
function intercept_room_reservation_theme() {
  return [
    'room_reservation' => [
      'render element' => 'elements',
    ],
    'room_reservation_warnings' => [
      'variables' => ['warnings' => NULL],
    ],
    'room_reservation_scheduler' => [
      'variables' => ['content' => NULL],
    ],
    'room_reservation_selector' => [
      'variables' => ['content' => NULL],
    ],
    'room_reservation_reserve_calendar' => [
      'variables' => ['content' => NULL],
    ],
    'room_reservation_revision_summary' => [
      'variables' => [
        'current_revision_date' => NULL,
        'revisions_link' => NULL,
      ],
    ],
  ];
}

/**
 * Implements hook_mail().
 */
function intercept_room_reservation_mail($key, &$message, $params) {
  if (!empty($params['reservation_manager'])) {
    $params['reservation_manager']->buildEmail($key, $message, $params);
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function intercept_room_reservation_form_node_form_alter(&$form, FormStateInterface $form_state) {
  $node = $form_state->getFormObject()->getEntity();
  if ($node->bundle() == 'event') {
    \Drupal::service('class_resolver')
      ->getInstanceFromDefinition(EventNodeFormHelper::class)
      ->nodeFormAlter($form, $form_state);
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function intercept_room_reservation_form_views_exposed_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  $view = $form_state->get('view');
  if ($view->id() == 'intercept_room_reservations' && $view->current_display == 'page') {
    // Change the labels on the min and max date fields.
    $form['field_dates_value_wrapper']['field_dates_value']['min']['#title'] = new TranslatableMarkup('Dates between');
    $form['field_dates_value_wrapper']['field_dates_value']['max']['#title'] = new TranslatableMarkup('And');

    // Change the locations field to be a dropdown.
    $storage = Drupal::getContainer()->get('entity_type.manager')->getStorage('node');
    $query = \Drupal::entityQuery('node')
      ->accessCheck(FALSE)
      ->condition('status', 1)
      ->condition('type', 'location')
      ->sort('title');
    $nids = $query->execute();

    // If there are no nodes, move on.
    if (!$nids) {
      return FALSE;
    }

    // Start building out the options for our select list.
    $options = [];
    $nodes = $storage->loadMultiple($nids);

    // Specify the empty option for our select list.
    $options[''] = 'Any';

    // Push titles into select list.
    foreach ($nodes as $node) {
      $title = $node->getTitle();
      $options[$title] = $title;
    }

    // Start building out our new form element.
    $location_field = 'location';
    $form[$location_field]['#type'] = 'select';
    $form[$location_field]['#multiple'] = FALSE;

    // Add the $options from above to our select list.
    $form[$location_field]['#options'] = $options;
    unset($form[$location_field]['#size']);

    // Remove autocomplete from the reservation form's date fields.
    $form['date']['min']['#attributes']['autocomplete'] = 'off';
    $form['date']['max']['#attributes']['autocomplete'] = 'off';
  }
  elseif ($view->id() == 'intercept_export_room_reservations' && $view->current_display == 'page') {
    // Changes to room reservation export filters will go here.
  }
  elseif ($view->id() == 'intercept_rooms' && $view->current_display == 'default') {
    $form['staff_use']['#options'][1] = 'Staff';
    $form['staff_use']['#options'][0] = 'Public';
  }
  elseif ($view->id() == 'intercept_rooms' && $view->current_display == 'rooms') {
    $user = \Drupal::currentUser();

    // Check if the current user is logged in and is a customer.
    if ($user->isAuthenticated() && in_array('intercept_registered_customer', $user->getRoles()) ) {

      // Set the default value for the exposed filter.
      if (isset($form['location']) && empty($form_state->getUserInput()['location'])) {
        // Get the location of the user's last reservation made and set that as the default.
        $room_reservation_manager = \Drupal::service('intercept_core.reservation.manager');
        $last_reservation = $room_reservation_manager->getUserLastMadeReservation($user);

        if (!empty($last_reservation)) {
          $last_reservation = \Drupal::entityTypeManager()->getStorage('room_reservation')->load($last_reservation);
          $last_reservation_location = $last_reservation->get('field_room')->entity->get('field_location')->entity->id();
          // Make sure this location is still an available option in the exposed filter.
          if (isset($form['location']['#options'][$last_reservation_location])) {
            $form_state->setUserInput(['location' => [$last_reservation_location]]);
          }
        }
      }
    }
  }

  // Room Reserve Staff Calendar View.
  if (in_array($form['#id'], [
    'views-exposed-form-intercept-rooms-default',
    'views-exposed-form-intercept-rooms-rooms',
  ])) {
    if (isset($form['min-capacity']) && isset($form['max-capacity'])) {
      // Hide min-capacity as we will set the value to match max capacity
      // to mimic an IS_BETWEEN query. See: intercept_room_reservation_views_pre_view().
      $form['min-capacity']['#type'] = 'hidden';
      $form['min-capacity']['#attributes'] = [
        'class' => ['visually-hidden'],
      ];
    }
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function intercept_room_reservation_form_room_reservation_update_status_form_alter(&$form, $form_state, $form_id) {
  $arguments = $form['#title']->getArguments();
  $operation = $arguments['@action'];
  if ($operation == 'cancel') {
    // Rewrite the confirmation message to match the customer confirmation.
    $form['description'] = [
      '#markup' => '<p>' . new TranslatableMarkup('Are you sure you want to cancel this reservation?') . '</p>',
    ];
    // Add margin to the right of the first action button.
    $form['actions']['cancel']['#prefix'] = '<div class="actions">';
    $form['actions']['submit']['#suffix'] = '</div>';
    $form['actions']['cancel']['#attached'] = [
      'library' => ['intercept_room_reservation/roomReservationActionButton'],
    ];
    // Change the titles and position of the buttons.
    $form['actions']['cancel']['#title'] = 'No';
    $form['actions']['cancel']['#weight'] = 4;
    $form['actions']['submit']['#value'] = 'Yes';
    $form['actions']['submit']['#weight'] = 5;
  }
}

/**
 * Implements hook_page_attachments().
 */
function intercept_room_reservation_page_attachments(array &$page) {
  $certifications = [];
  $current_user = \Drupal::currentUser();
  $roles = $current_user->getRoles();
  if (in_array('intercept_registered_customer', $roles) && \Drupal::moduleHandler()->moduleExists('intercept_certification')) {
    $entity_ids = \Drupal::entityQuery('certification')
      ->accessCheck(FALSE)
      ->condition('field_user', $current_user->id())
      ->execute();
    $certs = \Drupal::entityTypeManager()
      ->getStorage('certification')
      ->loadMultiple($entity_ids);
    foreach ($certs as $cert) {
      $room = $cert->field_room->entity->id();
      $certifications[] = $room;
    }
    $page['#attached']['drupalSettings']['userCertifications'] = $certifications;
  }
  else {
    $page['#attached']['drupalSettings']['userCertifications'] = 'all';
  }
  $current_path = \Drupal::service('path.current')->getPath();
  if ($current_path == '/user/' . $current_user->id()) {
    // Attach any necessary drupalSettings here.
    $room_reservation_settings = \Drupal::config('intercept_room_reservation.settings');
    $refreshments_text = $room_reservation_settings->get('refreshments_text');
    $page['#attached']['drupalSettings']['intercept']['room_reservations'] = [
      'refreshments_text' => strip_tags($refreshments_text['value']),
    ];
  }

  // Allow other modules to attach settings using a new hook:
  // hook_intercept_room_reservation_settings_alter().
  \Drupal::moduleHandler()->invokeAll('intercept_room_reservation_settings_alter', [&$page]);

}

/**
 * Implements hook_form_alter().
 */
function intercept_room_reservation_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  switch ($form_id) {
    case 'room_reservation_add_form':
      // Don't alter the form if the user cannot reserve the room.
      if (array_key_exists('user_cannot_reserve_room', $form)) {
        return;
      }

      $current_user = \Drupal::currentUser();
      $roles = $current_user->getRoles();
      $room = $form_state->getValue('field_room');
      if (in_array('intercept_registered_customer', $roles)) {
        $form['field_room']['widget'][0]['target_id']['#attributes']['readonly'][] = 'readonly';
      }
      // The default value on the field needs to be requested,
      // but if a staff member is using the reservation add form
      // let's default to "approved" and just make the field be required.
      if (!$form_state->getValue('field_status')) {
        $form['field_status']['widget']['#default_value'][0] = 'approved';
      }

      if ($form['field_refreshments_description']) {
        $form['field_refreshments_description']['#states']['visible'] = [
          ':input[name="field_refreshments"]' => ['value' => '1'],
        ];
        $form['field_refreshments_description']['#states']['required'] = [
          ':input[name="field_refreshments"]' => ['value' => '1'],
        ];
      }

      $form_display = $form_state->getStorage()['form_display']->getMode();
      if ($form_display === 'customer_reserve') {
        // Hide a few items for customers.
        if ($form['revision_log_message']) {
          $form['revision_log_message']['#type'] = 'hidden';
        }
        if ($form['field_user']) {
          $form['field_user']['#type'] = 'hidden';
        }
        if ($form['author']) {
          $form['author']['#type'] = 'hidden';
        }
      }
      // Add any JS form tweaks here.
      $form['#attached']['library'][] = 'intercept_room_reservation/room_reservation_form_helper';
      break;
  }
}

/**
 * Implements hook_field_widget_single_element_form_alter().
 */
function intercept_room_reservation_field_widget_single_element_form_alter(&$element, FormStateInterface $form_state, $context) {
  // Removes the text format options from field_refreshments_description.
  $map = [
    'field_refreshments_description' => ['basic_html'],
  ];

  $field_name = $context['items']->getFieldDefinition()->getName();

  if (array_key_exists($field_name, $map)) {
    $element['#allowed_formats'] = $map[$field_name];
    $element['#after_build'][] = '_remove_text_format_box';
  }
}

/**
 * #after_build callback.
 */
function _remove_text_format_box($form_element, FormStateInterface $form_state) {
  // Remove help, guidelines and wrapper.
  unset($form_element['format']['help']);
  unset($form_element['format']['guidelines']);
  unset($form_element['format']['#type']);
  unset($form_element['format']['#theme_wrappers']);

  return $form_element;
}

/**
 * Prepares variables for Room reservation templates.
 *
 * Default template: room_reservation.html.twig.
 *
 * @param array $variables
 *   An associative array containing:
 *   - elements: An associative array containing the user information and any
 *   - attributes: HTML attributes for the containing element.
 */
function template_preprocess_room_reservation(array &$variables) {
  // Fetch RoomReservation Entity Object.
  $room_reservation = $variables['room_reservation'] = $variables['elements']['#room_reservation'];
  $variables['view_mode'] = $variables['elements']['#view_mode'];
  // Helpful $content variable for templates.
  foreach (Element::children($variables['elements']) as $key) {
    $variables['content'][$key] = $variables['elements'][$key];
  }
  $variables['label'] = $room_reservation->label();

  // Add edit link.
  if ($room_reservation->access('update')) {
    $edit_link = Link::createFromRoute(t('Edit'), 'intercept_room_reservation.reservation.edit', [
      'room_reservation' => $room_reservation->id(),
      // 'destination' => Url::fromRoute('<current>')->toString(),
      // 'destination' => Url::fromRoute('entity.room_reservation.canonical', ['room_reservation' => $room_reservation->id()])->toString(),
    ]);
    $edit_link = $edit_link->toRenderable();
    $edit_link['#attributes'] = [
      'class' => [
        'use-ajax',
        'button',
      ],
      'data-dialog-type' => 'dialog',
      'data-dialog-options' => '{"width": "400"}',
      'data-dialog-renderer' => 'off_canvas',
    ];
    $variables['edit_link'] = $edit_link;
  }

  // Add to my Calendar
  $data_calendars = [
    'Google Calendar',
    'Outlook Online',
    'iCalendar',
    'Yahoo! Calendar',
  ];
  $service = \Drupal::service('addtocalendar.apiwidget');
  $room = $room_reservation->get('field_room')->entity;
  $room_name = $room->label();
  $location_name = $room->get('field_location')->entity->label();
  $title = $room_name . ' Reservation';
  $location = $room_name . ', ' . $location_name;
  $start_date_value = $room_reservation->get('field_dates')->value;
  $end_date_value = $room_reservation->get('field_dates')->end_value;
  $start_date = \DateTime::createFromFormat('Y-m-d\TH:i:s', $start_date_value)->format('Ymd\THis\Z');
  $end_date = \DateTime::createFromFormat('Y-m-d\TH:i:s', $end_date_value)->format('Ymd\THis\Z');
  $reservationManager = \Drupal::service('intercept_core.reservation.manager');
  $blocked_dates = @$reservationManager->getBlockedDates([$room_reservation], [], $room);
  $message = $blocked_dates[0]['message'];
  if (!empty($message)) {
    $title = $message;
  }
  $config_values = [
    'atcStyle' => 'none',
    'atcDisplayText' => 'Add to my Calendar',
    'atcTitle' => $title,
    'atcDescription' => '',
    'atcLocation' => $location,
    'atcDateStart' => $start_date,
    'atcDateEnd' => $end_date,
    'atcPrivacy' => 'public',
    'atcDataSecure' => 'auto',
    'atcDataCalendars' => $data_calendars,
  ];
  $service->setWidgetValues($config_values);
   // Only show this on future reservations.
  if (strtotime($end_date_value) > strtotime('now')) {
    $add_to_calendar_widget = $service->generateWidget();
    $variables['addtocalendar'] = \Drupal::service('renderer')->render($add_to_calendar_widget);
  }

}

/**
 * Implements hook_theme_suggestions_HOOK().
 */
function intercept_room_reservation_theme_suggestions_room_reservation(array $variables) {
  $suggestions = [];
  $room_reservation = $variables['elements']['#room_reservation'];
  $sanitized_view_mode = strtr($variables['elements']['#view_mode'], '.', '_');

  $suggestions[] = 'room_reservation__' . $sanitized_view_mode;
  $suggestions[] = 'room_reservation__' . $room_reservation->bundle();
  $suggestions[] = 'room_reservation__' . $room_reservation->bundle() . '__' . $sanitized_view_mode;

  return $suggestions;
}

/**
 * Implements hook_entity_extra_field_info().
 */
function intercept_room_reservation_entity_extra_field_info() {
  $extra = [];

  $extra['node']['event']['form']['reservation'] = [
    'label' => new TranslatableMarkup('Reservations'),
    'description' => new TranslatableMarkup('Manage reservations for this event.'),
    'visible' => FALSE,
  ];

  $extra['room_reservation']['room_reservation']['display']['location_full'] = [
    'label' => new TranslatableMarkup('Full location'),
    'description' => new TranslatableMarkup('Location title and room name.'),
    'weight' => 10,
    'visible' => FALSE,
  ];

  $extra['room_reservation']['room_reservation']['display']['attendees_full'] = [
    'label' => new TranslatableMarkup('Attendee count'),
    'description' => new TranslatableMarkup('Attendee count and label.'),
    'weight' => 15,
    'visible' => FALSE,
  ];

  $extra['room_reservation']['room_reservation']['display']['action_button'] = [
    'label' => new TranslatableMarkup('Action button'),
    'description' => new TranslatableMarkup('Room reservation status change buttons.'),
    'weight' => 15,
    'visible' => FALSE,
  ];
  return $extra;
}

/**
 * Implements hook_entity_bundle_field_info().
 */
function intercept_room_reservation_entity_bundle_field_info(EntityTypeInterface $entity_type, $bundle, array $base_field_definitions) {
  $fields = [];
  if ($entity_type->id() == 'node' && $bundle == 'room') {
    // @todo Make this configurable by privileged user accounts.
    $fields['room_thumbnail'] = BaseFieldDefinition::create('intercept_computed')
      ->setLabel(new TranslatableMarkup('Image Thumbnail'))
      ->setComputed(TRUE)
      ->setSetting('properties', [
        'thumbnail' => DataDefinition::create('string')
          ->setLabel(t('Image Thumbnail')),
      ])
      ->setClass(EntityImageThumbnailUriField::class);
  }
  return $fields;
}

/**
 * Implements hook_entity_form_display_alter().
 */
function intercept_room_reservation_entity_form_display_alter(EntityFormDisplayInterface &$form_display, array $context) {
  // Use the customer_reserve form display mode for customers.
  if ($context['entity_type'] == 'room_reservation' && ($context['form_mode'] == 'add' || $context['form_mode'] == 'edit')) {
    $user = \Drupal::currentUser();
    $roles = $user->getRoles();
    $storage = \Drupal::service('entity_type.manager')->getStorage('entity_form_display');
    if (in_array('intercept_registered_customer', $roles)) {
      $form_display = $storage->load('room_reservation.room_reservation.customer_reserve');
    }
    else {
      $form_display = $storage->load('room_reservation.room_reservation.default');
    }
  }
}

/**
 * Implements hook_views_query_alter().
 */
function intercept_room_reservation_views_query_alter(ViewExecutable $view, QueryPluginBase $query) {
  if ($view->id() == 'intercept_room_reservations' && $view->current_display == 'page') {
    foreach ($query->where as &$condition_group) {
      // For each condition in condition group.
      foreach ($condition_group['conditions'] as &$condition) {
        if (isset($condition['field']) && strstr($condition['field'], 'field_dates_value')) {

          // $multi_date_reservation_or = $condition['field'];
          // Run the query from the beginning of the start date
          // until the end of the end date.
          $condition['field'] = preg_replace("/(?<=BETWEEN DATE_FORMAT\(\('\d{4}-\d{2}-\d{2}T)\d{2}:\d{2}:\d{2}' \+ INTERVAL [-+]?\d* SECOND\)/", "00:00:00')", $condition['field']);
          $condition['field'] = preg_replace("/(?<=AND DATE_FORMAT\(\('\d{4}-\d{2}-\d{2}T)\d{2}:\d{2}:\d{2}' \+ INTERVAL [-+]?\d* SECOND\)/", "23:59:59')", $condition['field']);

          // // We need to account for the user asking for a time period
          // // in the middle of a multi-day reservation.
          // preg_match("/BETWEEN DATE_FORMAT\(\('(\d{4}-\d{2}-\d{2})T\d{2}:\d{2}:\d{2}' \+ (INTERVAL [-+]?\d* SECOND)\).*AND DATE_FORMAT\(\('(\d{4}-\d{2}-\d{2})/", $multi_date_reservation_or, $matches);
          // // Need 3 captures from the original value of the condition.
          // // 1) Start date, 2) Interval and 3) end date
          // // Need # seconds exactly.
          // preg_match("/INTERVAL ([-+]?\d*) SECOND/", $matches[2], $matches_seconds);
          // if ($matches_seconds) {
          //   if ($matches_seconds[1] < 0) {
          //     // The number is negative.
          //     $start_date = date('Y-m-d\TH:i:s', strtotime($matches[1] . 'T00:00:00') + abs($matches_seconds[1]));
          //     $end_date = date('Y-m-d\TH:i:s', strtotime($matches[3] . 'T00:00:00') + abs($matches_seconds[1]));
          //   }
          //   else {
          //     // The number is positive.
          //     $start_date = date('Y-m-d\TH:i:s', strtotime($matches[1] . 'T00:00:00') - abs($matches_seconds[1]));
          //     $end_date = date('Y-m-d\TH:i:s', strtotime($matches[3] . 'T00:00:00') - abs($matches_seconds[1]));
          //   }
          //   $multi_date_reservation_or = "DATE_FORMAT((room_reservation__field_dates.field_dates_value), '%Y-%m-%d\T%H:%i:%s') <= DATE_FORMAT(('" . $start_date . "'), '%Y-%m-%d\T%H:%i:%s') AND DATE_FORMAT((room_reservation__field_dates.field_dates_end_value), '%Y-%m-%d\T%H:%i:%s') >= DATE_FORMAT(('" . $end_date . "'), '%Y-%m-%d\T%H:%i:%s')";
          //   $condition['field'] = '(' . $condition['field'] . ') OR (' . $multi_date_reservation_or . ')';
          // }
        }
      }
    }
  }

  if ($view->id() == 'intercept_rooms' && in_array($view->current_display, ['entity_reference_1', 'rooms'])) {
    $current_user = \Drupal::currentUser();

    // Filter out any staff use only rooms if the user can't reserve them.
    if (!$current_user->hasPermission('view staff use room reservation')) {
      $query->addTable('node__field_staff_use_only');
      $query->addWhere('permissions', 'node__field_staff_use_only.field_staff_use_only_value', 0, '=');
    }
  }
}

/**
 * Implements hook_views_pre_view().
 */
function intercept_room_reservation_views_pre_view(ViewExecutable $view, $display_id, array &$args) {
  if ($view->id() == 'intercept_export_room_reservations' && $display_id == 'page') {
    // Build the link to download the CSV.
    $request = \Drupal::request();
    $link_renderable = Link::createFromRoute('Download CSV', 'view.intercept_export_room_reservations.rest_export', ['_format' => 'csv'] + $request->query->all())->toRenderable();
    $link_renderable['#attributes'] = ['class' => ['button', 'create-content-button']];
    $link = \Drupal::service('renderer')->renderPlain($link_renderable);
    $options = [
      'id' => 'area_text_custom',
      'table' => 'views',
      'field' => 'area_text_custom',
      'relationship' => 'none',
      'group_type' => 'none',
      'admin_label' => '',
      'empty' => TRUE,
      'tokenize' => FALSE,
      'content' => '<h1 class="page-title">Export Room Reservations</h1>' .
      $link,
      'plugin_id' => 'text_custom',
    ];
    $view->setHandler('page', 'header', 'area_text_custom', $options);
  }

  switch ($view->id()) {
    case 'intercept_room_reservations':
      switch ($display_id) {
        case 'page':
          $exposed_input = $view->getExposedInput();
          if (!isset($exposed_input['field_dates_value'])) {
            $date = new DateTime('now');
            $date_future = new DateTime('+6 months');
            $exposed_date['field_dates_value']['min'] = $date->format('Y-m-d');
            $exposed_date['field_dates_value']['max'] = $date_future->format('Y-m-d');
            $view->setExposedInput(array_merge($exposed_input, $exposed_date));
          }

          // Attach JS.
          $view->element['#attached']['library'][] = 'intercept_room_reservation/roomReservationList';
          break;
        default:
          break;
      }
      break;
    case 'intercept_rooms':
      switch ($display_id) {
        case 'default':
        case 'rooms':
          $filters = $view->getExposedInput();

          // Match min-capacity to max-capacity to mimic an IS_BETWEEN query.
          if (isset($filters['max-capacity'])) {
            $filters['min-capacity'] = $filters['max-capacity'];
            $view->setExposedInput($filters);
          }
          break;

        default:
          break;
      }
      break;

    default:
      break;
  }
}

/**
 * Allowed values callback function for room reservation status field.
 *
 * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $definition
 *   The field storage definition.
 * @param \Drupal\Core\Entity\FieldableEntityInterface|null $entity
 *   (optional) The entity context if known, or NULL if the allowed values are
 *   being collected without the context of a specific entity.
 * @param bool &$cacheable
 *   (optional) If an $entity is provided, the $cacheable parameter should be
 *   modified by reference and set to FALSE if the set of allowed values
 *   returned was specifically adjusted for that entity and cannot not be reused
 *   for other entities. Defaults to TRUE.
 *
 * @return array
 *   The array of allowed values.
 */
function intercept_room_reservation_status_values(FieldStorageDefinitionInterface $definition, FieldableEntityInterface $entity = NULL, &$cacheable = FALSE) {
  $options = [
    'requested' => new TranslatableMarkup('Requested'),
    'approved' => new TranslatableMarkup('Approved'),
    'denied' => new TranslatableMarkup('Denied'),
    'canceled' => new TranslatableMarkup('Canceled'),
    'archived' => new TranslatableMarkup('Archived'),
  ];

  return $options;
}

/**
 * Implements hook_preprocess node().
 */
function intercept_room_reservation_preprocess_node(&$variables) {
  $node = $variables['node'];
  if ($node->getType() === 'room') {
    $variables['is_certified'] = FALSE;
    $view_mode = $variables['view_mode'];
    if ($view_mode === 'off_canvas') {
      $nid = $node->id();
      $current_uid = \Drupal::currentUser()->id();
      $query = \Drupal::entityQuery('certification')
        ->accessCheck(FALSE)
        ->condition('field_user', $current_uid)
        ->condition('field_room', $nid);
      $entity_ids = $query->execute();
      $storage = \Drupal::entityTypeManager()->getStorage('certification');
      $certification = $storage->loadMultiple($entity_ids);
      if ($certification) {
        $variables['is_certified'] = TRUE;
      }
      $room = $nid;
      $uid = $current_uid;
      if ($room && $uid) {

        // If intercept_certification is installed, see if the current user is
        // certified to reserve this room.
        $moduleHandler = \Drupal::moduleHandler();
        $userIsCertified = TRUE;
        if ($moduleHandler->moduleExists('intercept_certification')) {
          $userIsCertified = (!empty($room)) ? \Drupal::service('intercept_room_reservation.certification_checker')->userIsCertified($uid, $room) : TRUE;
        }

        if (empty($userCanReserveRoom) && !empty($room)) {
          $node = \Drupal::entityTypeManager()->getStorage('node')->load($room);
          // Check permissions to reserve this room.
          $userCanReserveRoom = userCanReserveRoom($node, $userIsCertified);
        }
        if ($userCanReserveRoom == TRUE) {
          $variables['reservation_link'] = Link::createFromRoute('Reserve', 'entity.room_reservation.add_form', [
              'room' => $node->id(),
            ], [
            'attributes' => [
              'class' => ['button button-action button--primary action-button__button action-button__button--outlined action-button__button--small use-ajax'],
              'data-dialog-type' => 'dialog',
              'data-dialog-options' => '{"width": "400"}',
              'data-dialog-renderer' => 'off_canvas',
            ],
          ]);
        }
      }

      if (empty($uid)) {
        $variables['reservation_link'] = Link::createFromRoute('Reserve', 'user.login', [
          'destination' => Url::fromRoute('intercept_room_reservation.reserve_room_calendar')->toString(),
        ], [
          'attributes' => [
            'class' => ['button button-action button--primary action-button__button action-button__button--outlined action-button__button--small'],
          ],
        ]);
      }

    }
  }
}

/**
 * Mimics userCanReserveRoom function from RoomReserveApp Step 1 (JS)
 */
function userCanReserveRoom($node, $userIsCertifiedForRoom) {
  $current_user = \Drupal::currentUser();
  $roles = $current_user->getRoles();
  $userIsManager = $userIsStaff = FALSE;
  if (in_array('intercept_event_manager', $roles) || in_array('intercept_staff', $roles) || in_array('intercept_system_admin', $roles) || in_array('intercept_room_manager', $roles)) {
    $userIsManager = TRUE;
  }
  if (in_array('intercept_staff', $roles)) {
    $userIsStaff = TRUE;
  }
  // Is the room reservable online?
  $reservable = (bool) $node->get('field_reservable_online')->value;
  // Requires certification?
  $moduleHandler = \Drupal::moduleHandler();
  if ($moduleHandler->moduleExists('intercept_certification')) {
    $mustCertify = (bool) $node->get('field_requires_certification')->value;
  }
  else {
    $mustCertify = FALSE;
  }
  // A user can reserve this room if...
  // they are a manager OR...
  if ($userIsManager == TRUE) {
    return TRUE;
  }
  // The room is reservable and they are staff OR...
  if ($reservable && $userIsStaff) {
    return TRUE;
  }
  // ...the room is reservable, requires certification and they are certified OR...
  if ($reservable && $mustCertify && $userIsCertifiedForRoom) {
    return TRUE;
  }
  // ...the room is reservable and does not require certification.
  if ($reservable && !$mustCertify) {
    return TRUE;
  }

  return FALSE;
}

/**
 * Implements hook_entity_bundle_field_info_alter().
 */
function intercept_room_reservation_entity_bundle_field_info_alter(&$fields, EntityTypeInterface $entity_type, $bundle) {
  if ($bundle === 'event') {
    if (isset($fields['field_capacity_max'])) {
      // Use the ID as defined in the annotation of the constraint definition.
      $fields['field_capacity_max']->addConstraint('MaxCapacity', []);
    }
  }
}

/**
 * Implements hook_intercept_room_reservation_settings_alter().
 */
function intercept_room_reservation_intercept_room_reservation_settings_alter(&$build) {
  // Here we'll set eligibility to all, but other modules can override this.
  $build['#attached']['drupalSettings']['intercept']['room_reservations']['eligibility_text'] = '';
  $build['#attached']['drupalSettings']['intercept']['room_reservations']['eligibility'] = 'all';
}
