<?php

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

use Drupal\Core\Config\FileStorage;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\Core\Field\FieldItemList;
use Drupal\Core\File\FileSystemInterface;
use Drupal\crop\Entity\CropType;
use Drupal\file\Entity\File;
use Drupal\menu_link_content\Entity\MenuLinkContent;
use Drupal\user\RoleInterface;
use Drupal\views\Entity\View;

/**
 * Implements hook_requirements().
 */
function social_core_requirements($phase) {
  $requirements = [];

  switch ($phase) {
    case 'update':
      // From 9.0 on-wards a site must update to 8.1 first to ensure that the
      // features removal update hooks have run that are no longer present in
      // Open Social 9.0.
      if (\Drupal::service('update.update_hook_registry')->getInstalledVersion('social_core') <= 8805) {
        $requirements['social_core_update_version'] = [
          'title' => t('Open Social Update Version'),
          'description' => t('You must update Open Social to the latest version of 8.x before upgrading to 9.0 or later.'),
          'severity' => REQUIREMENT_ERROR,
        ];
      }
      break;

  }

  return $requirements;
}

/**
 * Implements hook_install().
 *
 * Perform actions related to the installation of social_core.
 */
function social_core_install() {
  // Grant the default permissions for this feature.
  user_role_grant_permissions(
    RoleInterface::ANONYMOUS_ID,
    [
      'access content',
    ]
  );
  user_role_grant_permissions(
    RoleInterface::AUTHENTICATED_ID,
    [
      'access content',
      'view own unpublished content',
      'use text format basic_html',
      'use text format simple_text',
      'view the administration theme',
    ]
  );
  user_role_grant_permissions(
    'contentmanager',
    [
      'access content overview',
      'access toolbar',
      'administer nodes',
      'administer menu',
      'access site reports',
      'access administration pages',
      'view all revisions',
      'revert all revisions',
      'delete all revisions',
      'create url aliases',
      'use text format full_html',
      'access social_core dashboard',
    ]
  );
  user_role_grant_permissions(
    'sitemanager',
    [
      'access content overview',
      'access toolbar',
      'administer nodes',
      'administer menu',
      'access site reports',
      'access administration pages',
      'view all revisions',
      'revert all revisions',
      'delete all revisions',
      'create url aliases',
      'use text format full_html',
      'administer taxonomy',
      'delete terms in expertise',
      'edit terms in expertise',
      'delete terms in interests',
      'edit terms in interests',
      'delete terms in topic_types',
      'edit terms in topic_types',
      'administer site configuration',
      'administer users',
      'administer account settings',
      'administer themes',
      'administer blocks',
      'administer block content',
      'access social_core dashboard',
    ]
  );
  // Create AN Homepage block.
  _social_core_create_an_homepage_block();
  // Add menu items.
  _social_core_create_menu_links();

  // Set image quality defaults.
  $config = \Drupal::service('config.factory')->getEditable('system.image.gd');
  $config->set('jpeg_quality', 90)->save();

  // If social blue is enabled, and default image is still Open Social.
  // Lets add the default e-mail logo for open social as well.
  _social_core_set_default_email_logo_for_socialblue();
  // Add favicon to GIN.
  _social_core_set_favicon();
}

/**
 * Custom function to create the homepage block for AN users.
 */
function _social_core_create_an_homepage_block() {
  // @todo use a better image from the theme.
  // Block image.
  $path = \Drupal::service('extension.list.module')->getPath('social_core');
  $image_path = $path . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . 'homepage-block.jpg';
  $uri = \Drupal::service('file_system')->copy($image_path, 'public://homepage-block.jpg', FileSystemInterface::EXISTS_REPLACE);

  $media = File::create([
    'langcode' => 'en',
    'uid' => 1,
    'status' => 1,
    'uri' => $uri,
  ]);
  $media->save();

  $fid = $media->id();

  // Apply image cropping.
  $data = [
    'x' => 0,
    'y' => 0,
    'width' => 1200,
    'height' => 490,
  ];
  $crop_type = \Drupal::entityTypeManager()
    ->getStorage('crop_type')
    ->load('hero_an');
  if (!empty($crop_type) && $crop_type instanceof CropType) {
    $image_widget_crop_manager = \Drupal::service('image_widget_crop.manager');
    $image_widget_crop_manager->applyCrop($data, [
      'file-uri' => $uri,
      'file-id' => $fid,
    ], $crop_type);
  }

  // Create a block with a specific uuid so we can use it in the config
  // to load it into the theme see block.block.anhomepageheroblock.yml.
  $block = \Drupal::entityTypeManager()->getStorage('block_content')->create([
    'type' => 'hero_call_to_action_block',
    'info' => 'AN homepage hero block',
    'uuid' => '8bb9d4bb-f182-4afc-b138-8a4b802824e4',
  ]);
  $block->field_text_block = [
    'value' => '<h3>Become a member or log in to your community</h3><p>This community is powered by Open Social, the plug-and-play community solution for NGOs and semi-governmental organizations.</p>',
    'format' => 'full_html',
  ];

  $block_image = [
    'target_id' => $fid,
    'alt' => "Anonymous front page image homepage'",
  ];
  $block->field_hero_image = $block_image;

  // Set the links.
  $action_links = [
    [
      'uri' => 'internal:/user/register',
      'title' => t('Sign up'),
    ],
    [
      'uri' => 'internal:/user/login',
      'title' => t('Login'),
    ],
  ];
  $itemList = new FieldItemList($block->field_call_to_action_link->getFieldDefinition());
  $itemList->setValue($action_links);
  $block->field_call_to_action_link = $itemList;
  $block->save();
}

/**
 * Function to create some menu items.
 */
function _social_core_create_menu_links() {
  // Home.
  MenuLinkContent::create([
    'title' => t('Home'),
    'link' => ['uri' => 'internal:/'],
    'menu_name' => 'main',
    'expanded' => FALSE,
    'weight' => 10,
  ])->save();

  // Explore.
  MenuLinkContent::create([
    'title' => t('Explore'),
    // This way we get an empty link.
    'link' => ['uri' => 'internal:'],
    'menu_name' => 'main',
    'expanded' => TRUE,
    'weight' => 20,
  ])->save();

}

/**
 * Implements hook_update_dependencies().
 */
function social_core_update_dependencies(): array {
  // Update 123003 fixes a view corruption from 8105,
  // so that one needs to be executed first.
  $dependencies['social_core'][123003] = [
    'message' => 8105,
  ];

  return $dependencies;
}

/**
 * Implements hook_update_last_removed().
 */
function social_core_update_last_removed() : int {
  return 111206;
}

/**
 * Install social emoji module.
 */
function social_core_update_121001(): void {
  $modules = [
    'social_emoji',
  ];
  \Drupal::service('module_installer')->install($modules);
}

/**
 * Add permission for SM to edit block content.
 */
function social_core_update_121002(): void {
  // This was added in a Drupal Core release as new permission.
  // Considering we already assign administer blocks, this seems like a good
  // first iteration.
  user_role_grant_permissions('sitemanager', ['administer block content']);
}

/**
 * Create views.view.posts (module social_post).
 */
function social_core_update_122001(): void {
  // Only create if the posts view doesn't exist and views is enabled.
  if (\Drupal::moduleHandler()->moduleExists('views') && !View::load('posts')) {
    $config_path = Drupal::service('extension.list.module')->getPath('social_post') . '/config/install';
    $source = new FileStorage($config_path);
    $data = $source->read('views.view.posts');

    if (is_array($data)) {
      \Drupal::entityTypeManager()->getStorage('view')->create($data)->save();
    }
  }
}

/**
 * Uninstall admin_toolbar_tools if nothing uses it.
 */
function social_core_update_123001() : void {
  // Nothing to do if the module is not installed.
  if (!\Drupal::moduleHandler()->moduleExists('admin_toolbar_tools')) {
    return;
  }

  // If there are modules depending on admin_toolbar_tools, and it is enabled
  // we can't uninstall it.
  $module_dependencies = \Drupal::service('extension.list.module')->get('admin_toolbar_tools')->required_by ?? [];
  $has_dependency_enabled = FALSE;
  foreach ($module_dependencies as $module_name => $module_dependency) {
    if (\Drupal::moduleHandler()->moduleExists($module_name)) {
      $has_dependency_enabled = TRUE;
      break;
    }
  }
  if ($has_dependency_enabled) {
    return;
  }

  // Uninstall the module (nothing should depend on it but let the module
  // uninstaller double-check just in case).
  \Drupal::service('module_installer')->uninstall(['admin_toolbar_tools'], FALSE);
}

/**
 * Add permission for SM to administer site configuration.
 */
function social_core_update_123002(): void {
  user_role_grant_permissions('sitemanager', ['administer site configuration']);
}

/**
 * Set cache maximum age with 1 hour.
 */
function social_core_update_123003(): void {
  // Get cache configuration.
  $config = \Drupal::service('config.factory')->getEditable('system.performance');

  // When the max-age is empty, set 1 hour.
  if (empty($config->get('cache.page.max_age'))) {
    $config->set('cache.page.max_age', 3600)
      ->save();
  }
}

/**
 * Recreate views.view.message (module message) if it is corrupted.
 */
function social_core_update_123004(): void {
  if (\Drupal::moduleHandler()->moduleExists('views') && \Drupal::moduleHandler()->moduleExists('message')) {
    // Check if the message view exists.
    $views = View::load('views.view.message');

    // If the view does not exist, it can still be in the config table.
    if ($views === NULL) {
      $config = \Drupal::service('config.factory')
        ->getEditable('views.view.message');

      // If the config view does exist it means it's not a valid view
      // and corrupted by the message update hook 8105.
      if (!empty($config->getRawdata())) {
        // Replace the view config with the new one.
        $config_path = \Drupal::service('extension.list.module')->getPath('social_core') . '/config/static';
        $source = new FileStorage($config_path);
        /** @var \Drupal\Core\Config\StorageInterface $config_storage */
        $config_storage = \Drupal::service('config.storage');

        $data = $source->read('views.view.message_123003');
        if (is_array($data)) {
          $config_storage->write('views.view.message', $data);
        }
      }
    }
  }
}

/**
 * Uninstall Social Lazy Loading module.
 */
function social_core_update_124000(): void {
  // Nothing to do if the module is not installed.
  if (!\Drupal::moduleHandler()->moduleExists('social_lazy_loading')) {
    return;
  }

  // If there are modules depending on admin_toolbar_tools, and it is enabled
  // we can't uninstall it.
  $module_dependencies = \Drupal::service('extension.list.module')->get('social_lazy_loading')->required_by ?? [];
  $has_dependency_enabled = FALSE;
  foreach ($module_dependencies as $module_name => $module_dependency) {
    if (\Drupal::moduleHandler()->moduleExists($module_name)) {
      $has_dependency_enabled = TRUE;
      break;
    }
  }
  if ($has_dependency_enabled) {
    return;
  }

  // Uninstall the module (nothing should depend on it but let the module
  // uninstaller double-check just in case).
  \Drupal::service('module_installer')->uninstall(['social_lazy_loading'], FALSE);
}

/**
 * Uninstall Lazy module.
 */
function social_core_update_124001(): void {
  // Nothing to do if the module is not installed.
  if (!\Drupal::moduleHandler()->moduleExists('lazy')) {
    return;
  }

  // If there are modules depending on admin_toolbar_tools, and it is enabled
  // we can't uninstall it.
  $module_dependencies = \Drupal::service('extension.list.module')->get('lazy')->required_by ?? [];
  $has_dependency_enabled = FALSE;
  foreach ($module_dependencies as $module_name => $module_dependency) {
    if (\Drupal::moduleHandler()->moduleExists($module_name)) {
      $has_dependency_enabled = TRUE;
      break;
    }
  }
  if ($has_dependency_enabled) {
    return;
  }

  // Uninstall the module (nothing should depend on it but let the module
  // uninstaller double-check just in case).
  \Drupal::service('module_installer')->uninstall(['lazy'], FALSE);

  // Remove configuration.
  $config = \Drupal::service('config.factory')->getEditable('lazy.settings');
  $config->delete()->save();
}

/**
 * Bootstrap 8.x-3.32 #3427365.
 *
 * Due to themes not having update hooks this needs to be added for Open Social.
 * Otherwise, it will result in dependencies not met.
 */
function social_core_update_124002(): void {
  $modules = [
    'jquery_ui',
    'jquery_ui_draggable',
    'jquery_ui_resizable',
  ];
  \Drupal::service('module_installer')->install($modules);
}

/**
 * Update editor.
 */
function social_core_update_124003() : void {
  $editor_ids = [
    'editor.editor.basic_html',
    'editor.editor.full_html',
  ];

  foreach ($editor_ids as $editor_id) {
    $editor_config = \Drupal::configFactory()->getEditable($editor_id);
    // Skip editor configs that don't yet exist.
    if ($editor_config->isNew()) {
      continue;
    }
    // Only update config that isn't already private.
    if ($editor_config->get('image_upload.scheme') === "public") {
      $editor_config->set('image_upload.scheme', 'private')->save();
    }
  }
}

/**
 * Fix move config overrides into active configuration.
 */
function social_core_update_124004() : void {
  /** @var \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory */
  $logger_factory = \Drupal::service('logger.factory');
  $logger = $logger_factory->get('os_private_update_logger');

  $field_storages = [
    ['entity_type' => 'block_content', 'field_name' => 'field_hero_image'],
    ['entity_type' => 'comment', 'field_name' => 'field_comment_files'],
    ['entity_type' => 'group', 'field_name' => 'field_group_image'],
    ['entity_type' => 'node', 'field_name' => 'field_book_image'],
    ['entity_type' => 'node', 'field_name' => 'field_event_image'],
    ['entity_type' => 'node', 'field_name' => 'field_files'],
    ['entity_type' => 'node', 'field_name' => 'field_page_image'],
    ['entity_type' => 'node', 'field_name' => 'field_topic_image'],
    ['entity_type' => 'post', 'field_name' => 'field_post_image'],
    ['entity_type' => 'profile', 'field_name' => 'field_profile_image'],
    ['entity_type' => 'profile', 'field_name' => 'field_profile_banner_image'],
    ['entity_type' => 'paragraph', 'field_name' => 'field_hero_image'],
    ['entity_type' => 'paragraph', 'field_name' => 'field_hero_small_image'],
  ];

  foreach ($field_storages as $field_storage) {
    $storage = FieldStorageConfig::loadByName($field_storage['entity_type'], $field_storage['field_name']);
    // No need to update fields that aren't installed.
    if ($storage === NULL) {
      $logger->info('Field storage not found: ' . $field_storage['entity_type'] . '.' . $field_storage['field_name']);
      continue;
    }
    // Only update fields that aren't already private.
    if ($storage->getSetting("uri_scheme") === "public") {
      $logger->info('Updating field storage: ' . $field_storage['entity_type'] . '.' . $field_storage['field_name']);
      $storage->setSetting("uri_scheme", "private");
      $storage->save();
      $logger->info('Field storage updated: ' . $field_storage['entity_type'] . '.' . $field_storage['field_name']);
    }
    else {
      $logger->info('Field storage already private: ' . $field_storage['entity_type'] . '.' . $field_storage['field_name']);
    }
  }
}
