<?php

/**
 * @file
 * Install and update functions for the social_group_flexible_group module.
 */

use Drupal\field\Entity\FieldConfig;
use Drupal\field\FieldConfigInterface;
use Drupal\search_api\Entity\Index;
use Drupal\taxonomy\Entity\Vocabulary;
use Symfony\Component\Yaml\Yaml;
use Drupal\Core\Config\FileStorage;
use Drupal\Core\Site\Settings;

/**
 * Implements hook_install().
 */
function social_group_flexible_group_install() {
  // Grant the default permissions for this feature.
  user_role_grant_permissions(
    'verified',
    [
      'create flexible_group group',
    ]
  );
  user_role_grant_permissions(
    'contentmanager',
    [
      'create flexible_group group',
    ]
  );
  user_role_grant_permissions(
    'sitemanager',
    [
      'create flexible_group group',
    ]
  );

  // Set module weight higher as the weight of the "Social Group" module because
  // needs have the Views group type field which added via the "Social Group"
  // module.
  module_set_weight('social_group_flexible_group', 5);
}

/**
 * Implements hook_update_dependencies().
 */
function social_group_flexible_group_update_dependencies() {
  // New config changes should run after the features removal/revert.
  $dependencies['social_group_flexible_group'][8801] = [
    'social_core' => 8805,
  ];

  return $dependencies;
}

/**
 * Update view mode for new small teaser style.
 */
function social_group_flexible_group_update_8801() {
  /** @var \Drupal\update_helper\Updater $updateHelper */
  $updateHelper = \Drupal::service('update_helper.updater');

  // Execute configuration update definitions with logging of success.
  $updateHelper->executeUpdate('social_group_flexible_group', 'social_group_flexible_group_update_8801');

  // Output logged messages to related channel of update execution.
  return $updateHelper->logger()->output();
}

/**
 * Trigger a search_api re-index.
 */
function social_group_flexible_group_update_8802() {
  $index = Index::load('social_groups');
  if ($index->status()) {
    $index->save();
    $index->clear();
    $index->reindex();
  }
}

/**
 * Add new field group visibility field to flexible groups.
 */
function social_group_flexible_group_update_8901(&$sandbox) {
  $config_file = \Drupal::service('extension.list.module')->getPath('social_group_flexible_group') . '/config/update/social_group_flexible_group_update_8901.yml';

  if (is_file($config_file)) {
    $sandbox['configs'] = Yaml::parse(file_get_contents($config_file));
    if (!isset($sandbox['total'])) {
      // Count the amount we need to add to cover batching..
      $sandbox['total'] = count($sandbox['configs']);
      $sandbox['current'] = 0;
    }

    $names = array_keys($sandbox['configs']);
    $name = (string) $names[$sandbox['current']++];
    $data = $sandbox['configs'][$name];

    $parts = explode('.', $name);

    switch ($parts[0] . '.' . $parts[1]) {
      case 'field.storage':
        $entity_type = \Drupal::service('config.manager')
          ->getEntityTypeIdByName($name);

        /** @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface $storage */
        $storage = \Drupal::entityTypeManager()->getStorage($entity_type);
        $entity = $storage->createFromStorageRecord($data);
        $entity->save();
        break;

      case 'field.field':
        $field_config = FieldConfig::loadByName($parts[2], $parts[3], $parts[4]);

        if ($field_config instanceof FieldConfigInterface) {
          $field_config->setDescription($data);
        }
        else {
          $field_config = FieldConfig::create($data);
        }

        $field_config->save();
        break;
    }

    $sandbox['#finished'] = $sandbox['current'] / $sandbox['total'];
  }
}

/**
 * Add the new group visibility field.
 */
function social_group_flexible_group_update_8902() {
  /** @var \Drupal\update_helper\Updater $updateHelper */
  $updateHelper = \Drupal::service('update_helper.updater');

  // Execute configuration update definitions with logging of success.
  $updateHelper->executeUpdate('social_group_flexible_group', 'social_group_flexible_group_update_8902');

  // Output logged messages to related channel of update execution.
  return $updateHelper->logger()->output();
}

/**
 * Configuration update.
 */
function social_group_flexible_group_update_8903() {
  /** @var \Drupal\update_helper\Updater $updateHelper */
  $updateHelper = \Drupal::service('update_helper.updater');

  // Execute configuration update definitions with logging of success.
  $updateHelper->executeUpdate('social_group_flexible_group', 'social_group_flexible_group_update_8903');

  // Output logged messages to related channel of update execution.
  return $updateHelper->logger()->output();
}

/**
 * Updated fieldsets labels and re-arranged existing fields.
 */
function social_group_flexible_group_update_8904() {
  /** @var \Drupal\update_helper\Updater $updateHelper */
  $updateHelper = \Drupal::service('update_helper.updater');

  // Execute configuration update definitions with logging of success.
  $updateHelper->executeUpdate('social_group_flexible_group', 'social_group_flexible_group_update_8904');

  // Output logged messages to related channel of update execution.
  return $updateHelper->logger()->output();
}

/**
 * Added "Additional information" fieldset to flexible groups.
 */
function social_group_flexible_group_update_8905() {
  /** @var \Drupal\update_helper\Updater $updateHelper */
  $updateHelper = \Drupal::service('update_helper.updater');

  // Execute configuration update definitions with logging of success.
  $updateHelper->executeUpdate('social_group_flexible_group', 'social_group_flexible_group_update_8905');

  // Output logged messages to related channel of update execution.
  return $updateHelper->logger()->output();
}

/**
 * Update label for field_group_image field.
 */
function social_group_flexible_group_update_8906() {
  // Load the existing configuration.
  $config_name = 'field.field.group.flexible_group.field_group_image';
  $config = \Drupal::configFactory()->getEditable($config_name);
  $config_data = $config->getRawData();

  if (!empty($config_data['label'])) {
    // This to ensure any custom added values are not affected.
    if (strpos($config_data['label'], 'Group Image') !== FALSE) {
      $config_data['label'] = 'Image';
    }

    $config->setData($config_data)->save();
    // Make sure we clear cached definitions for the fields.
    \Drupal::service('entity_field.manager')->clearCachedFieldDefinitions();
  }
}

/**
 * Create new vocabulary called 'Group types'.
 */
function social_group_flexible_group_update_8907() {
  $vid = 'group_type';
  $vocabularies = Vocabulary::loadMultiple();
  if (!isset($vocabularies[$vid])) {
    $vocabulary = Vocabulary::create([
      'vid' => $vid,
      'description' => 'Group types',
      'name' => 'Group type',
    ]);
    $vocabulary->save();
  }
}

/**
 * Create new field & field storage configuration for new group type field.
 */
function social_group_flexible_group_update_8908(&$sandbox) {
  $config_file = \Drupal::service('extension.list.module')->getPath('social_group_flexible_group') . '/config/update/social_group_flexible_group_update_8908.yml';

  if (is_file($config_file)) {
    $sandbox['configs'] = Yaml::parse(file_get_contents($config_file));
    if (!isset($sandbox['total'])) {
      // Count the amount we need to add to cover batching..
      $sandbox['total'] = count($sandbox['configs']);
      $sandbox['current'] = 0;
    }

    $names = array_keys($sandbox['configs']);
    $name = (string) $names[$sandbox['current']++];
    $data = $sandbox['configs'][$name];

    $parts = explode('.', $name);

    switch ($parts[0] . '.' . $parts[1]) {
      case 'field.storage':
        $entity_type = \Drupal::service('config.manager')
          ->getEntityTypeIdByName($name);

        /** @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface $storage */
        $storage = \Drupal::entityTypeManager()->getStorage($entity_type);
        $entity = $storage->createFromStorageRecord($data);
        $entity->save();
        break;

      case 'field.field':
        $field_config = FieldConfig::loadByName($parts[2], $parts[3], $parts[4]);

        if ($field_config instanceof FieldConfigInterface) {
          $field_config->setDescription($data);
        }
        else {
          $field_config = FieldConfig::create($data);
        }

        $field_config->save();
        break;
    }

    $sandbox['#finished'] = $sandbox['current'] / $sandbox['total'];
  }
}

/**
 * Added a new entity reference field called 'Type' in Flexible groups.
 */
function social_group_flexible_group_update_8909() {
  /** @var \Drupal\update_helper\Updater $updateHelper */
  $updateHelper = \Drupal::service('update_helper.updater');

  // Execute configuration update definitions with logging of success.
  $updateHelper->executeUpdate('social_group_flexible_group', 'social_group_flexible_group_update_8909');

  // Output logged messages to related channel of update execution.
  return $updateHelper->logger()->output();
}

/**
 * Create new field & field storage configuration for new group type icon field.
 */
function social_group_flexible_group_update_8910(&$sandbox) {
  $config_file = \Drupal::service('extension.list.module')->getPath('social_group_flexible_group') . '/config/update/social_group_flexible_group_update_8910.yml';

  if (is_file($config_file)) {
    $sandbox['configs'] = Yaml::parse(file_get_contents($config_file));
    if (!isset($sandbox['total'])) {
      // Count the amount we need to add to cover batching..
      $sandbox['total'] = count($sandbox['configs']);
      $sandbox['current'] = 0;
    }

    $names = array_keys($sandbox['configs']);
    $name = (string) $names[$sandbox['current']++];
    $data = $sandbox['configs'][$name];

    $parts = explode('.', $name);

    switch ($parts[0] . '.' . $parts[1]) {
      case 'field.storage':
        $entity_type = \Drupal::service('config.manager')
          ->getEntityTypeIdByName($name);

        /** @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface $storage */
        $storage = \Drupal::entityTypeManager()->getStorage($entity_type);
        $entity = $storage->createFromStorageRecord($data);
        $entity->save();
        break;

      case 'field.field':
        $field_config = FieldConfig::loadByName($parts[2], $parts[3], $parts[4]);

        if ($field_config instanceof FieldConfigInterface) {
          $field_config->setDescription($data);
        }
        else {
          $field_config = FieldConfig::create($data);
        }

        $field_config->save();
        break;

      default:
        // Fallback similar to before.
        \Drupal::configFactory()
          ->getEditable($name)
          ->setData($data)
          ->save(TRUE);
    }

    $sandbox['#finished'] = $sandbox['current'] / $sandbox['total'];
  }
}

/**
 * Make group creators as a group managers by default.
 */
function social_group_flexible_group_update_8911() {
  $config_factory = \Drupal::configFactory();
  $group_type = 'flexible_group';

  $config = $config_factory->getEditable('group.type.' . $group_type);
  $config->set('creator_membership', TRUE);

  $creator_roles = $config->get('creator_roles');
  $group_role_id = $group_type . '-group_manager';
  if (!in_array($group_role_id, $creator_roles)) {
    $creator_roles[] = $group_role_id;
    $config->set('creator_roles', $creator_roles);
  }

  $config->save(TRUE);
}

/**
 * Make group creators as a group managers by default.
 */
function social_group_flexible_group_update_8912() {
  /** @var \Drupal\update_helper\Updater $updateHelper */
  $updateHelper = \Drupal::service('update_helper.updater');

  // Execute configuration update definitions with logging of success.
  $updateHelper->executeUpdate('social_group_flexible_group', 'social_group_flexible_group_update_8912');

  // Output logged messages to related channel of update execution.
  return $updateHelper->logger()->output();
}

/**
 * Allow CM+ translate "Flexible Group" group type.
 */
function social_group_flexible_update_10301() {
  // Removed code in the function as it is neither an update hook nor it is
  // called anywhere. The name of the function should have been
  // social_group_flexible_group_update_10301() which qualifies it to be an
  // update hook. We have moved the code to a new update hook().
  // @see social_group_flexible_group_update_11001()
  // @see https://www.drupal.org/project/social/issues/3273182
}

/**
 * Update view display for Flexible Group.
 */
function social_group_flexible_update_10302() {
  // Removed code in the function as it is neither an update hook nor it is
  // called anywhere. The name of the function should have been
  // social_group_flexible_group_update_10302() which qualifies it to be an
  // update hook. We have moved the code to a new update hook().
  // @see social_group_flexible_group_update_11002()
  // @see https://www.drupal.org/project/social/issues/3273182
}

/**
 * Install display view modes for group contents for Flexible group type.
 */
function social_group_flexible_group_update_10303(&$sandbox) {
  // Install new configs.
  $config_files = [
    'core.entity_view_display.group_content.flexible_group-group_node-event.activity' => 'core.entity_view_display.group_content.flexible_group-group_node-event.activity_10303',
    'core.entity_view_display.group_content.flexible_group-group_node-topic.activity' => 'core.entity_view_display.group_content.flexible_group-group_node-topic.activity_10303',
  ];

  $config_storage = \Drupal::service('config.storage');
  $config_path = \Drupal::service('extension.list.module')->getPath('social_group_flexible_group') . '/config/static';
  $source = new FileStorage($config_path);

  foreach ($config_files as $origin_name => $update_name) {
    $config_storage->write($origin_name, $source->read($update_name));
  }
}

/**
 * Allow CM+ translate "Flexible Group" group type.
 */
function social_group_flexible_group_update_11001(): void {
  foreach (['sitemanager', 'contentmanager'] as $role_id) {
    user_role_grant_permissions($role_id, ['translate flexible_group group']);
  }
}

/**
 * Update view display for Flexible Group.
 */
function social_group_flexible_group_update_11002(): string {
  /** @var \Drupal\update_helper\Updater $updateHelper */
  $updateHelper = \Drupal::service('update_helper.updater');

  // Execute configuration update definitions with logging of success.
  $updateHelper->executeUpdate('social_group_flexible_group', __FUNCTION__);

  // Output logged messages to related channel of update execution.
  return $updateHelper->logger()->output();
}

/**
 * Create a new field to disable posts in groups.
 */
function social_group_flexible_group_update_11201(): void {
  $config_path = \Drupal::service('extension.list.module')->getPath('social_group_flexible_group') . '/config/static';
  $source = new FileStorage($config_path);
  $entity_type_manager = \Drupal::entityTypeManager();

  /** @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface $field_storage_config_storage */
  $field_storage_config_storage = $entity_type_manager->getStorage('field_storage_config');

  // Create field storages.
  $field_type_manager = \Drupal::service('plugin.manager.field.field_type');
  $data = $source->read('field.storage.group.field_group_posts_enabled_11201');
  if (is_array($data)) {
    $class = $field_type_manager->getPluginClass($data['type']);
    if (is_null($field_storage_config_storage->load($data['id']))) {
      $data['settings'] = $class::storageSettingsFromConfigData($data['settings']);
      $field_storage_config_storage->create($data)->save();
    }
  }

  /** @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface $field_config_storage */
  $field_config_storage = $entity_type_manager->getStorage('field_config');
  // Create field settings.
  $data = $source->read('field.field.group.field_group_posts_enabled_11201');
  if (is_array($data) && is_null($field_config_storage->load($data['id']))) {
    $field_config_storage->create($data)->save();
  }
}

/**
 * Updates the flexible group's display.
 */
function social_group_flexible_group_update_11202(): string {
  /** @var \Drupal\update_helper\Updater $updateHelper */
  $updateHelper = \Drupal::service('update_helper.updater');

  // Execute configuration update definitions with logging of success.
  $updateHelper->executeUpdate('social_group_flexible_group', __FUNCTION__);

  // Output logged messages to related channel of update execution.
  return $updateHelper->logger()->output();
}

/**
 * Make "Group Types" vocabulary translatable.
 */
function social_group_flexible_group_update_11203(): void {
  if (!\Drupal::moduleHandler()->moduleExists('social_content_translation')) {
    return;
  }

  $config_storage = \Drupal::service('config.storage');
  $config_path = \Drupal::service('extension.list.module')->getPath('social_group_flexible_group') . '/config/static';
  $source = new FileStorage($config_path);

  $config_storage->write('language.content_settings.taxonomy_term.group_type', (array) $source->read('language.content_settings.taxonomy_term.group_type_11203'));
}

/**
 * Allow extending list of allowed join methods dynamically.
 */
function social_group_flexible_group_update_11401(): string {
  /** @var \Drupal\update_helper\UpdaterInterface $update_helper */
  $update_helper = \Drupal::service('update_helper.updater');

  // Execute configuration update definitions with logging of success.
  $update_helper->executeUpdate('social_group_flexible_group', __FUNCTION__);

  // Output logged messages to related channel of update execution.
  return $update_helper->logger()->output();
}

/**
 * Allow extending list of allowed join methods dynamically.
 */
function social_group_flexible_group_update_11402(): string {
  /** @var \Drupal\update_helper\UpdaterInterface $update_helper */
  $update_helper = \Drupal::service('update_helper.updater');

  // Execute configuration update definitions with logging of success.
  $update_helper->executeUpdate('social_group_flexible_group', __FUNCTION__);

  // Output logged messages to related channel of update execution.
  return $update_helper->logger()->output();
}

/**
 * Apply default value of field_group_posts_enabled where it is not defined.
 *
 * Before field_group_posts_enabled was introduced, all users were able to post
 * by default as this was a hardcoded functionality. If the flexible group was
 * created before field_group_posts_enabled existed, this value is not defined
 * in the database. If field_group_posts_enabled is not defined (not in the
 * database) and the group is edited without any changes, the default value is
 * not set and this results in the group being updated with
 * field_group_posts_enabled disabled (value 0). Because of these facts, we need
 * to define the field_group_posts_enabled value for each flexible group where
 * it is not defined. The field_group_posts_enabled value is defined based on
 * the field_group_posts_enabled default value.
 */
function social_group_flexible_group_update_11701(array &$sandbox): string {
  if (!isset($sandbox['total'])) {
    $sandbox['total'] = \Drupal::entityQuery('group')
      ->condition('type', 'flexible_group')
      ->notExists('field_group_posts_enabled')
      ->count()
      ->accessCheck(FALSE)
      ->execute();

    if (!$sandbox['total']) {
      return t('No flexible groups were updated.');
    }

    $definitions = \Drupal::service('entity_field.manager')->getFieldDefinitions('group', 'flexible_group');
    if (isset($definitions['field_group_posts_enabled'])) {
      $sandbox['field_group_posts_enabled_default_value'] =
        $definitions['field_group_posts_enabled']->getDefaultValueLiteral()[0]['value'];
    }
    else {
      return t('field_group_posts_enabled does not exist. No flexibe groups were updated.');
    }

    $sandbox['processed'] = 0;
    $sandbox['current_id'] = -1;
    $sandbox['limit'] = Settings::get('entity_update_batch_size', 10);
  }

  $entity_ids = \Drupal::entityQuery('group')
    ->condition('type', 'flexible_group')
    ->condition('id', $sandbox['current_id'], '>')
    ->notExists('field_group_posts_enabled')
    ->sort('id')
    ->range(0, $sandbox['limit'])
    ->accessCheck(FALSE)
    ->execute();

  $storage = \Drupal::entityTypeManager()->getStorage('group');

  foreach ($entity_ids as $entity_id) {
    /** @var \Drupal\social_group\Entity\Group $entity */
    $entity = $storage->load($entity_id);
    $entity->field_group_posts_enabled->value =
      $sandbox['field_group_posts_enabled_default_value'];
    $entity->save();

    $sandbox['processed']++;
    $sandbox['current_id'] = $entity_id;
    $sandbox['processed_ids'][] = $entity_id;
  }

  $sandbox['#finished'] = $sandbox['processed'] >= $sandbox['total'] ? TRUE : $sandbox['processed'] / $sandbox['total'];

  if ($sandbox['#finished'] === TRUE) {
    return t('Post for group members was @status for @count flexible groups (ids): @ids', [
      '@status' => $sandbox['field_group_posts_enabled_default_value'] === 1 ? t('enabled') : t('disabled'),
      '@count' => count($sandbox['processed_ids']),
      '@ids' => implode(', ', $sandbox['processed_ids']),
    ]);
  }
  else {
    return '';
  }
}

/**
 * Remove redundant views filter.
 */
function social_group_flexible_group_update_11801(): void {
  $configurations = ['views.view.latest_topics', 'views.view.upcoming_events'];
  foreach ($configurations as $configuration) {
    $config = \Drupal::configFactory()->getEditable($configuration);

    $displays = $config->get('display');
    foreach ($displays as $id => $settings) {
      unset($displays[$id]['display_options']['filters']['flexible_group_node_access']);
    }

    $config->set('display', $displays);
    $config->save();
  }
}

/**
 * Add field_group_type filter to the all-groups overview.
 */
function social_group_flexible_group_update_11901(): string {
  /** @var \Drupal\update_helper\Updater $updater */
  $updater = \Drupal::service('update_helper.updater');

  // Execute configuration update definitions with logging of success.
  $updater->executeUpdate('social_group_flexible_group', 'social_group_flexible_group_update_11901');

  // Output logged messages to related channel of update execution.
  return $updater->logger()->output();
}

/**
 * Search for invalid permission(s) and remove them from existing roles.
 *
 * Permissions to check:
 * - "translate flexible_group group".
 */
function social_group_flexible_group_update_111101(): void {
  // Moved to social_group_flexible_group_update_111103.
}

/**
 * Disable revisions for flexible group.
 */
function social_group_flexible_group_update_111102(): void {
  $config = \Drupal::configFactory()->getEditable("group.type.flexible_group");
  $config->set("new_revision", FALSE);
  $config->save();
}

/**
 * Search for invalid permission(s) and remove them from existing roles.
 *
 * Permissions to check:
 * - "translate flexible_group group".
 */
function social_group_flexible_group_update_111103(): void {
  $entity_type_manager = \Drupal::entityTypeManager();
  $all_permissions = array_keys(\Drupal::service('user.permissions')->getPermissions());
  /** @var \Drupal\user\RoleInterface[] $roles */
  $roles = $entity_type_manager->getStorage('user_role')->loadMultiple();

  $permissions_to_check = [
    'translate flexible_group group',
  ];

  // If permission is not valid (is not on the list of all permissions),
  // we need to revoke it from all existing roles.
  foreach ($permissions_to_check as $permission_to_check) {
    if (!in_array($permission_to_check, $all_permissions)) {
      foreach ($roles as $role) {
        if ($role->hasPermission($permission_to_check)) {
          $role->revokePermission($permission_to_check);
          $role->save();
        }
      }
    }
  }
}

/**
 * Add taxonomy permission with the Taxonomy Access Fix is enabled.
 */
function social_group_flexible_group_update_120001(): void {
  // Nothing to do if the module is not installed.
  if (!\Drupal::moduleHandler()->moduleExists("taxonomy_access_fix")) {
    return;
  }

  // Permission to grant.
  $taxonomy_permissions = [
    'view terms in group_type',
    'view term names in group_type',
    'select terms in group_type',
  ];

  // Load the permission.
  $entity_type_manager = \Drupal::entityTypeManager();
  /** @var \Drupal\user\RoleInterface $role */
  $role = $entity_type_manager->getStorage('user_role')->load('authenticated');

  // If the role is not have the permission, grant permission.
  foreach ($taxonomy_permissions as $taxonomy_permission) {
    if (!$role->hasPermission($taxonomy_permission)) {
      $role->grantPermission($taxonomy_permission);
      $role->save();
    }
  }
}
