<?php

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

use Drupal\Core\Config\FileStorage;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\field\FieldConfigInterface;
use Drupal\taxonomy\Entity\Term;
use Drupal\menu_link_content\Entity\MenuLinkContent;
use Drupal\user\Entity\Role;
use Drupal\user\RoleInterface;
use Symfony\Component\Yaml\Yaml;

/**
 * Implements hook_install().
 *
 * Perform actions related to the installation of social_topic.
 */
function social_topic_install() {
  // Grant the default permissions for this feature.
  user_role_grant_permissions(
    RoleInterface::ANONYMOUS_ID,
    [
      'view node.topic.field_content_visibility:public content',
    ]
  );
  user_role_grant_permissions(
    RoleInterface::AUTHENTICATED_ID,
    [
      'view node.topic.field_content_visibility:public content',
    ]
  );
  user_role_grant_permissions(
    'verified',
    [
      'create topic content',
      'delete own topic content',
      'edit own topic content',
      'override topic published option',
      'view node.topic.field_content_visibility:community content',
      'view topics on my profile',
      'view topics on other profiles',
    ]
  );
  user_role_grant_permissions(
    'contentmanager',
    [
      'create topic content',
      'delete own topic content',
      'edit own topic content',
      'override topic published option',
      'view node.topic.field_content_visibility:community content',
      'view topics on my profile',
      'view topics on other profiles',
      'delete any topic content',
      'edit any topic content',
      'revert topic revisions',
      'delete topic revisions',
      'view topic revisions',
      'override topic authored by option',
      'override topic authored on option',
      'override topic promote to front page option',
      'override topic revision option',
      'override topic sticky option',
    ]
  );
  user_role_grant_permissions(
    'sitemanager',
    [
      'create topic content',
      'delete own topic content',
      'edit own topic content',
      'override topic published option',
      'view node.topic.field_content_visibility:community content',
      'view topics on my profile',
      'view topics on other profiles',
      'delete any topic content',
      'edit any topic content',
      'revert topic revisions',
      'delete topic revisions',
      'view topic revisions',
      'override topic authored by option',
      'override topic authored on option',
      'override topic promote to front page option',
      'override topic revision option',
      'override topic sticky option',
      'administer visibility settings',
      'administer social_topic settings',
    ]
  );
  // Add some links.
  $parents = \Drupal::entityTypeManager()->getStorage('menu_link_content')
    ->loadByProperties(['menu_name' => 'main', 'title' => 'Explore', 'expanded' => TRUE]);
  $parent = reset($parents);
  if ($parent !== FALSE) {
    MenuLinkContent::create([
      'title' => t('All topics'),
      'link' => ['uri' => 'internal:/all-topics'],
      'menu_name' => 'main',
      'expanded' => FALSE,
      'weight' => 40,
      'parent' => 'menu_link_content:' . $parent->uuid(),
    ])->save();
  }

  // Create some default taxonomy terms.
  $terms = [
    [
      'label' => t('Blog'),
      'icon' => 'icon-blog',
    ],
    [
      'label' => t('Content'),
      'icon' => 'icon-page',
    ],
    [
      'label' => t('Dialog'),
      'icon' => 'icon-discussion',
    ],
    [
      'label' => t('News'),
      'icon' => 'icon-news',
    ],
  ];

  foreach ($terms as $term) {
    $term = Term::create([
      'vid' => 'topic_types',
      'name' => $term['label'],
      'field_topic_type_icon' => $term['icon'],
    ]);
    $term->save();
  }
}

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

  // New image style change should run after the new image style is imported.
  $dependencies['social_topic'][8909] = [
    'social_core' => 8908,
  ];

  return $dependencies;
}

/**
 * Install download_count module.
 */
function social_topic_update_8001() {

}

/**
 * Set my topics permissions.
 */
function social_topic_update_8002() {
  // Make it so that everyone who has the 'access user profiles' permission,
  // now also get these two new permissions.
  $permissions = [
    'view topics on my profile',
    'view topics on other profiles',
  ];

  /** @var \Drupal\user\Entity\Role $role */
  foreach (Role::loadMultiple() as $role) {
    if ($role->hasPermission('access user profiles')) {
      user_role_grant_permissions($role->id(), $permissions);
    }
  }
}

/**
 * Set social_topic settings.
 */
function social_topic_update_8003() {
  // Give SM the administer social_topic settings permission.
  user_role_grant_permissions('sitemanager', ['administer social_topic settings']);

  // Set default topic type settings.
  $config = \Drupal::configFactory()->getEditable('social_topic.settings');
  $config->set('social_topic_type_select_changer', 8);
  $config->save();
}

/**
 * Update view mode for new small teaser style.
 */
function social_topic_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_topic', 'social_topic_update_8801');

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

/**
 * Update like setting in small teaser style.
 */
function social_topic_update_8901() {
  $config_file = \Drupal::service('extension.list.module')->getPath('social_topic') . '/config/static/core.entity_view_display.node.topic.small_teaser_8901.yml';

  if (is_file($config_file)) {
    $settings = Yaml::parse(file_get_contents($config_file));
    if (is_array($settings)) {
      $config = \Drupal::configFactory()
        ->getEditable('core.entity_view_display.node.topic.small_teaser');

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

/**
 * Empty, moved to social_topic_update_8909().
 */
function social_topic_update_8902() {
  // Code has been moved to social_page_update_8909().
}

/**
 * Add image file extensions to be added as attachments.
 */
function social_topic_update_8903() {
  $config_file = \Drupal::service('extension.list.module')->getPath('social_topic') . '/config/static/field.field.node.topic.field_files_8903.yml';

  if (is_file($config_file)) {
    $settings = Yaml::parse(file_get_contents($config_file));
    if (is_array($settings)) {
      $config = \Drupal::configFactory()
        ->getEditable('field.field.node.topic.field_files');

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

/**
 * Updated fieldset labels and added new fieldset called Settings.
 */
function social_topic_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_topic', 'social_topic_update_8904');

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

/**
 * Added fields to their new respective fieldsets.
 */
function social_topic_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_topic', 'social_topic_update_8905');

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

/**
 * Remove group_topic_description fieldgroup from topic content type.
 */
function social_topic_update_8906() {
  // There can be possibility that someone might have added fields in
  // fieldgroup we are removing. So, we want to skip removal if there are any
  // children present in fieldgroup.
  $group = field_group_load_field_group('group_topic_description', 'node', 'topic', 'form', 'default');
  if ($group && empty($group->children)) {
    field_group_delete_field_group($group);
  }
}

/**
 * Create new field & field storage configuration for new topic type icon field.
 */
function social_topic_update_8907(&$sandbox) {
  $config_file = \Drupal::service('extension.list.module')->getPath('social_topic') . '/config/static/social_topic_update_8907.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'];
  }
}

/**
 * Add icons to default topic types.
 */
function social_topic_update_8908() {
  // Default topic types.
  $topic_types = [
    [
      'label' => t('Blog')->render(),
      'icon' => 'icon-blog',
    ],
    [
      'label' => t('Discussion')->render(),
      'icon' => 'icon-discussion',
    ],
    [
      'label' => t('News')->render(),
      'icon' => 'icon-news',
    ],
  ];

  foreach ($topic_types as $type) {
    $term = _social_topic_get_term_id_by_name($type['label']);
    if ($term && $term->hasField('field_topic_type_icon') && $term->field_topic_type_icon->isEmpty()) {
      $term->field_topic_type_icon->value = $type['icon'];
      $term->save();
    }
  }
}

/**
 * Find topic type term by name.
 */
function _social_topic_get_term_id_by_name($term_name) {
  $terms = \Drupal::entityTypeManager()
    ->getStorage('taxonomy_term')
    ->loadByProperties([
      'name' => $term_name,
      'vid' => 'topic_types',
    ]);
  $term = reset($terms);

  return !empty($term) ? $term : FALSE;
}

/**
 * Show thumbnail on File fields when attachment is an image.
 */
function social_topic_update_8909() {
  $config_file = \Drupal::service('extension.list.module')->getPath('social_topic') . '/config/static/core.entity_view_display.node.topic.default_8909.yml';

  if (is_file($config_file)) {
    $settings = Yaml::parse(file_get_contents($config_file));
    if (is_array($settings)) {
      $config = \Drupal::configFactory()
        ->getEditable('core.entity_view_display.node.topic.default');

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

/**
 * Update topic form labels.
 */
function social_topic_update_10201() {
  /** @var \Drupal\update_helper\Updater $updateHelper */
  $updateHelper = \Drupal::service('update_helper.updater');

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

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

/**
 * Add translation compatibility for Topic fields and views.
 */
function social_topic_update_10301() {
  /** @var \Drupal\update_helper\Updater $updateHelper */
  $updateHelper = \Drupal::service('update_helper.updater');

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

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

/**
 * Allow CM+ translate "Topic" node type.
 */
function social_topic_update_10302() {
  foreach (['sitemanager', 'contentmanager'] as $role_id) {
    user_role_grant_permissions($role_id, ['translate topic node']);
  }
}

/**
 * Update the configs in the database in accordance with the newest schema.
 */
function social_topic_update_10303() {
  /** @var \Drupal\update_helper\Updater $updateHelper */
  $updateHelper = \Drupal::service('update_helper.updater');

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

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

/**
 * Make topic type multiple.
 */
function social_topic_update_11101(): void {
  /** @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface $field_storage_config_storage */
  $field_storage_config_storage = \Drupal::entityTypeManager()->getStorage('field_storage_config');

  /** @var \Drupal\field\FieldStorageConfigInterface $topic_type */
  $topic_type = $field_storage_config_storage->load('node.field_topic_type');
  $topic_type->setCardinality(FieldStorageConfig::CARDINALITY_UNLIMITED)->save();
}

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

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

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

/**
 * Update exposed filter button name.
 */
function social_topic_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_topic', __FUNCTION__);

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

/**
 * Remove deprecated permission "override topic revision log entry".
 */
function social_topic_update_111101(): void {
  $entity_type_manager = \Drupal::entityTypeManager();
  /** @var \Drupal\user\RoleInterface[] $roles */
  $roles = $entity_type_manager->getStorage('user_role')->loadMultiple();

  $deprecated_permissions = [
    'override topic revision log entry',
  ];

  foreach ($deprecated_permissions as $deprecated_permission) {
    foreach ($roles as $role) {
      if ($role->hasPermission($deprecated_permission)) {
        $role->revokePermission($deprecated_permission);
        $role->save();
      }
    }
  }
}

/**
 * Search for invalid permission(s) and remove them from existing roles.
 *
 * Permissions to check:
 * - "translate topic node".
 */
function social_topic_update_111102(): 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 topic node',
  ];

  // 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 sort identifier to exposed sorts.
 */
function social_topic_update_120001(): string {
  /** @var \Drupal\update_helper\Updater $updater */
  $updater = \Drupal::service('update_helper.updater');

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

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

/**
 * Add taxonomy permission with the Taxonomy Access Fix is enabled.
 */
function social_topic_update_120002(): 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 topic_types',
    'view term names in topic_types',
    'select terms in topic_types',
  ];

  // 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.
  $roleUpdated = FALSE;
  foreach ($taxonomy_permissions as $taxonomy_permission) {
    if (!$role->hasPermission($taxonomy_permission)) {
      $role->grantPermission($taxonomy_permission);
      $roleUpdated = TRUE;
    }
  }

  if ($roleUpdated) {
    $role->save();
  }
}
