<?php

/**
 * @file
 * Contains intercept_equipment.module.
 */

use Drupal\Core\Access\AccessResult;
use Drupal\Core\Database\Database;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Link;
use Drupal\Core\Render\Element;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\node\Entity\Node;
use Drupal\node\NodeInterface;
use Drupal\taxonomy\Entity\Term;
use Drupal\views\ViewExecutable;
use Drupal\views\Plugin\views\query\QueryPluginBase;

/**
 * Implements hook_help().
 */
function intercept_equipment_help($route_name, RouteMatchInterface $route_match) {
  switch ($route_name) {
    // Main module help for the intercept_equipment module.
    case 'help.page.intercept_equipment':
      $output = '';
      $output .= '<h3>' . new TranslatableMarkup('About') . '</h3>';
      $output .= '<p>' . new TranslatableMarkup('Provides equipment and equipment reservations for Intercept.') . '</p>';
      return $output;

    default:
  }
}

/**
 * Implements hook_jsonapi_ENTITY_TYPE_filter_access().
 */
function intercept_equipment_jsonapi_equipment_reservation_filter_access(EntityTypeInterface $entity_type, AccountInterface $account) {
  return ([
    // @todo CRL-163: The 'view referenced user equipment_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 equipment_reservation', 'view referenced user equipment_reservation'], 'OR'),
    JSONAPI_FILTER_AMONG_ALL => AccessResult::allowedIfHasPermissions($account, ['view equipment_reservation'], 'OR'),
    JSONAPI_FILTER_AMONG_OWN => AccessResult::allowedIfHasPermission($account, 'view own equipment_reservation'),
  ]);
}

/**
 * Implements hook_node_access().
 */
function intercept_equipment_node_access(NodeInterface $node, $op, AccountInterface $account) {
  if ($node->bundle() == 'equipment' && $op == 'view') {
    return AccessResult::forbiddenIf(!$account->hasPermission('view reservation content'))
      ->cachePerPermissions()
      ->cachePerUser()
      ->addCacheableDependency($node);
  }
  // No opinion.
  return AccessResult::neutral();
}

/**
 * Implements hook_update_N().
 *
 * Install temporary database table so that we can uninstall and reinstall
 * module with new equipment reservation entity.
 */
function intercept_equipment_update_8001() {
  $spec = [
    'description' => 'Equipment Reservations',
    'fields' => [
      'id' => [
        'type' => 'serial',
        'unsigned' => TRUE,
        'not null' => TRUE,
      ],
      'vid' => [
        'type' => 'int',
        'length' => 11,
        'not null' => TRUE,
        'default' => 0,
      ],
    ],
    'primary key' => ['id'],
  ];
  $schema = Database::getConnection()->schema();
  if ($schema->tableExists('equipment_reservation') == FALSE) {
    $schema->createTable('equipment_reservation', $spec);
  }
}

/**
 * Implements hook_mail().
 */
function intercept_equipment_mail($key, &$message, $params) {
  $options = [
    'langcode' => $message['langcode'],
  ];
  switch ($key) {
    case 'created':
      $message['from'] = \Drupal::config('system.site')->get('mail');
      $message['subject'] = new TranslatableMarkup('Equipment Request - @equipment_title for @username', ['@username' => $params['username'], '@equipment_title' => $params['equipment_title']], $options);
      $message['body'] = [];
      $message['body'][] = new TranslatableMarkup("The following request was submitted:\n\nContact: @email_address\n Location: @location_title\n Room: @room_title\n Event: @event_title\n Equipment: @equipment_title\n Start: @reservation_start_date\n End: @reservation_end_date", [
        '@email_address' => $params['email_address'],
        '@username' => $params['username'],
        '@equipment_title' => $params['equipment_title'],
        '@reservation_start_date' => $params['reservation_start_date'],
        '@reservation_end_date' => $params['reservation_end_date'],
        '@location_title' => $params['location_title'],
        '@room_title' => $params['room_title'],
        '@event_title' => $params['event_title'],
      ], $options);
      break;
    case 'canceled':
      $message['from'] = \Drupal::config('system.site')->get('mail');
      $message['subject'] = new TranslatableMarkup('Equipment Request Canceled - @equipment_title for @username', ['@username' => $params['username'], '@equipment_title' => $params['equipment_title']], $options);
      $message['body'] = [];
      $message['body'][] = new TranslatableMarkup("The following request was canceled:\n\n Contact: @email_address\n Location: @location_title\n Room: @room_title\n Event: @event_title\n Equipment: @equipment_title\n Start: @reservation_start_date\n End: @reservation_end_date", [
        '@email_address' => $params['email_address'],
        '@username' => $params['username'],
        '@equipment_title' => $params['equipment_title'],
        '@reservation_start_date' => $params['reservation_start_date'],
        '@reservation_end_date' => $params['reservation_end_date'],
        '@location_title' => $params['location_title'],
        '@room_title' => $params['room_title'],
        '@event_title' => $params['event_title'],
      ], $options);
      break;
  }
}

/**
 * Reusable function to send emails.
 */
function intercept_equipment_send_email($entity, $key) {

  // Get the "to" email address.
  $equipment_nid = $entity->get('field_equipment')->getValue();
  $equipment_node = Node::load($equipment_nid[0]['target_id']);

  // Get the equipment node's taxonomy term
  // (e.g., Technology, Operations, etc.)
  $equipment_type = $equipment_node->get('field_equipment_type')->getValue();
  $equipment_type = $equipment_type[0]['target_id'];
  $equipment_term = Term::load($equipment_type);
  // Email the appropriate email address(es) as specified on the term.
  $email_addresses = $equipment_term->get('field_email')->getValue();
  $email_addresses = $email_addresses[0];

  if (!empty($email_addresses)) {
    // Set up variables used in the subject/body of the emails.
    // Get requestor/current user's email address.
    $user = \Drupal::currentUser();
    $email = $user->getEmail();
    // Username of requestor.
    $username = $user->getDisplayName();
    // Equipment title.
    $equipment_title = $equipment_node->getTitle();
    // Dates/times of reservation.
    $reservation_dates = $entity->get('field_dates')->getValue();
    $dateTime = new DrupalDateTime($reservation_dates[0]['value'], 'UTC');
    $reservation_start_date = date('m-d-Y g:i A', $dateTime->getTimestamp());
    $dateTime = new DrupalDateTime($reservation_dates[0]['end_value'], 'UTC');
    $reservation_end_date = date('m-d-Y g:i A', $dateTime->getTimestamp());
    // Location.
    $location_nid = $entity->get('field_location')->getValue();
    $location_node = Node::load($location_nid[0]['target_id']);
    $location_title = $location_node->getTitle();
    // Room.
    $room_nid = $entity->get('field_room')->getValue();
    if ($room_nid) {
      $room_node = Node::load($room_nid[0]['target_id']);
      $room_title = $room_node->getTitle();
    }
    else {
      $room_title = '';
    }
    // Event.
    $event_nid = $entity->get('field_event')->getValue();
    if ($event_nid) {
      $event_node = Node::load($event_nid[0]['target_id']);
      $event_title = $event_node->getTitle();
    }
    else {
      $event_title = '';
    }

    // Send it!
    $mailManager = \Drupal::service('plugin.manager.mail');
    $module = 'intercept_equipment';
    $langcode = \Drupal::languageManager()->getCurrentLanguage()->getId();
    $params = [
      'email_address' => $email,
      'username' => $username,
      'equipment_title' => $equipment_title,
      'reservation_start_date' => $reservation_start_date,
      'reservation_end_date' => $reservation_end_date,
      'location_title' => $location_title,
      'room_title' => $room_title,
      'event_title' => $event_title,
    ];
    $send = TRUE;
    foreach ($email_addresses as $email_address) {
      $to = $email_address;
      $mailManager->mail($module, $key, $to, $langcode, $params, NULL, $send);
    }
  }
}

/**
 * Implements hook_ENTITY_TYPE_insert().
 */
function intercept_equipment_equipment_reservation_insert(EntityInterface $entity) {
  intercept_equipment_send_email($entity, 'created');
}

/**
 * Implements hook_ENTITY_TYPE_delete().
 */
function intercept_equipment_equipment_reservation_delete(EntityInterface $entity) {
  intercept_equipment_send_email($entity, 'canceled');
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function intercept_equipment_form_views_exposed_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  $view = $form_state->get('view');
  if ($view->id() == 'intercept_equipment_reservations' && $view->current_display == 'embed') {

    // Change the labels on the min and max date fields.
    $form['field_dates_value_1_wrapper']['field_dates_value_1']['min']['#title'] = new TranslatableMarkup('Dates between');
    $form['field_dates_value_1_wrapper']['field_dates_value_1']['max']['#title'] = new TranslatableMarkup('And');

    // Change the locations field to be a dropdown.
    // See: https://atendesigngroup.com/blog/drupal-8-views-entity-reference-exposed-filter-select-list
    // Query nodes.
    $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 = 'title_1';
    $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']);
  }
  elseif ($view->id() == 'intercept_export_equipment_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');
  }
}

/**
 * Implements hook_views_pre_view().
 */
function intercept_equipment_views_pre_view(ViewExecutable $view, $display_id, array &$args) {
  if ($view->id() == 'intercept_equipment_reservations' && $display_id == 'embed') {
    // Show future equipment reservations by default.
    $exposed_input = $view->getExposedInput();
    if (!isset($exposed_input['field_dates_value_1'])) {
      $date = new DateTime('now');
      $date_future = new DateTime('+3 months');
      $exposed_date['field_dates_value_1']['min'] = $date->format('Y-m-d');
      $exposed_date['field_dates_value_1']['max'] = $date_future->format('Y-m-d');
      $view->setExposedInput(array_merge($exposed_input, $exposed_date));
    }
  }
  elseif ($view->id() == 'intercept_export_equipment_reservations' && $display_id == 'page') {
    // Build the link to download the CSV.
    $request = \Drupal::request();
    $link_renderable = Link::createFromRoute('Download CSV', 'view.intercept_export_equipment_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 Equipment Reservations</h1>' .
      $link,
      'plugin_id' => 'text_custom',
    ];
    $view->setHandler('page', 'header', 'area_text_custom', $options);
  }
}

/**
 * Implements hook_theme().
 */
function intercept_equipment_theme() {
  return [
    'equipment_reservation' => [
      'render element' => 'elements',
    ],
  ];
}

/**
 * Prepares variables for Equipment reservation templates.
 *
 * Default template: equipment_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_equipment_reservation(array &$variables) {
  // Fetch EquipmentReservation Entity Object.
  $equipment_reservation = $variables['equipment_reservation'] = $variables['elements']['#equipment_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'] = $equipment_reservation->label();
}

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

  $suggestions[] = 'equipment_reservation__' . $sanitized_view_mode;
  $suggestions[] = 'equipment_reservation__' . $equipment_reservation->bundle();
  $suggestions[] = 'equipment_reservation__' . $equipment_reservation->bundle() . '__' . $sanitized_view_mode;

  return $suggestions;
}

/**
 * Implements hook_views_query_alter().
 */
function intercept_equipment_views_query_alter(ViewExecutable $view, QueryPluginBase $query) {
  if ($view->id() == 'intercept_equipment_reservations' && $view->current_display == 'embed') {
    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')) {
          // Run the query from the beginning of the start date
          // until the end of the end date.
          $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']);
          $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']);
        }
      }
    }
  }
}
