<?php

/**
 * @file
 * Defines common functionality for intercept_event.
 */

use Drupal\Core\Access\AccessResult;
use Drupal\Core\Block\BlockPluginInterface;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Link;
use Drupal\Core\Render\Markup;
use Drupal\Core\Routing\LocalRedirectResponse;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\TypedData\DataDefinition;
use Drupal\Core\Url;
use Drupal\Component\Datetime\DateTimePlus;
use Drupal\node\Entity\Node;
use Drupal\node\NodeInterface;
use Drupal\user\Entity\User;
use Drupal\views\Plugin\views\filter\FilterPluginBase;
use Drupal\views\Plugin\views\query\QueryPluginBase;
use Drupal\views\ViewExecutable;
use Drupal\intercept_core\EntityImageThumbnailUriField;
use Drupal\intercept_event\CheckinPeriodField;
use Drupal\intercept_event\Entity\EventRegistrationInterface;
use Drupal\intercept_event\EventRegistrationField;

/**
 * Implements hook_help().
 */
function intercept_event_help($route_name, RouteMatchInterface $route_match) {
  switch ($route_name) {
    // Main module help for the intercept_event module.
    case 'help.page.intercept_event':
      $output = '';
      $output .= '<h3>' . new TranslatableMarkup('About') . '</h3>';
      $output .= '<p>' . new TranslatableMarkup('Provides events and event registration for Intercept.') . '</p>';
      return $output;
  }
}

/**
 * Implements hook_theme().
 */
function intercept_event_theme() {
  return [
    'event_attendance_scan_form' => [
      'render element' => 'form',
    ],
    'event_registration_event_form' => [
      'render element' => 'form',
    ],
    'event_registration_user_form' => [
      'variables' => [
        'event' => NULL,
        'header' => NULL,
        'form' => NULL,
      ],
    ],
    'event_registration_guest_form' => [
      'variables' => [
        'event' => NULL,
        'header' => NULL,
        'form' => NULL,
      ],
    ],
    'event_registration_warnings' => [
      'variables' => ['warnings' => NULL],
    ],
    'events_recommended' => [
      'variables' => ['content' => NULL],
      'template' => 'events-recommended',
    ],
    'node_event_analysis' => [
      'variables' => ['content' => NULL],
      'template' => 'node-event-analysis',
    ],
    'node_event_calendar' => [
      'variables' => [
        'title' => NULL,
        'subtitle' => NULL,
        'date' => NULL,
        'time' => NULL,
        'url' => NULL,
        'body' => NULL,
      ],
      'template' => 'node-event-calendar',
    ],
    'node_event_registrations' => [
      'variables' => ['content' => NULL],
      'template' => 'node-event-registrations',
    ],
    'evaluation_eval_widget' => [
      'variables' => [
        'label' => new TranslatableMarkup('Your rating for this event:'),
        'value' => NULL,
      ],
      'template' => 'evaluation-eval-widget',
    ],
    'event_eval_feedback' => [
      'variables' => [
        'content' => NULL,
        'user' => NULL,
      ],
      'template' => 'event-eval-feedback',
    ],
    'intercept_events_page' => [
      'variables' => [
        'intercept_view_switcher' => NULL,
        'events_list' => NULL,
      ],
      'template' => 'intercept-events-page',
    ],
  ];
}

/**
 * Implements hook_entity_access().
 */
function intercept_event_entity_access(EntityInterface $entity, $operation, AccountInterface $account) {
  if ($entity->getEntityTypeId() == 'flagging' && $entity->bundle() == 'saved_event' && $operation == 'view') {
    $permissions = [
      'view saved_event flagging entities',
      'administer flaggings',
    ];
    /** @var \Drupal\flag\FlaggingInterface $entity */
    if ($entity->getOwnerId() == $account->id()) {
      $permissions[] = 'view own saved_event flagging entities';
    }
    return AccessResult::allowedIfHasPermissions($account, $permissions, 'OR');
  }
}

/**
 * Implements hook_entity_operation().
 */
function intercept_event_entity_operation(EntityInterface $entity) {
  if ($entity->getEntityTypeId() == 'event_registration') {
    $operation = function ($op) use ($entity) {
      return [
        'title' => new TranslatableMarkup(ucwords($op)),
        'url' => Url::fromRoute("entity.event_registration.{$op}_form", [
          'event_registration' => $entity->id(),
          'destination' => Url::fromRoute('<current>')->toString(),
        ]),
      ];
    };
    return [
      'cancel' => $operation('cancel'),
    ];
  }
}

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

  $extra['node']['event']['display']['checkin_link'] = [
    'label' => new TranslatableMarkup('Checkin link'),
    'description' => new TranslatableMarkup('Displays a link to check in during the check-in period'),
    'weight' => 10,
    'visible' => FALSE,
  ];

  $extra['node']['event']['display']['evaluation_staff'] = [
    'label' => new TranslatableMarkup('Evaluation (staff)'),
    'description' => new TranslatableMarkup('View evaluation widget for staff users.'),
    'weight' => 10,
    'visible' => FALSE,
  ];

  $extra['node']['event']['display']['recommended'] = [
    'label' => new TranslatableMarkup('Recommended'),
    'description' => new TranslatableMarkup('Adds a label to recommended events.'),
    'weight' => 10,
    'visible' => FALSE,
  ];

  $extra['event_attendance']['event_attendance']['form']['event_summary'] = [
    'label' => new TranslatableMarkup('Event summary'),
    'description' => new TranslatableMarkup('View event summary information.'),
    'weight' => 10,
    'visible' => FALSE,
  ];

  $extra['node']['event']['form']['recurring_event'] = [
    'label' => new TranslatableMarkup('Recurring event'),
    'description' => new TranslatableMarkup('View event recurrence information.'),
    'weight' => 10,
    'visible' => FALSE,
  ];

  return $extra;
}

/**
 * Implements hook_entity_type_build().
 */
function intercept_event_entity_type_build(array &$entity_types) {
  /** @var \Drupal\Core\Entity\EntityTypeInterface[] $entity_types */
  $entity_types['node']
    ->setFormClass('template', 'Drupal\intercept_event\Form\EventTemplateForm')
    ->setLinkTemplate('template-form', '/node/{node}/template')
    ->setFormClass('recurrence', 'Drupal\intercept_event\Form\EventRecurrenceEventsForm')
    ->setLinkTemplate('recurrence-form', '/node/{node}/recurrences')
    ->setFormClass('attendance', 'Drupal\intercept_event\Form\EventAttendanceEventsForm')
    ->setLinkTemplate('attendance-form', '/node/{node}/recurrences');
}

/**
 * Alter the Fullcalendar block settings for a specific block.
 *
 * @param array $block_settings
 *   The current fullcalendar block settings.
 * @param array $block_content
 *   The block render array.
 * @param \Drupal\Core\Block\BlockPluginInterface $block
 *   The current block instance.
 */
function intercept_event_fullcalendar_block_settings_alter(array &$block_settings, array &$block_content, BlockPluginInterface $block) {
  $block_settings['calendar_options']['startParam'] = 'start';
  $block_settings['calendar_options']['endParam'] = 'end';
  $block_settings['calendar_options']['timeZone'] = date_default_timezone_get();
}

/**
 * Implements hook_node_access().
 */
function intercept_event_node_access(NodeInterface $node, $op, AccountInterface $account) {
  if ($node->bundle() == 'event' && $op == 'template') {
    return AccessResult::allowedIfHasPermission($account, 'create event content from template');
  }
  if ($node->bundle() == 'event' && $op == 'analysis') {
    return AccessResult::allowedIfHasPermission($account, 'analyze events');
  }
  if ($node->bundle() == 'event' && $op == 'scan') {
    return AccessResult::allowedIfHasPermission($account, 'scan event_attendance');
  }
  return AccessResult::neutral();
}

/**
 * Implements hook_entity_field_access().
 */
function intercept_event_entity_field_access($operation, FieldDefinitionInterface $field_definition, AccountInterface $account, FieldItemListInterface $items = NULL) {
  // Check if it's edit because 'view' might not have any items.
  if ($operation == 'edit') {
    if ($items->getEntity()->bundle() == 'event' && $field_definition->getName() == 'field_event_is_template') {
      return AccessResult::forbiddenIf(!$account->hasPermission('edit event field field_event_is_template'));
    }
    if ($items->getEntity()->bundle() == 'event' && $field_definition->getName() == 'field_featured') {
      return AccessResult::forbiddenIf(!$account->hasPermission('edit event field field_featured'));
    }
  }
  return AccessResult::neutral();
}

/**
 * Implements hook_page_attachments().
 */
function intercept_event_page_attachments(array &$attachments) {
  // Make a request to invalidate the check-in period with each page request.
  $attachments['#attached']['library'][] = 'intercept_event/eventCheckinPeriodInvalidator';
}

/**
 * Implements hook_node_insert().
 */
function intercept_event_node_insert(NodeInterface $node) {
  if ($node->bundle() == 'event') {
    $event_recurrences = $node->get('event_recurrence')->referencedEntities();
    foreach ($event_recurrences as $recurrence) {
      if (!$recurrence->get('event')->target_id) {
        $recurrence->set('event', $node->id());
        $recurrence->save();
      }
    }
    // Only redirect when the user has a real recurrence value set.
    $recurrence = \Drupal::service('intercept_event.recurring_manager')->getBaseEventRecurrence($node);
    $field_event_rrule = @$recurrence->field_event_rrule;
    if ($field_event_rrule) {
      $rrule_value = $field_event_rrule->first()->getValue('rrule')['rrule'];
    }
    if ($recurrence && !empty($event_recurrences) && $rrule_value) {
      // Redirect to the recurrences page.
      $recurrences_url = Url::fromRoute('entity.node.recurrence_form', [
        'node' => $node->id(),
      ]);
      $response = new LocalRedirectResponse($recurrences_url->toString());
      $response->setMaxAge(0);
      $response->addCacheableDependency($recurrence);
      $response->addCacheableDependency($node);
      $response->send();
    }
  }
}

/**
 * Implements hook_entity_presave().
 */
function intercept_event_entity_presave(EntityInterface $entity) {
  switch ($entity->bundle()) {
    case 'event':
      $keywords = '';
      $keyword_values = [
        'title' => $entity->get('title')->value,
        'teaser' => $entity->get('field_text_teaser')->value ?: "",
        'content' => $entity->get('field_text_content')->value ?: "",
        'intro' => $entity->get('field_text_intro')->value ?: "",
      ];
      $title = $keyword_values['title'];
      foreach($keyword_values as $key => $keyword_value) {
        $keywords_values[$key] = html_entity_decode(strip_tags($keyword_value));
      }
      $keywords = implode(' ', $keyword_values);
      $types = $entity->get('field_event_type')->referencedEntities();
      foreach ($types as $type) {
        $keywords .= ' ' . $type->getName();
      }
      $audiences = $entity->get('field_event_audience')->referencedEntities();
      foreach ($audiences as $audience) {
        $keywords .= ' ' . $audience->getName();
      }
      $tags = $entity->get('field_event_tags')->referencedEntities();
      foreach ($tags as $tag) {
        $keywords .= ' ' . $tag->getName();
      }
      // Clean up the keywords string to remove punctuation & extra white space.
      $keywords = preg_replace('/[Rr]ésumé/', 'résumé resume', $keywords);
      $keywords = preg_replace("/[^a-zA-Z 0-9\-]/", '', $keywords);
      $keywords = preg_replace('/\s+/', ' ', $keywords);
      $keywords = trim($keywords);
      // Setting the value into the keywords field.
      $entity->field_keywords->value = $keywords;

      // Transition waitlisted attendees to an active state in case
      // capacity was increased.
      \Drupal::service('intercept_event.manager')->fillEventOpenCapacity($entity);

      // STATUS CHANGES.
      if ($entity->isNew() == FALSE) {
        $event_status_original = $entity->original->get('field_event_status')->value;
        $event_status = $entity->get('field_event_status')->value;
        // Did the event status change?
        if ($event_status != $event_status_original) {
          if ($event_status == 'canceled') {
            // Update the node and prepend "Canceled: " to the title.
            if (strpos($title, 'Canceled') == FALSE) {
              $entity->title = 'Canceled: ' . trim($title);
            }
            // Cancel the room reservation (if present)
            if ($reservation = \Drupal::service('intercept_core.reservation.manager')->getEventReservation($entity)) {
              $reservation->cancel();
            }
            // Notify the staff member that it's a recurring event and only 1 has been canceled.
            $recurring_event = \Drupal::service('intercept_event.recurring_manager')->isRecurringEvent($entity);
            if ($recurring_event) {
              \Drupal::messenger()->addMessage(t('You have successfully canceled this instance of the event. If you need to cancel other recurrences, please use the Recurrences tab.'));
            }
            // Registration should also be closed.
            $entity->set('field_must_register', NULL);
            $entity->set('field_has_waitlist', NULL);
            $entity->set('field_capacity_max', NULL);
            $entity->set('field_event_register_period', NULL);
            $entity->set('field_waitlist_max', NULL);
          }
          elseif ($event_status == 'scheduled') {
            if (strpos($title, 'Canceled') !== FALSE) {
              $entity->title = str_replace('Canceled: ', '', $title);
            }
          }
        }

        // Redirect to the recurrences page as appropriate.
        $event_recurrences = $entity->get('event_recurrence')->referencedEntities();
        $referrer = parse_url(\Drupal::request()->server->get('HTTP_REFERER'));
        if (!empty($referrer['path']) && !strstr($referrer['path'], '/analysis')) {
          if ($recurrence = \Drupal::service('intercept_event.recurring_manager')->getBaseEventRecurrence($entity) && !empty($event_recurrences)) {
            $recurrences_url = Url::fromRoute('entity.node.recurrence_form', [
              'node' => $entity->id(),
            ]);
            $response = new LocalRedirectResponse($recurrences_url->toString());
            $response->setMaxAge(0);
            $response->addCacheableDependency($recurrence);
            $response->addCacheableDependency($entity);
            $response->send();
          }
        }
      }

      // Set value of field_presented_by_non_staff to TRUE if field_presenter
      // has value, FALSE otherwise.
      $presented_by_non_staff = !$entity->get('field_presenter')->isEmpty();
      $entity->set('field_presented_by_non_staff', $presented_by_non_staff);
      break;

    case 'audience':
    case 'event_type':
    case 'tag':
      // It's a taxonomy term from one of our taxonomy fields that gets
      // concatenated into the keywords field.
      // Find the term id.
      $tid = $entity->id();
      if ($tid) {
        // Then, find all of the event nodes that currently use this term.
        $nodes = \Drupal::entityTypeManager()->getStorage('node')->loadByProperties([
          'field_event_audience' => $tid,
        ]);
        if (empty($nodes)) {
          $nodes = \Drupal::entityTypeManager()->getStorage('node')->loadByProperties([
            'field_event_type' => $tid,
          ]);
        }
        if (empty($nodes)) {
          $nodes = \Drupal::entityTypeManager()->getStorage('node')->loadByProperties([
            'field_event_tags' => $tid,
          ]);
        }
        // Trigger a re-save (thus updating keywords field data).
        if (count($nodes) > 0) {
          $eventQueue = Drupal::service('intercept_event.queue');
          foreach ($nodes as $node) {
            $eventQueue->add($node->id());
          }
        }
      }
      break;
  }
}

/**
 * Implements hook_ENTITY_TYPE_update().
 */
function intercept_event_event_registration_update(EventRegistrationInterface $event_registration) {
  // Release the lock for the event registration.
  \Drupal::service('intercept_event.event_registration_event_lock')
    ->releaseLock($event_registration->getEvent());
}

/**
 * Implements hook_ENTITY_TYPE_insert().
 */
function intercept_event_event_registration_insert(EventRegistrationInterface $event_registration) {
  // Release the lock for the event registration.
  \Drupal::service('intercept_event.event_registration_event_lock')
    ->releaseLock($event_registration->getEvent());
}

/**
 * Implements hook_cloned_node_alter().
 */
function intercept_event_cloned_node_alter(&$node) {
  if ($node->bundle() == 'event') {
    // Clear recurrence and registration info from cloned event.
    $node->set('event_recurrence', NULL);
    $node->set('field_must_register', NULL);
    $node->set('field_has_waitlist', NULL);
    $node->set('field_capacity_max', NULL);
    $node->set('field_event_register_period', NULL);
    $node->set('field_waitlist_max', NULL);
  }
}

/**
 * Implements hook_node_delete().
 */
function intercept_event_node_delete(NodeInterface $node) {
  if ($node->bundle() == 'event') {
    \Drupal::service('intercept_event.manager')->deleteRegisterAlias($node);
    return;
  }
}

/**
 * Implements hook_preprocess_HOOK().
 */
function intercept_event_preprocess_views_view(&$variables) {
  $view = $variables['view'];

  $tag = $view->storage->get('tag');
  if (in_array('management', explode(' ', $tag))) {
    $variables['title'] = [
      '#markup' => $view->getTitle(),
    ];
  }

  if ($view->id() == 'intercept_room_reservations' && $view->current_display == 'page') {
    $variables['title'] = [
      '#markup' => $view->getTitle(),
    ];
  }
}

/**
 * Implements hook_preprocess_views_view_field().
 */
function intercept_event_preprocess_views_view_field(&$variables) {
  $view = $variables['view'];
  $row = $variables['row'];
  $field = $variables['field'];
  $field_name = $variables['field']->field;
  $display = $view->current_display;
  if ($view->id() == 'intercept_event_templates' && $display == 'embed') {
    $current_user = \Drupal::currentUser();
    $user = User::load($current_user->id());
    if ($field_name == 'title') {
      $title = $field->getValue($row);
      if ($title) {
        if (!$user->hasPermission('edit event field field_event_is_template')) {
          return;
        }
        $nid = $variables['row']->nid;
        $variables['output'] = Markup::create('<a href="/node/' . $nid . '/edit">' . $variables['output'] . '</a>');
      }
    }
  }
  elseif ($view->id() == 'intercept_events' && $display == 'list_page') {
    if ($field->field == 'field_date_time') {
      $time = $field->getValue($row);
      $date = \Drupal::service('intercept_core.utility.dates')->getDrupalDate($time);
      $date = \Drupal::service('intercept_core.utility.dates')->convertTimezone($date, 'default')->format('l, F d, Y');
      $variables['output'] = ['#markup' => $date];
    }
  }
}

/**
 * Implements hook_preprocess_select() for select elements.
 */
function intercept_event_preprocess_select(&$variables) {
  $id = '';
  if (array_key_exists('#attributes', $variables['element'])
    && array_key_exists('id', $variables['element']['#attributes'])) {
    $id = $variables['element']['#attributes']['id'];
  }
  if ($id == 'edit-event-recurrence-0-inline-entity-form-field-event-rrule-0-mode') {
    $variables['attributes']['class'][] = 'rrule-mode';
  }
}

/**
 * Implement hook_theme_suggestions_HOOK_alter().
 */
function intercept_event_theme_suggestions_form_element_alter(&$suggestions, $variables) {
  if (!isset($variables['element']['#form_id'])) {
    return;
  }
  $form_id = $variables['element']['#form_id'];

  // Add a suggestion for staff evaluations.
  $evaluation_form_position = strrpos($form_id, 'event_evaluation_staff_form');
  if ($evaluation_form_position !== FALSE && $evaluation_form_position >= 0) {
    $suggestions[] = "form_element__type__" . $variables['element']['#type'] . "__event_evaluation_staff_form";
  }
  elseif ($form_id == 'views_exposed_form') {
    if ($variables['element']['#attributes']['id'] == 'edit-title' && $variables['element']['#title'] == 'Title') {
      $suggestions[] = "form_element__type__" . $variables['element']['#type'] . "__event_attendance_export_form";
    }
  }
}

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

    $form['field_location_abbreviation_value'] = [
      '#type' => 'textfield',
      '#title' => t('Location'),
      '#autocomplete_route_name' => 'intercept_event.location.autocomplete',
      '#autocomplete_route_parameters' => [
        'field_name' => 'location',
        'count' => 10,
        'entity_type_id' => 'node',
      ],
    ];
  }
  elseif ($view->id() == 'intercept_events' && $view->current_display == 'list_page') {
    // Reset date inputs to class to the date_start and date_end exposed form inputs.
    if (isset($form['date_start'])) {
      $form['date_start']['#attributes']['class'][] = 'use-native-picker';
    }
    if (isset($form['date_end'])) {
      $form['date_end']['#attributes']['class'][] = 'use-native-picker';
    }

    $form['location']['#options'] = intercept_event_sort_locations();
  }
  elseif ($view->id() == 'intercept_events' && $view->current_display == 'events') {
    // Reset date inputs to class to the date_start and date_end exposed form inputs.
    if (isset($form['start'])) {
      $form['start']['#type'] = 'hidden';
    }
    if (isset($form['end'])) {
      $form['end']['#type'] = 'hidden';
    }

    $form['location']['#options'] = intercept_event_sort_locations();
  }
  elseif ($view->id() == 'intercept_event_attendance' && $view->current_display == 'embed') {
    // Remove autocomplete from the form's date fields.
    $form['date_start']['#attributes']['autocomplete'] = 'off';
    $form['date_end']['#attributes']['autocomplete'] = 'off';
    $form['attendance_date']['min']['#attributes']['autocomplete'] = 'off';
    $form['attendance_date']['max']['#attributes']['autocomplete'] = 'off';
    // Change the labels on the min and max date fields.
    $form['attendance_date_wrapper']['attendance_date']['min']['#title'] = new TranslatableMarkup('Attendance dates between');
    $form['attendance_date_wrapper']['attendance_date']['max']['#title'] = new TranslatableMarkup('And');
  }
  elseif ($view->id() == 'intercept_event_attendance_by_age_group' && $view->current_display == 'page') {
    // Remove autocomplete from the form's date fields.
    $form['field_date_time_value']['min']['#attributes']['autocomplete'] = 'off';
    $form['field_date_time_value']['max']['#attributes']['autocomplete'] = 'off';
    // Change the labels on the min and max date fields.
    $form['field_date_time_value_wrapper']['field_date_time_value']['min']['#title'] = new TranslatableMarkup('Dates between');
    $form['field_date_time_value_wrapper']['field_date_time_value']['max']['#title'] = new TranslatableMarkup('And');
  }
}

/**
 * Implements hook_field_widget_single_element_WIDGET_TYPE_form_alter().
 */
function intercept_event_field_widget_single_element_date_recur_modular_alpha_form_alter(array &$elements, FormStateInterface $form_state, array $context) {
  $form_object = $form_state->getFormObject();
  if ($form_object->getBaseFormId() != 'node_form') {
    return;
  }
  if ($form_object->getEntity()->bundle() != 'event') {
    return;
  }
  \Drupal::service('intercept_event.recurring_manager')->dateRecurWidgetFormAlter($elements, $form_state, $context);
}

/**
 * Implements hook_field_widget_single_element_form_alter().
 */
function intercept_event_field_widget_single_element_form_alter(&$element, FormStateInterface $form_state, $context) {
  $field_definition = $context['items']->getFieldDefinition();
  switch ($field_definition->getName()) {
    case 'event_recurrence':
      \Drupal::service('intercept_event.recurring_manager')->entityReferenceWidgetFormAlter($element, $form_state, $context);
      break;

    case 'image_primary':
      \Drupal::service('intercept_event.manager')->entityReferenceWidgetFormAlter($element, $form_state, $context);
      break;

  }
}

/**
 * Implements hook_inline_entity_form_entity_form_alter().
 */
function intercept_event_inline_entity_form_entity_form_alter(&$entity_form, &$form_state) {
  \Drupal::service('intercept_event.recurring_manager')->iefEntityFormAlter($entity_form, $form_state);

  // Add validation function to check alt text,
  // but only alter IEF for media entities.
  if ($entity_form['#entity_type'] !== 'media') {
    return;
  }
  $entity_form['#element_validate'][] = 'intercept_event_validate_alt';
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function intercept_event_form_node_form_alter(&$form, FormStateInterface $form_state) {
  $node = $form_state->getFormObject()->getEntity();
  if ($node->bundle() == 'event') {
    $form['title']['widget'][0]['value']['#description'] = t('Titles are limited to 255 characters.');
    \Drupal::service('intercept_event.manager')->nodeFormAlter($form, $form_state);
    if (!$node->isNew()) {
      \Drupal::service('intercept_event.manager')->nodeEditFormAlter($form, $form_state);
    }
    // TEMP DEBUGGING. Set the event date to tomorrow.
    // if (is_null(@$form['field_date_time']['widget'][0]['value']['#default_value'])) {
    //   $form['field_date_time']['widget'][0]['value']['#default_value'] = new \Drupal\Core\Datetime\DrupalDateTime('tomorrow 12:00 pm');
    //   $form['field_date_time']['widget'][0]['end_value']['#default_value'] = new \Drupal\Core\Datetime\DrupalDateTime('tomorrow 1:00 pm');
    // }.
  }
  $request = \Drupal::service('request_stack')->getCurrentRequest();
  if ($node->bundle() == 'event' && $node->isNew() && $request->query->get('template')) {
    $form['field_event_is_template']['widget']['value']['#default_value'] = 1;
  }
  if ($form_state->getFormObject()->getEntity()->bundle() == 'event') {
    $form['#validate'][] = 'intercept_event_node_event_form_validate';
  }

  if ($node->bundle() == 'event' && $node->isNew()) {
    // Figure out whether each location is internal/external based on the
    // branch location field value.
    $internal_locations = \Drupal::service('intercept_event.manager')->getBranchLocations(TRUE);
    $external_locations = \Drupal::service('intercept_event.manager')->getBranchLocations(FALSE);

    // For each of these locations, only display the "external" fields if it's one of those.
    $external_locations_array = [];
    $counter = 0;
    foreach ($external_locations as $value) {
      if ($counter > 0) {
        $external_locations_array[] = 'or';
      }
      $external_locations_array[] = ['value' => $value];
      $counter++;
    }
    $external_locations_state = [
      'visible' => [
        ':input[name="field_location"]' => $external_locations_array,
      ],
    ];
    $form['field_external_location_name']['#states'] = $external_locations_state;
    $form['field_address']['#states'] = $external_locations_state;

    // Only show the room field if it's an internal location.
    // Note: This code only affects the initial page load of this field.
    // Subsequent loads via AJAX are handled in the fieldRoomAjaxCallback()
    // function.
    $internal_locations_array = [];
    $counter = 0;
    foreach ($internal_locations as $value) {
      if ($counter > 0) {
        $internal_locations_array[] = 'or';
      }
      $internal_locations_array[] = ['value' => $value];
      $counter++;
    }
    $room_state = [
      'visible' => [
        ':input[name="field_location"]' => $internal_locations_array,
      ],
    ];
    $form['field_room']['#states'] = $room_state;
  }
  if ($node->bundle() == 'location') {
    foreach ($form['field_features']['widget'] as $key => $widget) {
      $key = (int) $key;
      $form['field_features']['widget'][$key]['uri']['#description'] = new TranslatableMarkup('<div style="padding-left: 30px;">Enter &lt;nolink&gt; to add a feature name with just a title and no link.</div>');
    }
  }
}

/**
 * Validation function to check alt text on primary image field.
 */
function intercept_event_validate_alt(&$entity_form, &$form_state) {
  $triggering_element = $form_state->getTriggeringElement();
  if ($triggering_element['#name'] == 'ief-add-submit-image_primary-form') {
    $entities = $form_state->getValue('image_primary');
    // This will contain the fields of the new entity being inserted.
    $entity_form_fields = $entities['form'][array_key_first($entities['form'])];
    $alt = $entity_form_fields['field_media_image'][0]['alt'];

    $values = $form_state->getValues();
    $title = !empty($values['title']) ? $values['title'][0]['value'] : FALSE;
    if (!empty($alt) && !empty($title) && $title == $alt) {
      $form_state->setError($triggering_element, t('The alternate text for your image should not be exactly the same as your event title.'));
    }
  }
}

/**
 * Implements hook_node_view().
 */
function intercept_event_node_view(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode) {
  // Add 'Recommended for You' label to recommended events.
  if ($entity->bundle() == 'event' && $display->getComponent('recommended')) {
    /** @var \Drupal\intercept_event\SuggestedEventsProviderInterface $suggested_events_provider */
    $suggested_events_provider = Drupal::service('intercept_event.suggested_events_provider');
    $suggested_events = $suggested_events_provider->getSuggestedEventIds();
    $current_user = \Drupal::currentUser();
    $profile_storage = \Drupal::service('entity_type.manager')->getStorage('profile');
    $customer = $profile_storage->loadByUser($current_user, 'customer');

    if (in_array($entity->id(), $suggested_events)) {
      $build['recommended'] = [
        '#markup' => new TranslatableMarkup('Recommended For You'),
        '#cache' => [
          'contexts' => ['user'],
          'tags' => ['user:' . $current_user->id()]
        ]
      ];

      if ($customer) {
        $build['recommended']['#cache']['tags'][] = 'profile:' . $customer->id();
      }
    }

  }

  if ($entity->bundle() === 'event' && $entity->field_must_register->value === '1') {
    $build['register_prompt'] = [
      '#markup' => '<div class="event-register-button js--event-register-button" data-event-uuid="' . $entity->uuid() . '"></div>',
      '#attached' => [
        'library' => ['intercept_event/eventRegisterButton'],
      ],
    ];
  }
  if ($entity->bundle() === 'event' && $entity->hasField('field_event_register_period')) {
    // Invalidate this $entity's cache based on field_event_register_period.
    // Start and end points are standardized to UTC.
    $start = new DateTime($entity->field_event_register_period->value ?: "");
    $end = new DateTime($entity->field_event_register_period->end_value ?: "");
    $now = new DateTime();
    $time_zone = new DateTimeZone('UTC');
    $now->setTimeZone($time_zone);
    // Invalidate tags if we passed either endpoint in the last hour.
    if (($start->getTimestamp() >= $now->getTimestamp() - 60 * 60)
      || ($end->getTimestamp() >= $now->getTimestamp() - 60 * 60)) {
      Cache::invalidateTags($entity->getCacheTags());
    }
  }

  if ($entity->bundle() === 'event' && $entity->hasField('checkin_period') && $display->getComponent('checkin_link')) {
    /** @var \Drupal\intercept_event\EventAttendanceProviderInterface $event_attendance_provider */
    $event_attendance_provider = \Drupal::service('intercept_event.event_attendance_provider');

    $build['checkin_link'] = Link::createFromRoute(
      t('Check in'),
      'entity.node.checkin',
      [
        'node' => $entity->id(),
      ],
      [
        'attributes' => [
          'class' => ['button', 'button--checkin', 'button--small'],
        ],
      ]
    )->toRenderable();
    // Only show the link if it's during the check-in period and the user has not already checked in.
    $build['checkin_link']['#access'] = $entity->checkin_period->status === 'open' && !$event_attendance_provider->getEventAttendance($entity->id());
    // Cache the link per user.
    $build['checkin_link']['#cache']['contexts'][] = 'user';

    // Invalidate this $entity's cache based on field_event_register_period.
    // Start and end points are standardized to UTC.
    if ($entity->checkin_period->start) {
      $start = new DateTime($entity->checkin_period->start);
      $end = new DateTime($entity->checkin_period->end);
      $now = new DateTime();
      $time_zone = new DateTimeZone('UTC');
      $now->setTimeZone($time_zone);
      // Invalidate tags if we passed either endpoint in the last hour.
      if (($start->getTimestamp() >= $now->getTimestamp() - 60 * 60)
        || ($end->getTimestamp() >= $now->getTimestamp() - 60 * 60)) {
        Cache::invalidateTags($entity->getCacheTags());
      }
    }
  }
}

/**
 * Implements hook_vote_view().
 */
function intercept_event_vote_view(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode) {
  if ($entity->bundle() == 'evaluation' && $display->getComponent('evaluation')) {
    /** @var \Drupal\votingApi\VoteInterface $entity */
    $build['evaluation'] = [
      '#theme' => 'evaluation_eval_widget',
      '#value' => $entity->getValue() ? 'like' : 'dislike',
    ];
  }
}

/**
 * Custom form validation for the event node form.
 *
 * @see intercept_event_form_node_form_alter()
 */
function intercept_event_node_event_form_validate(&$form, FormStateInterface $form_state) {
  $values = $form_state->getValues();
  $must_register = !empty($values['field_must_register']) ? $values['field_must_register']['value'] : FALSE;
  if ($must_register) {
    // In this case a partial date was entered, but is invalid.
    $errors = $form_state->getErrors();
    if (!empty($errors['field_event_register_period][0']) || !empty($errors['field_date_time][0'])) {
      // Skip to avoid overcomplicating our date validation.
      return;
    }

    $valid_dates = function ($date_array) {
      if (empty($date_array['value']) || empty($date_array['end_value'])) {
        return FALSE;
      }
      if (!($date_array['value'] instanceof DateTimePlus) || !($date_array['end_value'] instanceof DateTimePlus)) {
        return FALSE;
      }
      return TRUE;
    };

    $dates_event = $values['field_date_time'][0];
    $dates_registration = $values['field_event_register_period'][0];

    if (!$valid_dates($dates_registration)) {
      $form_state->setErrorByName('field_event_register_period[0', new TranslatableMarkup('Registration period is required.'));
      return;
    }
    if (!$valid_dates($dates_event)) {
      // This should not be possible,
      // but just in case so further validation does not break.
      $form_state->setErrorByName('field_event_register_period[0', new TranslatableMarkup('Event date is invalid.'));
    }

    // @todo Check to see if date module has already set an error on the
    // end value being before the start value.
    $get_field_name = function ($field, $field_name = 'field_event_register') {
      $parents = [$field_name . '_period', 0, $field];
      return implode('][', $parents);
    };

    $title = $form['field_event_register_period']['widget']['#title'];

    if (!$dates_event['end_value']->diff($dates_registration['end_value'])->invert) {
      $form_state->setErrorByName($get_field_name('end_value'), new TranslatableMarkup('The @title must end before the event end date.', [
        '@title' => $title,
      ]));
    }
    if (!$dates_event['value']->diff($dates_registration['value'])->invert) {
      $form_state->setErrorByName($get_field_name('value'), new TranslatableMarkup('The @title must start before the event start date.', [
        '@title' => $title,
      ]));
    }
  }

  // Let's check NEW events with recurrences AND room reservations to
  // prevent double-bookings.
  // But don't trigger this if it's an AJAX callback validation.
  // This is largely reused code from EventRecurrenceEventsForm.php.
  $new_node = ($form['#form_id'] == 'node_event_form') ? TRUE : FALSE;
  $ajax_form = \Drupal::request()->query->get('ajax_form');
  if (isset($values['field_room']) && isset($values['field_room'][0])) {
    $room_id = $values['field_room'][0]['target_id'];
  }
  else {
    $room_id = NULL;
  }
  $event_recurrence = ($values['event_recurrence'][0]['inline_entity_form']['field_event_rrule'][0]['mode'] !== 'once') ? $values['event_recurrence'][0]['inline_entity_form']['field_event_rrule'][0]['mode'] : '';
  if (!empty($event_recurrence) && !empty($values['reservation']) && $ajax_form != TRUE && $room_id && $new_node == TRUE) {
    // Check for conflicts.
    $date_utility = \Drupal::service('intercept_core.utility.dates');
    $reservation_manager = \Drupal::service('intercept_core.reservation.manager');

    $room = Node::load($room_id);
    $room_name = $room->label();
    $rooms = [$room_id];

    // Get the start/end dates of the recurrences.
    $series_generator = \Drupal::service('intercept_bulk_room_reservation.series_generator');
    $logistics = $series_generator->getLogisticsFromFormState($form_state);
    $error = FALSE;

    foreach ($logistics['dates'] as $date) {
      $start = $date . 'T' . $logistics['times']['start'];
      $end = $date . 'T' . $logistics['times']['end'];
      // Checking to see if each date range is in conflict.
      $availability = $reservation_manager->availability([
        'start' => $start,
        'end' => $end,
        'rooms' => $rooms,
      ]);
      foreach ($availability as $room_availability) {
        if ($room_availability['user_exceeded_limit']
          || $room_availability['has_reservation_conflict']
          || $room_availability['has_conflict']
          || $room_availability['has_open_hours_conflict']
          || $room_availability['has_max_duration_conflict']
          || $room_availability['is_closed']) {
          $error = TRUE;

          // Unfortunately we can only set one error per form element
          // due to this core bug:
          // https://www.drupal.org/project/drupal/issues/2818437
          $formatted_date = date('m/d/Y', strtotime($date));
          \Drupal::messenger()->addError(t("There is a conflict in :room on :start.", [
            ':room' => $room_name,
            ':start' => $formatted_date,
          ]));
        }
      }
    }
    if ($error) {
      $form_state->setErrorByName('event_recurrence', "");
    }
  }

}

/**
 * Implements hook_entity_bundle_field_info().
 */
function intercept_event_entity_bundle_field_info(EntityTypeInterface $entity_type, $bundle, array $base_field_definitions) {
  $fields = [];
  if ($entity_type->id() == 'vote' && $bundle == 'evaluation') {
    $fields['vote_criteria'] = BaseFieldDefinition::create('map')
      ->setLabel(new TranslatableMarkup('Vote criteria'))
      ->setDisplayConfigurable('view', TRUE)
      ->setDisplayConfigurable('form', TRUE);
    $fields['feedback'] = BaseFieldDefinition::create('text_long')
      ->setLabel(new TranslatableMarkup('Feedback'))
      ->setDisplayConfigurable('view', TRUE)
      ->setDisplayConfigurable('form', TRUE);

    return $fields;
  }
  if ($entity_type->id() == 'node' && $bundle == 'event') {
    $fields['registration'] = BaseFieldDefinition::create('intercept_computed')
      ->setLabel(new TranslatableMarkup('Registration details'))
      ->setComputed(TRUE)
      ->setSetting('properties', [
        'total' => DataDefinition::create('integer')->setLabel(t('Total registrations:')),
        'total_waitlist' => DataDefinition::create('integer')->setLabel(t('Total waitlisted:')),
        'remaining_registration' => DataDefinition::create('integer')->setLabel(t('Seats remaining:')),
        'remaining_waitlist' => DataDefinition::create('integer')->setLabel(t('Waitlist spots remaining:')),
        'status' => DataDefinition::create('string')->setLabel(t('Registration Status:')),
        'status_user' => DataDefinition::create('string')->setLabel(t('Status for current user:')),
      ])
      ->setClass(EventRegistrationField::class);

    $fields['event_recurrence'] = BaseFieldDefinition::create('entity_reference')
      ->setLabel(new TranslatableMarkup('Recurring'))
      ->setSetting('target_type', 'event_recurrence')
      ->setDisplayConfigurable('view', TRUE)
      ->setDisplayConfigurable('form', TRUE);

    // @todo Make this configurable by privileged user accounts.
    $fields['event_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);

    // Define check-in period computed fields.
    $fields['checkin_period'] = BaseFieldDefinition::create('intercept_computed')
      ->setLabel(t('Check-in Period'))
      ->setComputed(TRUE)
      ->setSetting('properties', [
        'start' => DataDefinition::create('datetime_iso8601')->setLabel(t('Check-in Period Start:')),
        'end' => DataDefinition::create('datetime_iso8601')->setLabel(t('Check-in Period End:')),
        'status' => DataDefinition::create('string')->setLabel(t('Check-in Period Status:')),
        'checkin_url' => DataDefinition::create('string')->setLabel(t('Check-in Url:')),
      ])
      ->setClass(CheckinPeriodField::class);
  }
  return $fields;
}

/**
 * Implements hook_entity_base_field_info().
 */
function intercept_event_entity_base_field_info(EntityTypeInterface $entity_type) {
  if ($entity_type->id() == 'vote') {
    $definitions = [];
    $definitions['vote_criteria'] = BaseFieldDefinition::create('map')
      ->setName('vote_criteria')
      ->setLabel(new TranslatableMarkup('Vote criteria'))
      ->setCardinality(1);
    $definitions['feedback'] = BaseFieldDefinition::create('text_long')
      ->setName('feedback')
      ->setLabel(new TranslatableMarkup('Feedback'))
      ->setCardinality(1);
    return $definitions;
  }
  if ($entity_type->id() == 'node') {
    $definitions = [];
    $definitions['event_recurrence'] = BaseFieldDefinition::create('entity_reference')
      ->setName('event_recurrence')
      ->setLabel(new TranslatableMarkup('Event recurrence'))
      ->setTargetEntityTypeId($entity_type->id())
      ->setSettings([
        'unsigned' => FALSE,
        'size' => 'normal',
        'target_type' => 'event_recurrence',
      ])
      ->setCardinality(1);
    return $definitions;
  }
}

/**
 * Implements hook_jsonapi_ENTITY_TYPE_filter_access().
 */
function intercept_event_jsonapi_event_attendance_filter_access(EntityTypeInterface $entity_type, AccountInterface $account) {
  return ([
    JSONAPI_FILTER_AMONG_ALL => AccessResult::allowedIfHasPermissions($account, ['view event_attendance', 'view referenced user event_attendance'], 'OR'),
  ]);
}

/**
 * Implements hook_jsonapi_ENTITY_TYPE_filter_access().
 */
function intercept_event_jsonapi_event_registration_filter_access(EntityTypeInterface $entity_type, AccountInterface $account) {
  return ([
    JSONAPI_FILTER_AMONG_ALL => AccessResult::allowedIfHasPermissions($account, ['view event_registration', 'view referenced user event_registration'], 'OR'),
  ]);
}

/**
 * Implements hook_views_data().
 */
function intercept_event_views_data(): array {
  $data['views']['intercept_event_views_filters_summary'] = [
    'title' => t('Intercept Events Views exposed filters summary'),
    'help' => t('Shows result summary with filters.'),
    'area' => [
      'id' => 'intercept_event_views_filters_summary',
    ],
  ];

  return $data;
}

/**
 * Implements hook_views_filters_summary_info_alter().
 */
function intercept_event_views_filters_summary_info_alter(array &$info, FilterPluginBase $filter) {
  $view = $filter->view;
  switch ($filter->getPluginId()) {
    case 'datetime':
      // We want to hide the date filters from the summary on the events calendar.
      // They are not relevant to the calendar since the calendar navigation dictates
      // the start and end times.
      if ($view->id() == 'intercept_events' && $view->current_display == 'events') {
        $info['value'] = [];
      }
      // Set filter value using the medium DateFormatter
      else if ($time = strtotime($filter->value['value'])) {
        $info['value'] = [
          [
            'id' => 0,
            'raw' => $filter->value['value'],
            'value' => \Drupal::service('date.formatter')->format(
              $time,
              'custom',
              'F d, Y'
            ),
          ],
        ];
      }
      break;
    case 'intercept_entity_reference':
      $values = [];
      foreach ($filter->value as $index => $id) {
        if (isset($filter->getValueOptions()[$id])) {
          $values[] = [
            'id' => $id,
            'raw' => $id,
            'value' => $filter->getValueOptions()[$id],
          ];
        }
      }
      $info['value'] = $values;
      break;
  }
}

/**
 * Implements hook_views_query_alter().
 */
function intercept_event_views_query_alter(ViewExecutable $view, QueryPluginBase $query) {
  if ($view->id() == 'intercept_event_attendance') {
    foreach ($query->where as &$condition_group) {
      // For each condition in condition group.
      foreach ($condition_group['conditions'] as &$condition) {
        // Attendance dates.
        if (isset($condition['field']) && strstr($condition['field'], 'event_attendance.created')) {
          // Run the query from the beginning of the start date
          // until the end of the end date.
          // First, find the end date chosen (timestamp).
          preg_match('/(\d{10}) AND (\d{10})/', $condition['field'], $matches);
          if (!empty($matches)) {
            $end_timestamp = $matches[2] + 86399;
            $condition['field'] = preg_replace("/(?<=\d{10} AND )\d{10}/", $end_timestamp, $condition['field']);
          }
        }
        // Event dates.
        elseif (isset($condition['field']) && strstr($condition['field'], 'field_date_time_value') && strstr($condition['field'], '<=')) {
          // Run the query from the beginning of the start date
          // until the end of the end date.
          $condition['field'] = preg_replace("/(?<=\<\= DATE_FORMAT\(\('\d{4}-\d{2}-\d{2}T)\d{2}:\d{2}:\d{2}' \+ INTERVAL [-+]?\d* SECOND\)/", "23:59:59')", $condition['field']);
        }
      }
    }
  }
  elseif ($view->id() == 'intercept_event_attendance_by_age_group') {
    $query->addField('node', 'nid', 'node_nid', ['function' => 'groupby']);
    $query->addGroupBy('node.nid');
    foreach ($query->where as &$condition_group) {
      // For each condition in condition group.
      foreach ($condition_group['conditions'] as &$condition) {
        // Event dates.
        if (isset($condition['field']) && strstr($condition['field'], 'field_date_time_value')) {
          // 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']);
        }
      }
    }
  }
  elseif ($view->id() == 'intercept_events' && $view->current_display == 'embed') {
    $query->addField('node', 'nid', 'node_nid', ['function' => 'groupby']);
    $query->addGroupBy('node.nid');
    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_date_time_value')) {
          // 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']);
        }
      }
    }
  }
  elseif ($view->id() == 'intercept_events' && $view->current_display == 'list_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_date_time') && strstr($condition['field'], '<=')) {
          // Run the query until the end of the end date.
          $condition['field'] = preg_replace("/(<= DATE_FORMAT\(\('\d{4}-\d{2}-\d{2}T)\d{2}:\d{2}:\d{2}' \+ INTERVAL [-+]?\d* SECOND/", '${1}23:59:59\'', $condition['field']);
        }
      }
    }
  }
}

/**
 * Implements hook_views_pre_build().
 */
function intercept_event_views_pre_build(ViewExecutable $view) {
  // Ensure a default start and end time is set.
  if ($view->id() == 'intercept_events' && $view->current_display == 'events') {
    $exposed_input = $view->getExposedInput();
    if (empty($exposed_input['start'])) {
      // Set default start to the beginning of this week. Note relative weeks start on Monday, so we subtract a day for Sunday.
      $default_value = empty($exposed_input['end'])
        ? strtotime('midnight this week -1 day')
        : strtotime('midnight ' . $exposed_input['end'] . ' -1 week');
      $exposed_input['start'] = date('Y-m-d', $default_value);
    }
    if (empty($exposed_input['end'])) {
      // Set default end to the start of next week. Note relative weeks start on Monday, so we subtract a day for Sunday.
      $default_value = empty($exposed_input['start'])
      ? strtotime('midnight next week -1 day')
      : strtotime('midnight ' . $exposed_input['start'] . ' +1 week');
      $exposed_input['end'] = date('Y-m-d', $default_value);
    }
    $view->setExposedInput($exposed_input);
  }
}

/**
 * Sanitize keywords
 *
 * @param $input
 *   The keywords string to sanitize.
 * @return string
 */
function intercept_event_sanitize_keywords($input) {
  // Clean up the keywords string to remove punctuation & extra white space.
  $keywords = urldecode($input);
  $keywords = preg_replace('/[Rr]ésumé/', 'résumé resume', $keywords);
  $keywords = preg_replace("/[^a-zA-Z 0-9\-]/", '', $keywords);
  $keywords = preg_replace('/\s+/', ' ', $keywords);
  $keywords = trim($keywords);

  return $keywords;
}


/**
 * Implements hook_views_pre_view().
 */
function intercept_event_views_pre_view(ViewExecutable $view, $display_id, array &$args) {
  if ($view->id() == 'intercept_event_attendance_by_age_group' && $display_id == 'page') {
    // Build the link/button to download the CSV.
    $request = \Drupal::request();
    $link_renderable = Link::createFromRoute('Download CSV', 'view.intercept_event_attendance_by_age_group.rest_export', ['_format' => 'csv'] + $request->query->all())->toRenderable();
    $link_renderable['#attributes'] = ['class' => ['button', 'create-content-button']];
    $link = \Drupal::service('renderer')->renderPlain($link_renderable);
    // Build button to download previous month's data.
    $link_renderable_prev = Link::createFromRoute('Download Previous Month to CSV', 'view.intercept_event_attendance_by_age_group.rest_export', [
      '_format' => 'csv',
      'field_date_time_value' => [
        'min' => date('Y-m-d', strtotime('first day of last month')),
        'max' => date('Y-m-d', strtotime('last day of last month')),
      ],
    ])->toRenderable();
    $link_renderable_prev['#attributes'] = ['class' => ['button', 'create-content-button', 'button--outline']];
    $link_prev = \Drupal::service('renderer')->renderPlain($link_renderable_prev);
    // Previous fiscal year button
    // First we'll determine if it is before or after June 30th.
    $last_year = date('Y', strtotime('last year'));
    if (strtotime('July 1') > time()) {
      // It's the first part of the year.
      $two_years_ago = date('Y', strtotime('-2 years'));
      $fy_min = 'July 1, ' . $two_years_ago;
      $fy_max = 'June 30, ' . $last_year;
    }
    else {
      // It's the last part of the year.
      $fy_min = 'July 1, ' . $last_year;
      $fy_max = 'June 30';
    }

    $link_renderable_fy = Link::createFromRoute('Download Previous Fiscal Year to CSV', 'view.intercept_event_attendance_by_age_group.rest_export', [
      '_format' => 'csv',
      'field_date_time_value' => [
        'min' => date('Y-m-d', strtotime($fy_min)),
        'max' => date('Y-m-d', strtotime($fy_max)),
      ],
    ])->toRenderable();
    $link_renderable_fy['#attributes'] = ['class' => ['button', 'create-content-button', 'button--outline']];
    $link_fy = \Drupal::service('renderer')->renderPlain($link_renderable_fy);
    // Output buttons.
    $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 Event Attendance</h1>' .
      $link . ' ' . $link_prev . ' ' . $link_fy,
      'plugin_id' => 'text_custom',
    ];
    $view->setHandler('page', 'header', 'area_text_custom', $options);

    // Default to showing data in the last few months.
    $exposed_input = $view->getExposedInput();
    if (!isset($exposed_input['field_date_time_value']['min'])) {
      $date_min = new DateTime('first day of -1 month');
      $date_max = new DateTime('today');
      $exposed_date['field_date_time_value']['min'] = $date_min->format('Y-m-d');
      $exposed_date['field_date_time_value']['max'] = $date_max->format('Y-m-d');
      $view->setExposedInput(array_merge($exposed_input, $exposed_date));
    }
  }
  elseif ($view->id() == 'intercept_events') {
    switch ($view->current_display) {
      case 'embed':
        // Show future events by default.
        $exposed_input = $view->getExposedInput();
        if (!isset($exposed_input['field_date_time_value'])) {
          $date = new DateTime('now');
          $date_future = new DateTime('+3 months');
          $exposed_date['field_date_time_value']['min'] = $date->format('Y-m-d');
          $exposed_date['field_date_time_value']['max'] = $date_future->format('Y-m-d');
          $view->setExposedInput(array_merge($exposed_input, $exposed_date));
        }
        break;
      case 'list_page':
        // Show future events by default.
        $exposed_input = $view->getExposedInput();
        $altered_input = [];

        // If neither date_start nor date_end are set, set date_start to start of today.
        if (!isset($exposed_input['date_start']) && !isset($exposed_input['date_end'])) {
          $date_start = new DateTime('midnight today');
          $altered_input['date_start'] = $date_start->format('Y-m-d');
        }
        // Else if date_start is not set but date_end is set date_start to start -3 months before date_end.
        elseif (!isset($exposed_input['date_start']) && isset($exposed_input['date_end'])) {
          $date_start = new DateTime($exposed_input['date_end']);
          // Subtract 3 months from date_start
          $date_start->sub(new DateInterval('P3M'));
          $altered_input['date_start'] = $date_start->format('Y-m-d');
        }
        // Else if date_start comes after date_end unset date_end
        elseif (isset($exposed_input['date_start']) && isset($exposed_input['date_end']) && $exposed_input['date_start'] > $exposed_input['date_end']) {
          unset($exposed_input['date_end']);
        }

        // Sanitize keyword input.
        if (!empty($exposed_input['keyword'])) {
          $altered_input['keyword'] = intercept_event_sanitize_keywords($exposed_input['keyword']);
        }

        $view->setExposedInput(array_merge($exposed_input, $altered_input));
        break;
      case 'events':
        // // Show future events by default.
        $exposed_input = $view->getExposedInput();
        $altered_input = [];

        // Sanitize keyword input.
        if (!empty($exposed_input['keyword'])) {
          $altered_input['keyword'] = intercept_event_sanitize_keywords($exposed_input['keyword']);
        }

        $view->setExposedInput(array_merge($exposed_input, $altered_input));
        break;

      default:
        break;
    }
  }
}

/**
 * Implements hook_views_pre_render().
 */
function intercept_event_views_pre_render(ViewExecutable $view) {
  if ($view->id() == 'intercept_event_attendance_by_age_group') {
    foreach ($view->field as $field_name => $field) {
      if (strpos($field_name, 'field_attendees') === 0) {
        $terms = \Drupal::entityTypeManager()
          ->getStorage('taxonomy_term')
          ->loadTree('population_segment');
        foreach ($terms as $index => $term) {
          if ($index == $field->options['delta_offset']) {
            // Re-style the headers to use whatever Population Segments are
            // configured in the site. It should be the same order that they're
            // in in that taxonomy vocabulary. That would match up with the
            // "offset" that the person used in the views UI.
            $view->field[$field_name]->options['label'] = $term->name;
            $view->style_plugin->view->field[$field_name]->options['label'] = $term->name;
          }
        }
      }
    }
  }
}

/**
 * Sorts locations alphabetically. Returns an array of options.
 */
function intercept_event_sort_locations() {
  // Sort locations field alphabetically.
  $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);

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

  // Add the $options from above to our select list.
  return $options;
}

/**
 * Implements hook_cron().
 */
function intercept_event_cron() {

  // Housekeeping. Delete any existing PDF files in the tmp directory
  $public_file_path = \Drupal::service('file_system')->realpath(\Drupal::config('system.file')->get('default_scheme') . "://");
  $files = glob($public_file_path . '/intercept_event_tmp/*'); // get all file names

  foreach ($files as $file) { // iterate files
    if (is_file($file)) {
      unlink($file); // delete file
    }
  }
}

/**
 * Implements hook_mail_alter().
 */
function intercept_event_mail_alter(&$message) {
  // Send email to event organizer if there was a problem with the event.
  if ($message['id'] == 'webform_intercept_event_feedback_email_if_customer_wants_to_be_contacted') {
    // Get the event node id.
    $event_id = $message['params']['webform_submission']->get('entity_id')->value;
    if (!empty($event_id)) {
      // Get the event organizer's email address.
      $event = Node::load($event_id);
      $user = User::load($event->getOwner()->id());
      if ($user) {
        $email = $user->getEmail();
        // Add that to the "to" addresses for the message.
        $message['to'] .= ',' . $email;
      }
    }
  }
}
