<?php

/**
 * @file
 * The Social Private Message (de)installation file.
 */

use Symfony\Component\Yaml\Yaml;

/**
 * Implements hook_uninstall().
 */
function social_private_message_install() {
  // Grant the default permissions for this feature.
  user_role_grant_permissions(
    'verified',
    [
      'use private messaging system',
      'create private messages thread',
      'reply to private messages thread',
      'delete private messages thread',
    ]
  );
  user_role_grant_permissions(
    'contentmanager',
    [
      'use private messaging system',
      'create private messages thread',
      'reply to private messages thread',
      'delete private messages thread',
    ]
  );
  user_role_grant_permissions(
    'sitemanager',
    [
      'use private messaging system',
      'create private messages thread',
      'reply to private messages thread',
      'delete private messages thread',
    ]
  );

  // Disable the email notification provided by private_message.
  /** @var \Drupal\Core\Config\Config|\Drupal\Core\Config\ImmutableConfig $config */
  \Drupal::getContainer()
    ->get('config.factory')
    ->getEditable('private_message.settings')
    ->set('enable_notifications', FALSE)
    ->set('notify_by_default', FALSE)
    ->save();

  // Set module weight.
  module_set_weight('social_private_message', 1);

  // Alter display configuration of the private message module.
  _social_private_message_set_private_message_default_form_settings();
  _social_private_message_set_private_message_default_view_settings();
  _social_private_message_set_private_message_inbox_view_settings();
  _social_private_message_set_private_message_thread_default_view_settings();

  // Disable the default message template provided by private_message.
  \Drupal::configFactory()->getEditable('message.template.private_message_notification')
    ->set('status', FALSE)
    ->save();
}

/**
 * Implements hook_uninstall().
 */
function social_private_message_uninstall(bool $is_syncing) : void {
  // Delete any notifications that exist for private messages since we're
  // removing the template.
  $message_ids = \Drupal::entityQuery('message')
    ->accessCheck(TRUE)
    ->condition('template', 'create_private_message')
    ->execute();
  assert(is_array($message_ids));

  $storage = \Drupal::entityTypeManager()->getStorage('message');
  $entities = $storage->loadMultiple($message_ids);
  $storage->delete($entities);
}

/**
 * Implements hook_update_dependencies().
 */
function social_private_message_update_dependencies() {
  // Run the activities view mode update after the final features removal ran.
  $dependencies['social_private_message'][8801] = [
    'social_core' => 8802,
  ];

  // Make sure that we delete/update private_message configurations before
  // private_message_notify will be enabled in the private_message module.
  $dependencies['private_message'][8007] = [
    'social_private_message' => 11008,
  ];

  // > [notice] Update started: social_private_message_update_11002
  // > [error]  The "VariationCache" module is not installed.
  // Please run the update script to install it properly.
  // > [error]  Update failed: social_private_message_update_11002
  $dependencies['social_private_message'][11002] = [
    'group' => 8020,
  ];

  // Ensure the messages fields are installed before we try to update them.
  $dependencies['social_private_message'][11402] = [
    'activity_logger' => 11002,
  ];

  return $dependencies;
}

/**
 * Sets the widget for selecting Private Message thread members.
 */
function _social_private_message_set_private_message_default_form_settings() {
  /** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $pm_form_display */
  $pm_form_display = \Drupal::service('entity_type.manager')
    ->getStorage('entity_form_display')
    ->load('private_message_thread.private_message_thread.default');

  $pm_form_members_field = $pm_form_display->getComponent('members');

  $pm_form_members_field['type'] = 'social_private_message_thread_member_widget';

  $pm_form_display
    ->setComponent('members', $pm_form_members_field)
    ->save();
}

/**
 * Set the Open Social specific settings for the private message default view.
 */
function _social_private_message_set_private_message_default_view_settings() {
  /** @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface $pm_view_display */
  $pm_view_display = \Drupal::service('entity_type.manager')
    ->getStorage('entity_view_display')
    ->load('private_message.private_message.default');

  $created_field = $pm_view_display->getComponent('created');
  $created_field['type'] = 'timestamp_ago';
  $created_field['settings'] = [
    'granularity' => 1,
    'future_format' => '@interval hence',
    'past_format' => '@interval ago',
  ];

  $owner_field = $pm_view_display->getComponent('owner');
  $owner_field['type'] = 'social_private_message_owner_formatter';
  $owner_field['settings'] = ['link' => FALSE];

  $pm_view_display
    ->setComponent('created', $created_field)
    ->setComponent('owner', $owner_field)
    ->save();
}

/**
 * Set the Open Social specific settings for the private message inbox view.
 */
function _social_private_message_set_private_message_inbox_view_settings() {
  /** @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface $pm_view_display */
  $pm_view_display = \Drupal::service('entity_type.manager')
    ->getStorage('entity_view_display')
    ->load('private_message.private_message.inbox');

  $created_field = $pm_view_display->getComponent('created');
  $created_field['type'] = 'timestamp_ago';
  $created_field['settings'] = [
    'granularity' => 1,
    'future_format' => '@interval hence',
    'past_format' => '@interval ago',
  ];

  $owner_field = $pm_view_display->getComponent('owner');
  $owner_field['type'] = 'entity_reference_label';
  $owner_field['label'] = 'hidden';
  $owner_field['settings'] = ['link' => FALSE];

  $pm_view_display
    ->setComponent('created', $created_field)
    ->setComponent('owner', $owner_field)
    ->save();
}

/**
 * Set the OS specific settings for the private message thread default view.
 */
function _social_private_message_set_private_message_thread_default_view_settings() {
  /** @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface $pm_view_display */
  $pm_view_display = \Drupal::service('entity_type.manager')
    ->getStorage('entity_view_display')
    ->load('private_message_thread.private_message_thread.default');

  $deleted_field = [
    'weight' => -100,
    'region' => 'content',
    'settings' => [],
    'third_party_settings' => [],
  ];

  $members_field = $pm_view_display->getComponent('members');
  $members_field['type'] = 'social_private_message_thread_member_formatter';
  $members_field['label'] = 'hidden';

  $pm_field = $pm_view_display->getComponent('private_messages');
  $pm_field['label'] = 'hidden';
  $pm_field['settings'] = [
    'message_count' => 30,
    'ajax_previous_load_count' => 5,
    'ajax_refresh_rate' => 120,
    'message_order' => 'asc',
  ];

  $pm_view_display
    ->setComponent('deleted_field', $deleted_field)
    ->setComponent('members', $members_field)
    ->setComponent('private_messages', $pm_field)
    ->save();
}

/**
 * Set module weight.
 */
function social_private_message_update_8001() {
  module_set_weight('social_private_message', 1);
}

/**
 * Remove inbox blocks.
 */
function social_private_message_update_8002() {
  $plugin = 'social_private_message_inbox_block';

  $storage = \Drupal::entityTypeManager()->getStorage('block');
  $blocks = $storage->loadByProperties(['plugin' => $plugin]);

  /** @var \Drupal\block\Entity\Block $block */
  foreach ($blocks as $block) {
    $block->delete();
  }
}

/**
 * Config import social_private_message default configs.
 *
 * The feature removal script did not succeed for social_private_message.
 * Import the configs in an update hook.
 */
function social_private_message_update_8801() {
  $config_files = [
    'core.entity_form_display.private_message_thread.private_message_thread.default',
    'core.entity_view_display.private_message.private_message.default',
    'core.entity_view_display.private_message_thread.private_message_thread.default',
  ];

  // Revert the configurations.
  _social_private_message_features_removal_config_revert_helper($config_files);
}

/**
 * Revert remaining social_private_message configs not included in 8801.
 */
function social_private_message_update_8802() {
  $config_files = [
    'core.entity_view_display.private_message.private_message.inbox',
    'core.entity_view_display.profile.profile.compact_private_message',
    'core.entity_view_mode.profile.compact_private_message',
    'message.template.create_private_message',
    'views.view.inbox',
  ];
  // Revert the configurations.
  _social_private_message_features_removal_config_revert_helper($config_files);
}

/**
 * Helper function to revert configurations.
 *
 * @param array $config_files
 *   Array of configuration file names.
 */
function _social_private_message_features_removal_config_revert_helper(array $config_files) {
  foreach ($config_files as $config_file) {
    $config = \Drupal::service('extension.list.module')->getPath('social_private_message') . '/config/features_removal/' . $config_file . '.yml';

    if (is_file($config)) {
      $settings = Yaml::parse(file_get_contents($config));
      if (is_array($settings)) {
        $update_config = \Drupal::configFactory()
          ->getEditable($config_file);

        $update_config->setData($settings)->save(TRUE);
      }
    }
  }
}

/**
 * Update permissions for private messages.
 */
function social_private_message_update_8803() {
  $roles_storage = Drupal::entityTypeManager()->getStorage('user_role');
  $roles = $roles_storage->loadMultiple();
  if (!empty($roles)) {
    /** @var \Drupal\user\RoleInterface $role */
    foreach ($roles as $role) {
      if ($role->id() === 'administrator') {
        continue;
      }

      if ($role->hasPermission('use private messaging system')) {
        user_role_grant_permissions($role->id(), [
          'create private messages thread',
          'reply to private messages thread',
          'delete private messages thread',
        ]);
      }
    }
  }
}

/**
 * Update message template "message.template.activity_on_events_im_organizing".
 *
 * Added new 'preview' and 'cta_button' tokens.
 */
function social_private_message_update_10301() {
  $config_file = \Drupal::service('extension.list.module')->getPath('social_private_message') . '/config/static/message.template.create_private_message_10301.yml';
  if (is_file($config_file)) {
    $settings = Yaml::parse(file_get_contents($config_file));
    if (is_array($settings)) {
      $config = \Drupal::configFactory()
        ->getEditable('message.template.create_private_message');
      $config->setData($settings)->save(TRUE);
    }
  }
}

/**
 * Install the message_notify module.
 */
function social_private_message_update_11002(): void {
  \Drupal::service('module_installer')->install(['message_notify']);
}

/**
 * Update the Inbox view configurations.
 */
function social_private_message_update_11003(): string {
  /** @var \Drupal\update_helper\Updater $updateHelper */
  $updateHelper = \Drupal::service('update_helper.updater');

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

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

/**
 * Disable the default message template provided by private_message.
 */
function social_private_message_update_11004(): void {
  // Removed hook and moved code to the social_private_message_update_11007 due
  // to database update error that appears after moving the notification feature
  // to an optional submodule for private message.
  // @see https://www.drupal.org/project/social/issues/3264287
  // Configuration message.template.private_message_notification was moved to
  // that submodule and during enable it we receive error that configuration
  // objects (message.template.private_message_notification) provided by
  // private_message_notify already exist in active configuration.
}

/**
 * Disable the email notification provided by private_message.
 */
function social_private_message_update_11005(): void {
  \Drupal::getContainer()
    ->get('config.factory')
    ->getEditable('private_message.settings')
    ->set('enable_notifications', FALSE)
    ->set('notify_by_default', FALSE)
    ->save();
}

/**
 * Delete the default message template provided by private_message.
 */
function social_private_message_update_11006(): void {
  // Removed hook due to database update error.
  // @see https://www.drupal.org/project/social/issues/3266049
}

/**
 * Disable the default message template provided by private_message.
 */
function social_private_message_update_11007(): void {
  // Removed hook due to database update error and moved code to
  // social_private_message_update_11011.
  // @see https://www.drupal.org/project/social/issues/3266049
}

/**
 * Delete the configs that will be migrated to private_message_notify.
 */
function social_private_message_update_11008(): void {
  // Only delete configurations if private_message_notify is not installed
  // already.
  if (\Drupal::moduleHandler()->moduleExists('private_message_notify')) {
    return;
  }

  // We should delete configurations that will be migrated to
  // private_message_notify to prevent database update errors.
  $configs = [
    'core.entity_form_display.message.private_message_notification.default',
    'core.entity_view_display.message.private_message_notification.default',
    'core.entity_view_display.message.private_message_notification.mail_body',
    'core.entity_view_display.message.private_message_notification.mail_subject',
    'field.field.message.private_message_notification.field_message_pm_thread',
    'field.field.message.private_message_notification.field_message_private_message',
    'field.storage.message.field_message_pm_thread',
    'field.storage.message.field_message_private_message',
    'message.template.private_message_notification',
  ];

  $config_factory = \Drupal::configFactory();

  // Some users could edit the configuration, so we need to save it temporarily
  // and update after private_message_notify will be enabled.
  // @see social_private_message_update_11010
  foreach ($configs as $name) {
    $config = $config_factory->getEditable($name);
    if ($config->isNew()) {
      continue;
    }

    // Set config data to a new temporary config.
    $config_tmp = $config_factory->getEditable("{$name}.tmp");
    $config_tmp->setData($config->getRawData())->save();

    // Delete config.
    $config->delete();
  }
}

/**
 * Enable notifications submodule.
 */
function social_private_message_update_11009(): void {
  $module = 'private_message_notify';
  if (!\Drupal::moduleHandler()->moduleExists($module)) {
    /** @var \Drupal\Core\Extension\ModuleInstallerInterface $installer */
    $installer = \Drupal::service('module_installer');
    $installer->install([$module]);
  }
}

/**
 * Update the configs that were migrated to private_message_notify.
 */
function social_private_message_update_11010(): void {
  $configs = [
    'core.entity_form_display.message.private_message_notification.default',
    'core.entity_view_display.message.private_message_notification.default',
    'core.entity_view_display.message.private_message_notification.mail_body',
    'core.entity_view_display.message.private_message_notification.mail_subject',
    'field.field.message.private_message_notification.field_message_pm_thread',
    'field.field.message.private_message_notification.field_message_private_message',
    'field.storage.message.field_message_pm_thread',
    'field.storage.message.field_message_private_message',
    'message.template.private_message_notification',
  ];

  try {
    $config_factory = \Drupal::configFactory();

    foreach ($configs as $name) {
      // Load temporary config.
      $config_tmp = $config_factory->getEditable("{$name}.tmp");

      // If there is not exist a temporary config, do nothing.
      if ($config_tmp->isNew()) {
        continue;
      }

      // Load temporary config and update original one.
      $config = $config_factory->getEditable($name);
      $config->setData($config_tmp->getRawData())->save();

      // Delete temporary config.
      $config_tmp->delete();
    }
  }
  catch (Exception $e) {
    \Drupal::logger('social_private_message')->error($e->getMessage());
  }
}

/**
 * Disable the default message template provided by private_message.
 */
function social_private_message_update_11011(): void {
  $config = \Drupal::configFactory()->getEditable('message.template.private_message_notification');

  // Check if the config that we want to update already exists in the active
  // configuration.
  if (!$config->isNew()) {
    $config
      ->set('status', FALSE)
      ->save();
  }
}

/**
 * Update exposed filter button name.
 */
function social_private_message_update_11401(): string {
  /** @var \Drupal\update_helper\Updater $updateHelper */
  $updateHelper = \Drupal::service('update_helper.updater');

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

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

/**
 * Ensure module dependency is uninstalled when the module is.
 */
function social_private_message_update_11402() : void {
  $configFactory = \Drupal::configFactory();

  $toUpdate = [
    'core.entity_view_display.profile.profile.compact_private_message',
    'core.entity_view_mode.profile.compact_private_message',
    'field.field.message.create_private_message.field_message_context',
    'field.field.message.create_private_message.field_message_destination',
    'field.field.message.create_private_message.field_message_related_object',
    'message.template.create_private_message',
    'views.view.inbox',
  ];

  foreach ($toUpdate as $id) {
    $config = $configFactory->getEditable($id);
    $dependencies = $config->get('dependencies');
    if (!isset($dependencies['enforced'])) {
      $dependencies['enforced'] = [];
    }
    if (!isset($dependencies['enforced']['module'])) {
      $dependencies['enforced']['module'] = [];
    }
    $dependencies['enforced']['module'][] = 'social_private_message';
    $config->set('dependencies', $dependencies)
      ->save(TRUE);
  }
}
