<?php

/**
 * @file
 * Installation file for Social Tagging.
 */

use Drupal\Core\Config\FileStorage;
use Drupal\Core\Entity\EntityStorageException;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\search_api\Item\Field;

/**
 * Install the module.
 */
function social_tagging_install(): void {
  // Grant the default permissions for this feature.
  user_role_grant_permissions(
    'sitemanager',
    [
      'administer social_tagging',
      'delete terms in social_tagging',
      'edit terms in social_tagging',
    ]
  );

  // Add the new search api fields.
  _social_tagging_add_fields_search_api();

  // If the search module is enabled trigger updating of the indexes affected
  // by tagging.
  try {
    if (\Drupal::moduleHandler()->moduleExists('social_search')) {
      social_search_resave_search_indexes(['social_content', 'social_groups']);
    }
  }
  catch (EntityStorageException $e) {
    \Drupal::logger('social_tagging')->info($e->getMessage());
  }
}

/**
 * Uninstall the module.
 */
function social_tagging_uninstall(): void {
  // Remove the search api fields.
  _social_tagging_remove_fields_search_api();

  try {
    if (\Drupal::moduleHandler()->moduleExists('social_search')) {
      social_search_resave_search_indexes(['social_content', 'social_groups']);
    }
  }
  catch (EntityStorageException $e) {
    \Drupal::logger('social_tagging')->info($e->getMessage());
  }
}

/**
 * Add fields to search API.
 */
function _social_tagging_search_api_fields(): array {
  return [
    'social_content' => 'node',
    'social_groups' => 'group',
    'social_users' => 'profile',
  ];
}

/**
 * Install a base field for an entity type.
 *
 * @param string $entity_type
 *   The entity type identifier.
 */
function _social_tagging_add_base_field(string $entity_type): void {
  \Drupal::entityDefinitionUpdateManager()->installFieldStorageDefinition(
    'social_tagging',
    $entity_type,
    'social_tagging',
    _social_tagging_field(),
  );
}

/**
 * Add fields to search API.
 */
function _social_tagging_add_fields_search_api(): void {
  $fields = _social_tagging_search_api_fields();

  foreach ($fields as $index => $type) {
    $index_storage = \Drupal::entityTypeManager()
      ->getStorage('search_api_index');

    /** @var \Drupal\search_api\IndexInterface $index */
    $index = $index_storage->load($index);

    // Only add the field if it doesn't exist yet.
    if (!$index->getField('social_tagging')) {
      $field_intro = new Field($index, 'social_tagging');
      $field_intro->setType('integer');
      $field_intro->setPropertyPath('social_tagging');
      $field_intro->setDatasourceId('entity:' . $type);
      $field_intro->setLabel('Social Tagging');
      $index->addField($field_intro);

      $index->save();
    }
  }
}

/**
 * Remove fields from search API.
 */
function _social_tagging_remove_fields_search_api(): void {
  $fields = _social_tagging_search_api_fields();

  foreach ($fields as $index => $type) {
    $index_storage = \Drupal::entityTypeManager()
      ->getStorage('search_api_index');

    /** @var \Drupal\search_api\IndexInterface $index */
    $index = $index_storage->load($index);

    $index->removeField('social_tagging');
    $index->save();
  }
}

/**
 * Update the field definitions on install, or in an update hook.
 *
 * @param string $field
 *   The field definition you want to update.
 */
function _social_profile_field_definitions_update(string $field): void {
  // Update definitions and schema.
  $list = \Drupal::entityDefinitionUpdateManager()->getChangeList();
  if (!empty($list[$field])) {
    foreach ($list[$field] as $item) {
      \Drupal::entityDefinitionUpdateManager()->updateEntityType($item);
    }
  }
}

/**
 * Exclude landing pages from tagging.
 */
function social_tagging_update_8001(): void {
  // Set allow to true, since that's the case by default.
  $config = \Drupal::getContainer()->get('config.factory')->getEditable('social_tagging.settings');
  $config->set('tag_node_type_landing_page', FALSE)->save();
}

/**
 * Toggle group index.
 */
function social_tagging_update_8002(): void {
  // Toggle the index groups.
  try {
    // If the search module is enabled we need to update the group index.
    if (\Drupal::moduleHandler()->moduleExists('social_search')) {
      social_search_resave_search_indexes(['social_groups']);
    }
  }
  catch (EntityStorageException $e) {
    \Drupal::logger('social_tagging')->info($e->getMessage());
  }
}

/**
 * Install Tagging base field to profile entity type.
 */
function social_tagging_update_8003(): void {
  $field_storage_definition = BaseFieldDefinition::create('entity_reference')
    ->setLabel(t('Tagging'))
    ->setDescription(t('Tagging field.'))
    ->setSetting('target_type', 'taxonomy_term')
    ->setSetting('handler', 'default:taxonomy_term')
    ->setSetting('handler_settings', [
      'target_bundles' => [
        'social_tagging' => 'social_tagging',
      ],
    ])
    ->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED)
    ->setDisplayOptions('view', [
      'region' => 'hidden',
    ])
    ->setDisplayOptions('form', [
      'type' => 'options_select',
      'weight' => 3,
      'settings' => [],
    ])
    ->setDisplayConfigurable('form', TRUE)
    ->setDisplayConfigurable('view', TRUE);

  \Drupal::entityDefinitionUpdateManager()
    ->installFieldStorageDefinition('social_tagging', 'profile', 'social_tagging', $field_storage_definition);
}

/**
 * Toggle user index.
 */
function social_tagging_update_8004(): void {
  _social_profile_field_definitions_update('social_tagging');

  // Toggle the index users.
  try {
    // If the search module is enabled we need to update the group index.
    if (\Drupal::moduleHandler()->moduleExists('social_search')) {
      social_search_resave_search_indexes(['social_users']);
    }
  }
  catch (EntityStorageException $e) {
    \Drupal::logger('social_tagging')->info($e->getMessage());
  }
}

/**
 * Add the search api fields.
 */
function social_tagging_update_8005(): void {
  _social_tagging_add_fields_search_api();

  // Update definitions and schema.
  _social_profile_field_definitions_update('social_tagging');

  // Toggle the index users.
  try {
    // If the search module is enabled we need to update the group index.
    if (\Drupal::moduleHandler()->moduleExists('social_search')) {
      social_search_resave_search_indexes(['social_users']);
    }
  }
  catch (EntityStorageException $e) {
    \Drupal::logger('social_tagging')->info($e->getMessage());
  }
}

/**
 * Make "Social Tagging" vocabulary translatable.
 */
function social_tagging_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_tagging') . '/config/static';
  $source = new FileStorage($config_path);

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

/**
 * Install Tagging base field to the media entity type.
 */
function social_tagging_update_11401(): void {
  if (\Drupal::moduleHandler()->moduleExists('media')) {
    _social_tagging_add_base_field('media');
  }
}

/**
 * Remove filters from the views config so that it can be added in an overrider.
 */
function social_tagging_update_11402(): void {
  $config = \Drupal::configFactory()->getEditable('views.view.newest_users');

  if ($filters = $config->get('display.default.display_options.filters')) {
    $filters = array_filter($filters, fn($filter) => $filter['field'] !== 'social_tagging_target_id');
    $config->set('display.page_newest_users.display_options.filters', $filters);
    $config->set('display.block_newest_users.display_options.filters', $filters);
    $config->set('display.page_newest_users.display_options.defaults.filters', FALSE);
    $config->set('display.block_newest_users.display_options.defaults.filters', FALSE);
    $config->save(TRUE);
  }
}

/**
 * Install a base field for supported entity types.
 */
function social_tagging_update_11501(): void {
  /** @var \Drupal\social_tagging\SocialTaggingServiceInterface $helper */
  $helper = \Drupal::service('social_tagging.tag_service');

  foreach ($helper->types(TRUE) as $entity_type) {
    _social_tagging_add_base_field($entity_type);
  }
}

/**
 * Add a new field to social tagging.
 */
function social_tagging_update_11801(): void {
  $config_path = \Drupal::service('extension.list.module')->getPath('social_tagging') . '/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.
  /** @var \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_manager */
  $field_type_manager = \Drupal::service('plugin.manager.field.field_type');
  $data = $source->read('field.storage.taxonomy_term.field_term_page_url_11801');
  if (is_array($data)) {
    /** @var \Drupal\Core\Field\FieldItemInterface $class */
    $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();
    }
  }

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

  // Create/update a form/view display for the 'Social tagging' terms.
  $displays = [
    'entity_form_display',
    'entity_view_display',
  ];

  foreach ($displays as $display) {
    $display_storage = $entity_type_manager->getStorage($display);
    $data = $source->read("core.$display.taxonomy_term.social_tagging.default_11801");
    if (is_array($data)) {
      if (is_null($display_storage->load($data['id']))) {
        $display_storage->create($data)->save();
      }
      else {
        /** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $view_display */
        $view_display = $display_storage->load($data['id']);
        $view_display->setComponent('field_term_page_url', $data['content']['field_term_page_url']);
        $view_display->save();
      }
    }
  }
}

/**
 * Add Placement field to Content tags.
 */
function social_tagging_update_11802(): void {
  $config_path = \Drupal::service('extension.list.module')->getPath('social_tagging') . '/config/static';
  $source = new FileStorage($config_path);
  $entity_type_manager = \Drupal::entityTypeManager();

  // Create field storages.
  /** @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface $field_storage_config_storage */
  $field_storage_config_storage = $entity_type_manager->getStorage('field_storage_config');
  $data = $source->read('field.storage.taxonomy_term.field_category_usage_11802');
  if (!is_array($data)) {
    throw new \Exception('Could not load config file: field.storage.taxonomy_term.field_category_usage_11802');
  }
  $field_storage_config_storage->create($data)->save();

  // Create field settings.
  /** @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface $field_config_storage */
  $field_config_storage = $entity_type_manager->getStorage('field_config');
  $data = $source->read('field.field.taxonomy_term.social_tagging.field_category_usage_11802');
  if (!is_array($data)) {
    throw new \Exception('Could not load config file: field.field.taxonomy_term.social_tagging.field_category_usage_11802');
  }
  $field_config_storage->create($data)->save();
}

/**
 * Update a form and view display for the 'Social tagging' terms.
 */
function social_tagging_update_11803(): string {
  /** @var \Drupal\update_helper\Updater $updater */
  $updater = \Drupal::service('update_helper.updater');

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

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

/**
 * Fill content to field_category_usage field.
 */
function social_tagging_update_11804(): void {
  /** @var \Drupal\social_tagging\SocialTaggingServiceInterface $helper */
  $helper = \Drupal::service('social_tagging.tag_service');
  $options = $helper->getKeyValueOptions();
  // Option contains key=>value array where values are a label.
  // Get keys, and serialize like in TaggingUsageWidget.
  $values = array_keys($options);
  $terms = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadByProperties([
    'vid' => 'social_tagging',
    'parent' => '0',
  ]);
  foreach ($terms as $term) {
    $term->set('field_category_usage', serialize($values))->save();
  }
}
