<?php

/**
 * @file
 * Primary module hooks for social_group_request module.
 */

use Drupal\block\Entity\Block;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\EntityStorageException;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Link;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Url;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\grequest\Plugin\GroupContentEnabler\GroupMembershipRequest;
use Drupal\group\Entity\GroupContentTypeInterface;
use Drupal\group\Entity\GroupContentInterface;
use Drupal\views\ViewExecutable;
use Drupal\message\Entity\Message;
use Drupal\taxonomy\Entity\Term;

/**
 * Implements hook_ENTITY_TYPE_insert() for group_content_type.
 */
function social_group_request_group_content_type_insert(GroupContentTypeInterface $group_content_type) {
  if ($group_content_type->getContentPluginId() === 'group_membership_request') {
    // Add Message field.
    FieldConfig::create([
      'field_storage' => FieldStorageConfig::loadByName('group_content', 'field_grequest_message'),
      'bundle' => $group_content_type->id(),
      'label' => t('Message'),
      'description' => '',
      'required' => FALSE,
      'settings' => [],
      'field_type' => 'string_long',
    ])->save();
  }
}

/**
 * Implements hook_entity_base_field_info().
 */
function social_group_request_entity_base_field_info(EntityTypeInterface $entity_type) {
  $fields = [];

  if ($entity_type->id() === 'group') {
    $fields['allow_request'] = BaseFieldDefinition::create('boolean')
      ->setLabel(t('Allow users to request membership'))
      ->setRevisionable(TRUE)
      ->setTranslatable(TRUE)
      ->setDefaultValue(FALSE)
      ->setDisplayOptions('form', [
        'type' => 'boolean_checkbox',
        'settings' => [
          'display_label' => TRUE,
        ],
        'weight' => 99,
      ])
      ->setDisplayConfigurable('form', TRUE);
  }

  return $fields;
}

/**
 * Implements hook_social_group_join_method_usage().
 */
function social_group_request_social_group_join_method_usage(): array {
  return [
    [
      'entity_type' => 'group',
      'bundle' => 'closed_group',
      'field' => 'allow_request',
      'method' => 'request',
    ],
  ];
}

/**
 * Implements hook_social_group_join_method_info().
 */
function social_group_request_social_group_join_method_info(): array {
  return [
    'request' => [
      'title' => t('Request to join'),
      'description' => t('users can "request to join" this @entity_type_id which
@entity_type_id managers approve/decline.'),
      'icon' => 'join_close',
      'weight' => 15,
    ],
  ];
}

/**
 * Implements hook_ENTITY_TYPE_insert().
 */
function social_group_request_group_content_insert(GroupContentInterface $group_content) {
  $type = $group_content->getContentPlugin()->getPluginId();
  $membership_request_enabled = $group_content->getGroup()->getGroupType()->hasContentPlugin('group_membership_request');

  // If new membership is created, check related membership request exists.
  if ($type === 'group_membership' && $membership_request_enabled) {
    $approver = \Drupal::currentUser()->id();
    $storage = \Drupal::entityTypeManager()->getStorage('group_content');
    $filters = [
      'entity_id' => $group_content->getEntity()->id(),
      'grequest_status' => GroupMembershipRequest::REQUEST_PENDING,
    ];
    $requests = $storage->loadByGroup($group_content->getGroup(), 'group_membership_request', $filters);

    /** @var \Drupal\group\Entity\GroupContentInterface $request */
    foreach ($requests as $request) {
      try {
        $request
          ->set('grequest_status', GroupMembershipRequest::REQUEST_ACCEPTED)
          ->set('grequest_updated_by', $approver)
          ->save();

        \Drupal::messenger()->addStatus(t('Membership request approved'));
      }
      catch (EntityStorageException $exception) {
        \Drupal::logger('social_group_request')->critical("Exception trying to save a group membership request: @message", ['@message' => $exception->getMessage()]);
        \Drupal::messenger()->addError(t('Error updating Request'));
      }
    }
  }
}

/**
 * Implements hook_module_implements_alter().
 */
function social_group_request_module_implements_alter(&$implementations, $hook) {
  if ($hook == 'group_content_insert') {
    // Remove the grequest implementation, we have a fallback in our hook.
    if (isset($implementations['grequest'])) {
      unset($implementations['grequest']);
    }
  }
}

/**
 * Implements hook_theme_registry_alter().
 */
function social_group_request_theme_registry_alter(&$theme_registry) {
  // Unfortunately the preprocess functions aren't ordered by module weight.
  // Changing module weight doesn't work, also with dependency set to
  // social_group this should be dealt with but isn't.
  // So we enforce our preprocess after social_group.
  if (!empty($theme_registry['group']['preprocess functions'])) {
    $current_key = array_search('social_group_request_preprocess_group', $theme_registry['group']['preprocess functions'], FALSE);
    unset($theme_registry['group']['preprocess functions'][$current_key]);
    // Give it a new key all the way at the end.
    $theme_registry['group']['preprocess functions'][] = 'social_group_request_preprocess_group';
  }
}

/**
 * Implements hook_preprocess_HOOK().
 */
function social_group_request_preprocess_group(array &$variables): void {
  if (!\Drupal::config('social_group.settings')->get('social_group_type_required')) {
    return;
  }

  /** @var \Drupal\social_group\SocialGroupInterface $group */
  $group = $variables['group'];

  if (!$group->hasField('field_group_type')) {
    return;
  }

  /** @var \Drupal\social_group\JoinManagerInterface $manager */
  $manager = \Drupal::service('plugin.manager.social_group.join');

  /** @var string $bundle */
  $bundle = $group->getGroupType()->id();

  if ($manager->hasMethod($bundle, 'request')) {
    $field = $group->field_group_type;

    if (!$field->isEmpty()) {
      /** @var \Drupal\taxonomy\TermInterface $term */
      $term = $field->entity;

      if ($term instanceof Term) {
        $variables['group_type'] = $term->getName();
        $variables['group_type_icon'] = $term->field_group_type_icon->value;
      }
    }
  }
}

/**
 * Implements hook_activity_send_email_notifications_alter().
 */
function social_group_request_activity_send_email_notifications_alter(array &$items, array $email_message_templates) {
  if (isset($email_message_templates['request_to_join_a_group'])) {
    $items['what_manage']['templates'][] = 'request_to_join_a_group';
  }

  if (isset($email_message_templates['approve_request_join_group'])) {
    $items['message_to_me']['templates'][] = 'approve_request_join_group';
  }
}

/**
 * Implements hook_form_alter().
 */
function social_group_request_form_alter(array &$form, FormStateInterface $form_state, string $form_id): void {
  $social_group_types = [
    'open_group',
    'closed_group',
    'public_group',
  ];

  \Drupal::moduleHandler()->alter('social_group_types', $social_group_types);

  if (
    \Drupal::routeMatch()->getRouteName() === 'grequest.group_request_membership_approve' &&
    preg_match('/^group_content_(' . implode('|', $social_group_types) . ')-group_membership_add_form$/', $form_id)
  ) {
    // Name of user which we're adding to the group.
    $user_name = $form['entity_id']['widget'][0]['target_id']['#default_value']
      ->getDisplayName();

    $form['question'] = [
      '#type' => 'html_tag',
      '#tag' => 'p',
      '#value' => t(
        'Are you sure you want to approve the membership request for @name?',
        ['@name' => $user_name],
      ),
      '#weight' => 1,
    ];

    // Remove possibility to select members to add to group.
    if (isset($form['entity_id']['widget'][0]['target_id'])) {
      unset($form['entity_id']);
    }

    if (isset($form['group_roles']['widget'])) {
      unset($form['group_roles']);
    }

    $form['actions']['submit']['#value'] = t('Yes');
  }
  elseif (preg_match(
    '/^group_(' . implode('|', $social_group_types) . ')_(add|edit)_form$/',
    $form_id,
  )) {
    /** @var \Drupal\Core\Entity\EntityFormInterface $form_object */
    $form_object = $form_state->getFormObject();

    /** @var \Drupal\group\Entity\GroupInterface $group */
    $group = $form_object->getEntity();

    $group_type = $group->getGroupType();
    $prohibit = FALSE;

    if ($group_type->hasContentPlugin('group_membership_request')) {
      /** @var \Drupal\social_group\JoinManagerInterface $manager */
      $manager = \Drupal::service('plugin.manager.social_group.join');

      /** @var string $bundle */
      $bundle = $group_type->id();

      $found = FALSE;

      foreach ($manager->relations() as $relation) {
        if (
          $relation['entity_type'] === 'group' &&
          isset($relation['bundle'], $relation['method']) &&
          in_array($bundle, (array) $relation['bundle']) &&
          in_array('request', (array) $relation['method'])
        ) {
          $found = TRUE;

          break;
        }
      }

      if (!$found) {
        $prohibit = TRUE;
      }
    }
    else {
      $prohibit = TRUE;
    }

    if ($prohibit) {
      unset($form['allow_request']);
    }
  }
}

/**
 * Implements hook_preprocess_activity().
 */
function social_group_request_preprocess_activity(array &$variables) {
  /** @var \Drupal\activity_creator\ActivityInterface $activity */
  $activity = $variables['elements']['#activity'];

  if (!$activity->get('field_activity_message')->isEmpty()) {
    /** @var Drupal\message\Entity\Message $message */
    $message = $activity->field_activity_message->entity;

    if ($message instanceof Message && !$message->get('template')->isEmpty()) {
      switch ($message->template->target_id) {
        case 'request_to_join_a_group':
          $activity_entity = $activity->getRelatedEntity();
          if ($activity_entity instanceof GroupContentInterface) {
            $variables['full_url'] = Url::fromRoute('view.group_pending_members.membership_requests', [
              'arg_0' => $activity_entity->getGroup()->id(),
            ]);
          }
          break;

        case 'approve_request_join_group':
          $activity_entity = $activity->getRelatedEntity();
          if ($activity_entity instanceof GroupContentInterface) {
            $variables['full_url'] = Url::fromRoute('social_group.stream', [
              'group' => $activity_entity->getGroup()->id(),
            ]);
          }
          break;
      }
    }
  }
}

/**
 * Implements hook_preprocess_page_title().
 */
function social_group_request_preprocess_page_title(&$variables) {
  if (\Drupal::routeMatch()->getRouteName() !== 'view.group_pending_members.membership_requests') {
    return;
  }

  $group_id = \Drupal::routeMatch()->getParameter('arg_0');
  if (!isset($group_id) || empty($group_id)) {
    return;
  }

  /** @var \Drupal\group\Entity\GroupInterface $group */
  $group = \Drupal::entityTypeManager()->getStorage('group')->load($group_id);
  if (!$group->getGroupType()->hasContentPlugin('group_membership_request')) {
    return;
  }

  $contentTypeConfigId = $group
    ->getGroupType()
    ->getContentPlugin('group_membership_request')
    ->getContentTypeConfigId();

  $requests = \Drupal::entityQuery('group_content')
    ->condition('type', $contentTypeConfigId)
    ->condition('gid', $group->id())
    ->condition('grequest_status', GroupMembershipRequest::REQUEST_PENDING)
    ->count()
    ->accessCheck(TRUE)
    ->execute();

  $title_singular = '1 membership request to group: :group_title';
  $title_multiple = '@count membership requests to group: :group_title';
  $variables['title'] = \Drupal::translation()->formatPlural($requests, $title_singular, $title_multiple, [
    ':group_title' => $group->label(),
  ]);

  $variables['#cache']['tags'][] = 'request-membership:' . $group->id();
}

/**
 * Implements hook_views_pre_view().
 */
function social_group_request_views_pre_view(ViewExecutable $view, $display_id, array &$args) {
  if ($view->id() === 'group_pending_members' && $display_id === 'membership_requests') {
    $options = [
      'id' => 'area_text_custom',
      'table' => 'views',
      'field' => 'area_text_custom',
      'relationship' => 'none',
      'group_type' => 'none',
      'admin_label' => '',
      'empty' => TRUE,
      'tokenize' => FALSE,
      'content' => Link::fromTextAndUrl(t('Back to group'),
        Url::fromRoute('entity.group.canonical', ['group' => $args[0]], [
          'attributes' => [
            'class' => [
              'btn',
              'btn-default',
            ],
          ],
        ]))->toString(),
      'plugin_id' => 'text_custom',
    ];
    $view->setHandler('membership_requests', 'footer', 'area_text_custom', $options);
    $view->element['#attached']['library'][] = 'social_group_request/social_group_table';
  }
}

/**
 * Implements hook_block_access().
 */
function social_group_request_block_access(Block $block, $operation, AccountInterface $account) {
  if ($operation == 'view' && $block->getPluginId() == 'group_hero_block') {
    $route_name = \Drupal::routeMatch()->getRouteName();
    $excluded_routes = [
      'grequest.group_request_membership_approve',
      'grequest.group_request_membership_reject',
    ];

    if (in_array($route_name, $excluded_routes)) {
      return AccessResult::forbidden();
    }
  }
}
