<?php

/**
 * @file
 * Contains intercept_messages.module.
 */

use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Entity\EntityInterface;
use Drupal\node\Entity\Node;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Url;
use Drupal\intercept_messages\Form\EventAttendanceMessagesSettings;
use Drupal\intercept_messages\Form\EventRegistrationMessagesSettings;
use Drupal\intercept_messages\Form\EventSavedMessagesSettings;
use Drupal\intercept_messages\Form\UserSettings;
use Drupal\intercept_messages\Utility\MessageSettingsHelper;

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

    default:
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function intercept_messages_form_event_attendance_settings_alter(array &$form, FormStateInterface $form_state) {
  \Drupal::service('class_resolver')
    ->getInstanceFromDefinition(EventAttendanceMessagesSettings::class)
    ->alterSettingsForm($form, $form_state);
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function intercept_messages_form_event_registration_settings_alter(array &$form, FormStateInterface $form_state) {
  \Drupal::service('class_resolver')
    ->getInstanceFromDefinition(EventRegistrationMessagesSettings::class)
    ->alterSettingsForm($form, $form_state);
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function intercept_messages_form_event_saved_settings_alter(array &$form, FormStateInterface $form_state) {
  \Drupal::service('class_resolver')
    ->getInstanceFromDefinition(EventSavedMessagesSettings::class)
    ->alterSettingsForm($form, $form_state);
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function intercept_messages_form_user_form_alter(array &$form, FormStateInterface $form_state) {
  \Drupal::service('class_resolver')
    ->getInstanceFromDefinition(UserSettings::class)
    ->alterSettingsForm($form, $form_state);
}

/**
 * Implements hook_entity_insert().
 */
function intercept_messages_entity_insert(EntityInterface $entity) {
  $entity_type_id = $entity->getEntityTypeId();
  $bundle = $entity->bundle();
  if (!in_array($entity_type_id, ['event_attendance', 'event_registration', 'flagging', 'node'])) {
    return;
  }
  if ($entity_type_id == 'flagging' && $bundle != 'saved_event') {
    // The only type of flags we're worried about are saved events.
    return;
  }
  if ($entity_type_id == 'node' && $bundle != 'event') {
    // We do have a scheduled message for events but not other node types.
    return;
  }
  $templates = \Drupal::service('plugin.manager.intercept_message_template')->getDefinitionsByType($entity_type_id);
  $event_config = \Drupal::config('intercept_event.settings');
  if ($event_config && is_array($event_config->get('email')) && $bundle != 'event') {
    $filtered_messages = array_filter($event_config->get('email'), function ($message_id) use ($templates) {
      return in_array($message_id, array_column($templates, 'id'));
    }, ARRAY_FILTER_USE_KEY);
    if ($entity_type_id == 'flagging') {
      // Get the associated event for a flagging.
      $saved_event_id = $entity->get('entity_id')->getString();
      $event = Node::load($saved_event_id);
      $registration = $event->get('field_must_register')->value;
    }
    else {
      $event = $entity->getEvent();
    }
    foreach ($filtered_messages as $plugin_id => $message_setting) {

      if ($message_setting['enabled'] && $interval = NestedArray::getValue($message_setting, ['schedule', 'interval'])) {
        $interval_time = \Drupal::service('duration_field.service')->getSecondsFromDurationString($interval);
        if ($entity_type_id == 'flagging') {
          if ($registration == TRUE) {
            // Don't send messages for saves when registration is required.
            continue;
          }
        }
        if ($event->hasField('field_date_time')) {
          switch ($entity_type_id) {
            case 'event_attendance':
              $event_date = $event->get('field_date_time')->end_date;
              $send_timestamp = $event_date->format('U') + $interval_time;
              break;

            case 'event_registration':
            case 'flagging':
              // When the customer flags/saves the event, we're going to use the
              // difference between the start date and the selected interval
              // to build the timestamp.
              $event_date = $event->get('field_date_time')->start_date;
              $send_timestamp = $event_date->format('U') - $interval_time;
              // The send time must be in the future.
              if ($send_timestamp < time()) {
                return;
              }
              if ($entity_type_id == 'event_registration') {
                // Block this if the event_registration is in a "waitlist"
                // status and not active.
                $status = $entity->get('status')->getString();
                if ($status !== 'active') {
                  return;
                }
              }
              break;
          }
          intercept_messages_schedule_message($plugin_id, $entity_type_id, $entity->id(), 0, $send_timestamp);
        }
      }
      elseif ($message_setting['enabled'] && $plugin_id == 'saved_registration_active' && $registration == TRUE) {
        // When we want to alert the customer that registration is opening,
        // the send_timestamp should be the exact same date that
        // registration opens.
        $event_date = $event->get('field_event_register_period')->start_date;
        $send_timestamp = $event_date->format('U');
        if ($send_timestamp < time()) {
          continue;
        }
        intercept_messages_schedule_message('saved_registration_active', $entity_type_id, $entity->id(), 0, $send_timestamp);
      }
    }
  }
  elseif ($entity_type_id == 'node' && $bundle == 'event') {
    // Schedule a message that will go to staff after an event to remind them
    // to enter their number of attendees and note evaluating the event.

    // When the node is created by the staff member schedule the email in
    // advance of the end date.

    // Email would need to be sent directly following the event end time.
    $event_date = $entity->get('field_date_time')->end_date;
    // Send the message 24 hours after the event ends.
    $send_timestamp = $event_date->format('U') + 86400;
    if ($send_timestamp >= time()) {
      intercept_messages_schedule_message('event_staff_reminder', $entity_type_id, $entity->id(), 0, $send_timestamp);
    }
  }
}

/**
 * Inserts/schedules a message into the database table.
 */
function intercept_messages_schedule_message($template_id, $entity_type_id, $entity_id, $is_sent, $send_timestamp) {
  \Drupal::service('database')
    ->insert('intercept_messages_scheduled_message')
    ->fields([
      'template_id' => $template_id,
      'entity_type' => $entity_type_id,
      'entity_id' => $entity_id,
      'is_sent' => $is_sent,
      'send' => $send_timestamp,
    ])
    ->execute();
}

/**
 * Implements hook_entity_update().
 */
function intercept_messages_entity_update(EntityInterface $entity) {
  // Remove registration reminders when registration is canceled.
  if ($entity->getEntityTypeId() == 'event_registration' && $entity->status->value == 'canceled') {
    $messages = \Drupal::service('database')
      ->select('intercept_messages_scheduled_message', 'm')
      ->fields('m', [
        'message_id',
      ])
      ->condition('entity_id', $entity->id())
      ->execute()
      ->fetchAll();
    foreach ($messages as $message) {
      intercept_messages_delete_message($message->message_id);
    }
    return;
  }
  elseif ($entity->getEntityTypeId() !== 'node' || $entity->bundle() !== 'event') {
    return;
  }
  $dateTime = new DrupalDateTime($entity->get('field_date_time')->value, 'UTC');
  $event_date = $dateTime->getTimestamp();
  // Send any update messages immediately.
  $timestamp = $send_timestamp = strtotime('now');
  /** @var \Drupal\node\NodeInterface $entity */
  if (!$entity->isPublished() || ($entity->hasField('field_event_status') && $entity->field_event_status->value == 'canceled')) {
    $event_registration_provider = \Drupal::service('intercept_event.event_registration_provider');
    /** @var \Drupal\intercept_event\EventRegistrationProviderInterface $event_registration_provider */
    foreach ($event_registration_provider->getEventRegistrationIdsByEvent($entity) as $registration_id) {
      // Delete existing event registration reminder messages.
      $messages = \Drupal::service('database')
        ->select('intercept_messages_scheduled_message', 'm')
        ->fields('m', [
          'message_id',
        ])
        ->condition('entity_id', $registration_id)
        ->execute()
        ->fetchAll();
      foreach ($messages as $message) {
        intercept_messages_delete_message($message->message_id);
      }
      // Insert messages to notify registered customers that the registration
      // event has been canceled, but not if it was just unpublished.
      // Also make sure it's a future event before sending.
      if ($entity->hasField('field_event_status') && $entity->field_event_status->value == 'canceled') {
        // Future event. Send it!
        if ($event_date >= $timestamp) {
          \Drupal::service('database')
            ->insert('intercept_messages_scheduled_message')
            ->fields([
              'template_id' => 'registration_event_canceled',
              'entity_type' => 'event_registration',
              'entity_id' => $registration_id,
              'is_sent' => 0,
              'send' => $send_timestamp,
            ])
            ->execute();
          // Let's also cancel this customer's event registration.
          $registration_entity = reset($event_registration_provider->getEventRegistrationsByIds([$registration_id]));
          $registration_entity->set('status', 'canceled');
          $registration_entity->save();
        }
      }
    }
    // Find flaggings/saves that pertain to this event node.
    $flag = \Drupal::service('flag')->getFlagById('saved_event');
    $flaggings = \Drupal::service('flag')->getEntityFlaggings($flag, $entity);
    foreach ($flaggings as $flagging) {
      // Delete existing event save reminder messages.
      $messages = \Drupal::service('database')
        ->select('intercept_messages_scheduled_message', 'm')
        ->fields('m', [
          'message_id',
        ])
        ->condition('entity_id', $flagging->id())
        ->execute()
        ->fetchAll();
      foreach ($messages as $message) {
        intercept_messages_delete_message($message->message_id);
      }
    }
  }
  // If the event is edited, then update the scheduled staff reminder message.
  if ($entity->getEntityTypeId() == 'node' && $entity->bundle() == 'event') {
    // Find the new end time
    $event_date = $entity->get('field_date_time')->end_date;
    // Send the message 24 hours after the event ends.
    $send_timestamp = $event_date->format('U') + 86400;
    if ($send_timestamp >= time()) {
      $messages = \Drupal::service('database')
        ->update('intercept_messages_scheduled_message')
        ->fields([
          'send' => $send_timestamp,
        ])
        ->condition('entity_id', $entity->id())
        ->condition('template_id', 'event_staff_reminder')
        ->execute();
    }
  }
}

/**
 * Implements hook_entity_delete().
 */
function intercept_messages_entity_delete(EntityInterface $entity) {
  // The only type of flags we're worried about are saved events or event nodes.
  if (($entity->getEntityTypeId() == 'flagging' && $entity->bundle() == 'saved_event') || ($entity->getEntityTypeId() == 'node' && $entity->bundle() == 'event')) {
    // Delete existing event save reminder messages.
    $messages = \Drupal::service('database')
      ->select('intercept_messages_scheduled_message', 'm')
      ->fields('m', [
        'message_id',
      ])
      ->condition('entity_id', $entity->id())
      ->execute()
      ->fetchAll();
    foreach ($messages as $message) {
      intercept_messages_delete_message($message->message_id);
    }
  }
}

/**
 * Implements hook_cron().
 */
function intercept_messages_cron() {
  $messages = \Drupal::service('database')
    ->select('intercept_messages_scheduled_message', 'm')
    ->fields('m', [
      'message_id',
      'entity_type',
      'entity_id',
      'template_id',
    ])
    ->condition('is_sent', 0)
    ->condition('send', time(), '<')
    ->execute()
    ->fetchAll();
  if (!empty($messages)) {
    $event_config = \Drupal::config('intercept_event.settings')->get();
    foreach ($messages as $message) {
      if ($email_setting = NestedArray::getValue($event_config, ['email', $message->template_id])) {
        if ($email_setting['enabled'] && $entity = \Drupal::entityTypeManager()->getStorage($message->entity_type)->load($message->entity_id)) {
          switch ($message->entity_type) {
            case 'event_attendance':
            default:
              $emails = MessageSettingsHelper::getEventAttendanceEmails($entity, $email_setting);
              break;

            case 'event_registration':
              $emails = MessageSettingsHelper::getEventRegistrationEmails($entity, $email_setting);
              break;

            // Saved events.
            case 'flagging':
              $emails = MessageSettingsHelper::getEventSavedEmails($entity, $email_setting);
              break;
          }
          foreach ($emails as $email) {
            MessageSettingsHelper::mail($entity, $email, [
              'mail_key' => $message->template_id,
              'subject' => $email_setting['subject'],
              'body' => $email_setting['body'],
            ]);
          }
          intercept_messages_delete_message($message->message_id);
        }
      }
      elseif ($message->template_id == 'event_staff_reminder'  && $entity = \Drupal::entityTypeManager()->getStorage($message->entity_type)->load($message->entity_id)) {

        // Email would be sent to the event organizer.
        // If this field is empty, send email to staff presenter.
        // NOTE: ^^ Can't do this because Staff presenter field is plain text.
        $user = $entity->getOwner();
        $email = $user->getEmail();
        $uid = $user->id();
        $name = $user->name->value;
        $body = "";

        // Try to see if there's a profile for this staff member.
        $types = \Drupal::entityTypeManager()->getStorage('node_type')->loadMultiple();
        if (isset($types['person'])) {
          $query = \Drupal::entityQuery('node')
            ->condition('status', 1)
            ->condition('type', 'person')
            ->condition('field_associated_user_account', $uid);
          $query->accessCheck();
          $nids = $query->execute();
          if (!empty($nids)) {
            $node = \Drupal::entityTypeManager()->getStorage('node')->load(array_pop($nids));
            if (!empty($node)) {
              $name = $node->get('field_text_first_name')->getValue();
              $name = $name[0]['value'];
            }
          }
          $body = "\r\n<br><br>Sincerely,\r\n<br><br>EventsandExperience_mg";
          $body .= '@' . str_replace('staging-', '', str_replace('-site', 'library', str_replace('.pantheonsite.io', '.com', str_replace('.lndo.site', '.com', str_replace('/', '', str_replace('www', '', str_replace('https://', '', Url::fromRoute('<front>', [], ['absolute' => TRUE])->toString())))))));
        }

        MessageSettingsHelper::mail($entity, $email, [
          'mail_key' => 'event_staff_reminder',
          'subject' => 'Reminder: Event Attendance and Feedback for [node:title]',
          'body' => "Hello " . $name . ". We hope your program, [node:title] was a success! Don't forget to input your <a href=\"[site:url]event/" . $message->entity_id . "/analysis\">program attendance and feedback</a> on the website." . $body . "|Add Attendance and Feedback|[site:url]event/" . $message->entity_id . "/analysis",
        ]);
        intercept_messages_delete_message($message->message_id);
      }
    }
  }
}

/**
 * Deletes a message.
 */
function intercept_messages_delete_message($message_id) {
  \Drupal::service('database')
    ->delete('intercept_messages_scheduled_message')
    ->condition('message_id', $message_id)
    ->execute();
}

/**
 * Implements hook_mail().
 */
function intercept_messages_mail($key, &$message, $params) {
  $token_service = \Drupal::token();
  $context = $params['context'];
  $message['headers']['Content-Type'] = 'text/html';
  $message['from'] = \Drupal::config('system.site')->get('mail');
  $message['subject'] = $token_service->replace($context['subject'], $context['tokens']);
  $body = $token_service->replace($context['body'], $context['tokens']);
  $renderable = [
    '#theme' => 'intercept_messages_email',
    '#message' => html_entity_decode($body),
  ];
  $message['body'][] = \Drupal::service('renderer')->render($renderable);
}

/**
 * Implements hook_theme().
 */
function intercept_messages_theme($existing, $type, $theme, $path) {
  return [
    'intercept_messages_email' => [
      'variables' => ['message' => ''],
    ],
  ];
}
