<?php

/**
 * @file
 * The Social group secret module.
 */

use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\group\Entity\Group;
use Drupal\views\Plugin\views\query\QueryPluginBase;
use Drupal\views\Plugin\views\row\EntityRow;
use Drupal\views\ViewExecutable;

/**
 * Implements hook_form_FORM_ID_alter().
 */
function social_group_secret_form_social_group_add_alter(&$form, FormStateInterface $form_state, $form_id) {
  $items = &$form['group_settings']['group_type']['#options'];

  if (isset($items['secret_group'])) {
    $item = $items['secret_group'];
    unset($items['secret_group']);
    $items['secret_group'] = $item;
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Prohibit changing group type.
 */
function social_group_secret_form_group_secret_group_edit_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  $form['group_type']['widget']['#description'] = t('It is not possible to change the group type of a secret group.');
  foreach (array_keys($form['group_type']['widget']['#options']) as $type) {
    $form['group_type']['widget'][$type] = [
      '#disabled' => TRUE,
    ];
  }
  unset($form['group_type']['#ajax']);

  $id = array_search('_social_group_type_edit_submit', $form['actions']['submit']['#submit']);
  unset($form['actions']['submit']['#submit'][$id]);
}

/**
 * Implements hook_views_query_alter().
 *
 * Hide secret groups everywhere when the current user is not a member.
 */
function social_group_secret_views_query_alter(ViewExecutable $view, QueryPluginBase $query) {
  if (empty($view->rowPlugin) || !($view->rowPlugin instanceof EntityRow) || $view->rowPlugin->getEntityTypeId() !== 'group') {
    return;
  }

  $account = \Drupal::currentUser();

  if (social_group_secret_can_view_secret_groups($account)) {
    return;
  }
  /** @var \Drupal\group\GroupMembershipLoaderInterface $service */
  $service = \Drupal::service('group.membership_loader');

  // Define the list of the secret groups where the current user is a member.
  // It's needed because the user should see only secret groups where it is a
  // member.
  $ids = [];

  /** @var \Drupal\group\GroupMembership $membership */
  foreach ($service->loadByUser($account) as $membership) {
    if ($membership->getGroup()->bundle() === 'secret_group') {
      $ids[] = $membership->getGroup()->id();
    }
  }

  /** @var \Drupal\views\Plugin\views\query\Sql $query */
  $group = count($query->where);

  while (isset($query->where[$group])) {
    $group++;
  }

  if ($ids) {
    $ids = \Drupal::entityTypeManager()->getStorage('group')
      ->getQuery()
      ->condition('type', 'secret_group')
      ->condition('id', $ids, 'NOT IN')
      ->accessCheck()
      ->execute();

    if ($ids) {
      $query->addWhere($group, 'groups_field_data.id', $ids, 'NOT IN');
    }
  }
  else {
    // Add context so for AN it will have a different cache.
    $view->element['#cache']['contexts'][] = 'user.roles:anonymous';
    $query->addWhere($group, 'groups_field_data.type', ['secret_group'], 'NOT IN');
  }
}

/**
 * Determine whether a user can see secret groups as outsider.
 *
 * @param \Drupal\Core\Session\AccountInterface $account
 *   The user to check for.
 *
 * @return bool
 *   Whether the user is allowed to view secret groups.
 */
function social_group_secret_can_view_secret_groups(AccountInterface $account) : bool {
  return social_group_can_view_groups_of_type('secret_group', $account);
}

/**
 * Implements hook_preprocess_HOOK().
 */
function social_group_secret_preprocess_group(&$variables) {
  /** @var \Drupal\group\Entity\GroupInterface $group */
  $group = $variables['group'];

  if ($group->getGroupType()->id() === 'secret_group') {
    $variables['secret_group_shield'] = TRUE;
  }
}

/**
 * Implements hook_social_group_types_alter().
 */
function social_group_secret_social_group_types_alter(array &$social_group_types) {
  $social_group_types[] = 'secret_group';
}

/**
 * Implements hook_social_group_default_visibility_alter().
 */
function social_group_secret_social_group_default_visibility_alter(&$visibility, $group_type_id) {
  if ($group_type_id === 'secret_group') {
    $visibility = 'group';
  }
}

/**
 * Implements hook_social_group_allowed_visibilities_alter().
 */
function social_group_secret_social_group_allowed_visibilities_alter(array &$visibilities, $group_type_id) {
  if ($group_type_id === 'secret_group') {
    foreach ($visibilities as $visibility_name => &$is_visible) {
      $is_visible = $visibility_name === 'group';
    }
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Hides secret groups for users that are not a member unless the content is
 * already in the group.
 */
function social_group_secret_form_node_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  // The is_new field may not be set for new entities.
  $editing = isset($form['is_new']) && !$form['is_new']['#value'];

  if (!$editing) {
    return;
  }

  $user = \Drupal::currentUser();
  if (!empty($form["groups"]["widget"]['#default_value'])) {
    $current_group = $form["groups"]["widget"]['#default_value'][0];
  }
  $secret_group = t('Secret group')->__toString();

  // The options are either a list of group ids when there is only a single
  // group type or they're nested per group type.
  if (isset($form["groups"]["widget"]["#options"][$secret_group])) {
    $group_ids = array_keys($form["groups"]["widget"]["#options"][$secret_group]);
    $groups = Group::loadMultiple($group_ids);
    /** @var \Drupal\group\Entity\Group $group */
    foreach ($groups as $group) {
      if (isset($current_group) && $group->id() === $current_group) {
        continue;
      }

      if (!$group->hasPermission('view group', $user)) {
        unset($form["groups"]["widget"]["#options"][$secret_group][$group->id()]);
      }
    }

    if (empty($form["groups"]["widget"]["#options"][$secret_group])) {
      unset($form["groups"]["widget"]["#options"][$secret_group]);
    }
  }
  else {
    $group_ids = array_filter(
      array_keys($form["groups"]["widget"]["#options"]),
      'is_numeric'
    );

    $groups = Group::loadMultiple($group_ids);
    /** @var \Drupal\group\Entity\Group $group */
    foreach ($groups as $group) {
      // If this is a list of one group type and it's not a secret group then
      // we're done.
      if ($group->bundle() !== 'secret_group') {
        break;
      }

      if (isset($current_group) && $group->id() === $current_group) {
        continue;
      }

      if (!$group->hasPermission('view group', $user)) {
        unset($form["groups"]["widget"]["#options"][$group->id()]);
      }
    }
  }
}

/**
 * Implements hook_social_group_join_method_usage().
 */
function social_group_secret_social_group_join_method_usage(): array {
  return [
    [
      'entity_type' => 'group',
      'bundle' => 'secret_group',
      'method' => 'added',
    ],
  ];
}
