<?php

/**
 * @file
 * Install, update and uninstall functions for the social_follow_user module.
 */

use Drupal\Core\Config\FileStorage;
use Drupal\Core\Entity\Entity\EntityFormDisplay;
use Drupal\Core\Site\Settings;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\profile\Entity\Profile;
use Drupal\user\Entity\User;

/**
 * Implements hook_install().
 *
 * Perform actions related to the installation of social_follow_user.
 */
function social_follow_user_install(): void {
  // Grant the default permissions for this feature.
  $permissions = [
    'flag follow_user',
    'unflag follow_user',
  ];

  foreach (['verified', 'contentmanager'] as $role_id) {
    user_role_grant_permissions($role_id, $permissions);
  }

  // Allow SM to configure visibility for Social Follow User feature.
  $permissions[] = 'administer social_follow_user settings';
  user_role_grant_permissions('sitemanager', $permissions);

  // Add & apply user follow functionality.
  // Display hero & statistic block on the following users/followers pages.
  $config_names = [
    'block.block.socialblue_profile_hero_block',
    'block.block.socialblue_profile_statistic_block',
  ];
  foreach ($config_names as $config_name) {
    $config = \Drupal::configFactory()->getEditable($config_name);

    if (!empty($config->getRawData())) {
      $pages = $config->get('visibility.request_path.pages');
      $pages .= "\r\n/user/*/following/users";
      $pages .= "\r\n/user/*/followers";
      $config->set('visibility.request_path.pages', $pages);
      $config->save();
    }
  }

  // Add our settings field to the user settings form.
  $form_mode = EntityFormDisplay::load("user.user.default");
  if ($form_mode === NULL) {
    throw new \RuntimeException("Default user form mode is missing, Drupal is not correctly installed.");
  }
  $form_mode->setComponent('field_allow_following', [
    "type" => "boolean_checkbox",
    "weight" => 3,
    "region" => "content",
    "settings" => [
      "display_label" => TRUE,
    ],
    "third_party_settings" => [],
  ]);
  $privacy_group = $form_mode->getThirdPartySetting("field_group", "group_privacy");
  if ($privacy_group !== NULL) {
    if (empty($privacy_group['children'])) {
      $privacy_group['children'] = [];
    }
    $privacy_group['children'][] = 'field_allow_following';
  }
  $form_mode->setThirdPartySetting("field_group", "group_privacy", $privacy_group);
  $form_mode->save();
}

/**
 * Implements hook_uninstall().
 */
function social_follow_user_uninstall(): void {
  $config_factory = \Drupal::service('config.factory');

  // Remove users follow fields.
  foreach (['statistic', 'teaser'] as $display_id) {
    $config = $config_factory->getEditable('core.entity_view_display.profile.profile.' . $display_id);
    $config->clear('content.followers');
    $config->clear('content.following');
    $config->save();
  }

  // Delete follow user views.
  $views = [
    'views.view.followers_user',
    'views.view.following_users',
  ];
  foreach ($views as $config) {
    $config_factory->getEditable($config)->delete();
  }

  // Remove views urls from the visibility block rules.
  $paths = [
    '/user/*/following/users',
    '/user/*/followers',
  ];
  $config_names = [
    'block.block.socialblue_profile_hero_block',
    'block.block.socialblue_profile_statistic_block',
  ];
  foreach ($config_names as $config_name) {
    $config = $config_factory->getEditable($config_name);

    if (!empty($config->getRawData())) {
      $pages = $config->get('visibility.request_path.pages');
      $pages = explode("\r\n", $pages ?? "");

      foreach ($pages as $delta => $page) {
        if (in_array($page, $paths)) {
          unset($pages[$delta]);
        }
      }
      $pages = implode("\r\n", $pages);
      $config->set('visibility.request_path.pages', $pages);
      $config->save();
    }
  }

  // Cleanup settings.
  \Drupal::configFactory()->getEditable('social_follow_user.settings')->delete();

  // Remove fields.
  $entity_type_manager = \Drupal::entityTypeManager();

  // Delete field config.
  /** @var \Drupal\field\FieldConfigStorage $field_config_storage */
  $field_config_storage = $entity_type_manager->getStorage('field_config');

  $field = $field_config_storage->load('user.user.field_allow_following');
  if (!is_null($field)) {
    $field->delete();
  }

  // Delete storage.
  /** @var \Drupal\field\FieldStorageConfigStorage $field_storage_storage */
  $field_storage_storage = $entity_type_manager->getStorage('field_storage_config');

  /** @var \Drupal\field\FieldStorageConfigInterface|null $field_storage */
  $field_storage = $field_storage_storage->load('user.field_allow_following');
  if (!is_null($field_storage)) {
    $field_storage->delete();
  }
}

/**
 * Implements hook_update_dependencies().
 */
function social_follow_user_update_dependencies() : array {
  $dependencies = [];

  // The social_user update creates the field group we depend on.
  $dependencies['social_user'][11402] = [
    'social_follow_user' => 11406,
  ];

  return $dependencies;
}

/**
 * Update profile view modes/block visibility rules.
 */
function social_follow_user_update_11201(): void {
  // Display hero & statistic block on the following users/followers pages.
  $config_names = [
    'block.block.socialblue_profile_hero_block',
    'block.block.socialblue_profile_statistic_block',
  ];
  foreach ($config_names as $config_name) {
    $config = \Drupal::configFactory()->getEditable($config_name);

    if (!empty($config->getRawData())) {
      $pages = $config->get('visibility.request_path.pages');
      $pages .= "\r\n/user/*/following/users";
      $pages .= "\r\n/user/*/followers";
      $config->set('visibility.request_path.pages', $pages);
      $config->save();
    }
  }

  // Update statistic profile view mode.
  /** @var \Drupal\update_helper\Updater $updateHelper */
  $updateHelper = \Drupal::service('update_helper.updater');
  // Execute configuration update definitions with logging of success.
  $updateHelper->executeUpdate('social_follow_user', 'social_follow_user_update_11201');
  // Output logged messages to related channel of update execution.
  $updateHelper->logger()->output();
}

/**
 * Create follow/following user lists.
 */
function social_follow_user_update_11202(): void {
  $configs = [
    'views.view.followers_user' => 'views.view.followers_user_11202',
    'views.view.following_users' => 'views.view.following_users_11202',
  ];
  $config_storage = \Drupal::service('config.storage');
  $config_path = \Drupal::moduleHandler()
    ->getModule('social_follow_user')
    ->getPath() . '/config/static/';
  $source = new FileStorage($config_path);

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

/**
 * Remove title on the followers page.
 */
function social_follow_user_update_11203(): 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_follow_user', __FUNCTION__);

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

/**
 * Change a weigh of the social_follow_user module.
 */
function social_follow_user_update_11401(): void {
  module_set_weight('social_follow_user', 6);
}

/**
 * Add "Allow following" field to profile.
 */
function social_follow_user_update_11402(): void {
  $config_path = \Drupal::moduleHandler()
    ->getModule('social_follow_user')
    ->getPath() . '/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.profile.field_profile_allow_following_11402');
  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.profile.profile.field_profile_allow_following_11402');
  if (is_array($data) && is_null($field_config_storage->load($data['id']))) {
    $field_config_storage->create($data)->save();
  }
}

/**
 * Create `field_allow_following` on user.
 */
function social_follow_user_update_11403(): void {
  $profile_storage = FieldStorageConfig::load("profile.field_profile_allow_following");
  $profile_config = FieldConfig::load("profile.profile.field_profile_allow_following");
  if ($profile_storage === NULL || $profile_config === NULL) {
    throw new \RuntimeException("field_profile_allow_following is not correctly installed.");
  }

  $user_storage = FieldStorageConfig::create([
    "langcode" => $profile_storage->get("langcode"),
    "status" => $profile_storage->get("status"),
    "dependencies" => [
      "module" => ["user"],
      "enforced" => [
        "module" => ["social_follow_user"],
      ],
    ],
    "id" => "user.field_allow_following",
    "field_name" => "field_allow_following",
    "entity_type" => "user",
    "type" => $profile_storage->get("type"),
    "settings" => [],
    "module" => $profile_storage->get("module"),
    "locked" => $profile_storage->get("locked"),
    "cardinality" => $profile_storage->get("cardinality"),
    "translatable" => $profile_storage->get("translatable"),
    "indexes" => $profile_storage->get("indexes") ?? [],
    "persist_with_no_fields" => $profile_storage->get("persist_with_no_fields"),
    "custom_storage" => $profile_storage->get("custom_storage"),
  ]);
  $user_storage->save();

  $user_config = FieldConfig::create([
    "langcode" => $profile_config->get("langcode"),
    "status" => $profile_config->get("status"),
    "dependencies" => [
      "config" => ["field.storage.user.field_allow_following"],
      "enforced" => [
        "module" => ["social_follow_user"],
      ],
    ],
    "id" => "user.user.field_allow_following",
    "field_name" => "field_allow_following",
    "entity_type" => "user",
    "bundle" => "user",
    "label" => $profile_config->get("label"),
    "description" => $profile_config->get("description"),
    "required" => $profile_config->get("required"),
    "translatable" => $profile_config->get("translatable"),
    "default_value" => $profile_config->get("default_value"),
    "default_value_callback" => $profile_config->get("default_value_callback"),
    "settings" => $profile_config->get("settings"),
    "field_type" => $profile_config->get("field_type"),
  ]);
  $user_config->save();
}

/**
 * Move `field_profile_allow_following` data to `field_allow_following`.
 */
function social_follow_user_update_11404(array &$sandbox) : void {
  if (!isset($sandbox['count'])) {
    $sandbox['ids'] = \Drupal::entityQuery('user')
      ->accessCheck(FALSE)
      ->execute();

    $sandbox['count'] = count($sandbox['ids']);
    $sandbox['limit'] = Settings::get('entity_update_batch_size', 50);
  }

  $ids = array_splice($sandbox['ids'], 0, $sandbox['limit']);

  // Load accounts by users IDs.
  $users = User::loadMultiple($ids);
  $profile_ids = \Drupal::entityTypeManager()->getStorage('profile')
    ->getQuery()
    ->condition('uid', $ids, 'IN')
    ->condition('type', 'profile')
    ->condition('status', TRUE)
    ->condition('is_default', TRUE)
    ->accessCheck(FALSE)
    ->execute();
  assert(is_array($profile_ids), "Misconfigured query execute.");
  $profiles = Profile::loadMultiple($profile_ids);

  foreach ($profiles as $profile) {
    $user = $users[$profile->getOwnerId()] ?? NULL;
    // Ignore orphaned profiles.
    if ($user === NULL) {
      continue;
    }

    // Move value to user entity.
    $user->set('field_allow_following', $profile->get('field_profile_allow_following')->getValue())->save();
  }

  if (empty($sandbox['ids'])) {
    $sandbox['#finished'] = 1;
  }
  else {
    $sandbox['#finished'] = 1 - (count($sandbox['ids']) / $sandbox['count']);
  }
}

/**
 * Delete `field_profile_allow_following`.
 */
function social_follow_user_update_11405(): void {
  $profile_config = FieldConfig::load("profile.profile.field_profile_allow_following");

  if ($profile_config !== NULL) {
    $profile_config->delete();
  }

  $profile_storage = FieldStorageConfig::load("profile.field_profile_allow_following");
  if ($profile_storage !== NULL) {
    $profile_storage->delete();
  }
}

/**
 * Configure user form display for field_allow_following.
 */
function social_follow_user_update_11406() : void {
  $form_mode = EntityFormDisplay::load("user.user.default");
  if ($form_mode === NULL) {
    throw new \RuntimeException("Default user form mode is missing, Drupal is not correctly installed.");
  }
  $form_mode->setComponent('field_allow_following', [
    "type" => "boolean_checkbox",
    "weight" => 3,
    "region" => "content",
    "settings" => [
      "display_label" => TRUE,
    ],
    "third_party_settings" => [],
  ]);
  $privacy_group = $form_mode->getThirdPartySetting("field_group", "group_privacy");
  if ($privacy_group !== NULL) {
    if (empty($privacy_group['children'])) {
      $privacy_group['children'] = [];
    }
    $privacy_group['children'][] = 'field_allow_following';
  }
  $form_mode->setThirdPartySetting("field_group", "group_privacy", $privacy_group);
  $form_mode->save();
}

/**
 * Update the empty behaviour text for follower_user and following_users.
 */
function social_follow_user_update_11701(): 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_follow_user', __FUNCTION__);

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

/**
 * Remove flag follow user permission if the module is not enabled.
 */
function social_follow_user_update_11702(): void {
  $config = \Drupal::configFactory()
    ->getEditable('social_follow_user.settings');
  $follow_status = $config->get('status');
  $permission = 'flag follow_user';

  // Remove the permissions for following users if disabled.
  if ($follow_status === FALSE) {
    // Permission can be different from default so retrieve it dynamically.
    $roles = user_role_names(FALSE, $permission);
    $roles = array_keys($roles);

    foreach ($roles as $role) {
      user_role_revoke_permissions($role, [$permission]);
    }

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