<?php

/**
 * @file
 * The Social Path Manager module.
 */

use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\group\Entity\Group;
use Drupal\group\Entity\GroupInterface;
use Drupal\group\Entity\GroupType;

/**
 * Implements hook_form_alter().
 */
function social_path_manager_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  $social_group_types = _social_path_manager_group_types();

  $group_forms = [
    'add' => [],
    'edit' => [],
  ];

  foreach ($social_group_types as $social_group_type) {
    $group_forms['edit'][] = "group_{$social_group_type}_edit_form";
    $group_forms['add'][] = "group_{$social_group_type}_add_form";
  }

  if (in_array($form_id, $group_forms['add'], TRUE) || in_array($form_id, $group_forms['edit'], TRUE)) {
    $form['path']['#type'] = 'fieldset';
  }

  $user_forms = [
    'user_register_form',
    'user_form',
  ];

  // Hide the URL alias for user forms, doesn't do anything.
  if (!empty($form['path']) && in_array($form_id, $user_forms, TRUE)) {
    unset($form['path']);
  }
}

/**
 * Implements hook_module_implements_alter().
 */
function social_path_manager_module_implements_alter(&$implementations, $hook) {
  if ($hook === 'form_alter') {
    $group = $implementations['social_path_manager'];
    unset($implementations['social_path_manager']);
    $implementations['social_path_manager'] = $group;
  }
}

/**
 * Implements hook_entity_insert().
 */
function social_path_manager_entity_insert(EntityInterface $entity) {
  _social_path_manager_update_alias($entity, 'create');
}

/**
 * Implements hook_entity_update().
 */
function social_path_manager_entity_update(EntityInterface $entity) {
  _social_path_manager_update_alias($entity, 'update');
}

/**
 * Implements hook_entity_delete().
 */
function social_path_manager_entity_delete(EntityInterface $entity) {
  _social_path_manager_update_alias($entity, 'delete');
}

/**
 * Implements hook_batch_alter().
 */
function social_path_manager_batch_alter(&$batch) {
  if (!isset($batch['source_url'])) {
    return;
  }

  /** @var \Drupal\Core\Url $url */
  $url = &$batch['source_url'];

  // Bulk generate aliases include group tabs.
  if ($url->getRouteName() === 'pathauto.bulk.update.form') {
    $options = $batch['form_state']->getValue('update');
    $action = $batch['form_state']->getValue('action');

    // Check if the operation contains a group update.
    if ($options['canonical_entities:group'] === 'canonical_entities:group') {
      // Set additional batch to create group aliases for group tabs.
      $batch['operations'][] = [
        '_social_path_manager_update_group_tab_aliases',
        ['canonical_entities:group', $action],
      ];
      batch_set($batch);
    }
  }
}

/**
 * Common batch processing callback for all operations.
 *
 * @throws \Exception
 */
function _social_path_manager_update_group_tab_aliases($id, $action) {
  $group_ids = array_keys(\Drupal::entityQuery('group')->accessCheck()->execute());
  $created = 0;

  $groups = Group::loadMultiple($group_ids);
  foreach ($groups as $group) {
    _social_path_manager_update_alias($group, $action, TRUE);
    $created++;
  }

  \Drupal::service('messenger')->addMessage(\Drupal::translation()
    ->formatPlural($created, 'Generated 1 group tab aliases.', 'Generated @count group tab aliases.'));
}

/**
 * Get a full list of group types.
 *
 * @return array
 *   List of group types.
 */
function _social_path_manager_group_types() {
  $types = &drupal_static(__FUNCTION__);
  if (!isset($types)) {
    $types = [];
    /** @var \Drupal\group\Entity\GroupType $group_type */
    foreach (GroupType::loadMultiple() as $group_type) {
      $types[] = $group_type->id();
    }
  }

  // Allow other modules to change the group types.
  \Drupal::moduleHandler()->alter('social_path_manager_group_types', $types);

  return $types;
}

/**
 * Get a list of tabs used in groups.
 *
 * @return array
 *   Array of group tabs.
 */
function _social_path_manager_group_tabs() {
  $tabs = &drupal_static(__FUNCTION__);
  if (!isset($tabs)) {
    /** @var \Drupal\Core\Menu\LocalTaskManager $taskManager */
    $taskManager = Drupal::service('plugin.manager.menu.local_task');
    $tabs = [];

    $group_tabs = $taskManager->getLocalTasksForRoute('entity.group.canonical');
    $group_tabs = $group_tabs[0];

    // Loop over the available tabs on a group.
    foreach ($group_tabs as $key => $localtask) {
      /** @var \Drupal\Core\Url $localtask */
      $tabs[$key] = $localtask->getRouteName();
    }
    // Allow other modules to change the group tabs.
    \Drupal::moduleHandler()->alter('social_path_manager_group_tabs', $tabs);
  }
  return $tabs;
}

/**
 * Implements hook_social_path_manager_group_tabs_alter().
 */
function social_path_manager_social_path_manager_group_tabs_alter(array &$tabs) {
  foreach ($tabs as $key => $route) {
    // Only allow tabs that are part of the group.
    if (strpos($key, 'layout_builder') !== FALSE) {
      unset($tabs[$key]);
    }
  }
}

/**
 * Get the url suffix for a giving route of a group.
 *
 * @param \Drupal\group\Entity\GroupInterface $group
 *   The group being updated.
 * @param string $route
 *   The route of the tab being updated.
 *
 * @return string
 *   The url suffix of the tab.
 */
function _social_path_manager_get_path_suffix(GroupInterface $group, $route) {
  $url = Url::fromRoute($route, ['group' => $group->id()]);

  // Get the last part of the url.
  $url = explode('/', $url->getInternalPath());

  return end($url);
}

/**
 * Create the aliases for the views of the group.
 *
 * @param \Drupal\Core\Entity\EntityInterface $entity
 *   The entity that is the parent for the alias.
 * @param string $op
 *   The operation that is being performed.
 * @param bool $bulk
 *   Parameter to tell if the operation is coming from a bulk or not.
 *
 * @throws \Exception
 */
function _social_path_manager_update_alias(EntityInterface $entity, $op, $bulk = FALSE) {
  if ($entity->getEntityTypeId() === 'group') {
    switch ($op) {
      case 'all':
      case 'update':
      case 'create':
        /** @var \Drupal\Core\Path\AliasManager $pam */
        $pam = \Drupal::service('path_alias.manager');

        // If disabled generate automatic link.
        if (
          !$entity->get('path')->pathauto &&
          $entity->get('path')->alias
        ) {
          $path = $entity->get('path')->getValue()[0];
        }
        else {
          // If it's a bulk generate then get the alias by path.
          if ($bulk === TRUE) {
            $url = Url::fromRoute('entity.group.canonical', ['group' => $entity->id()]);
            $url = $url->getInternalPath();

            $path['alias'] = $pam->getAliasByPath('/' . $url);
          }
          else {
            // New alias.
            $path = \Drupal::service('pathauto.generator')
              ->updateEntityAlias($entity, 'update');
          }
        }

        // Check if the alias changed.
        // If yes, then change all the other views.
        if (!empty($path)) {
          foreach (_social_path_manager_group_tabs() as $route) {
            $suffix = _social_path_manager_get_path_suffix($entity, $route);
            $entity_language = $entity->language()->getId();

            // Prevent adding alias for default tab.
            $source_parts = explode('/', $path['source'] ?? "");
            if (end($source_parts) === $suffix) {
              continue;
            }

            // Get alias of the group tab.
            $grouptab_alias = $pam->getAliasByPath('/group/' . $entity->id() . '/' . $suffix);

            /** @var \Drupal\path_alias\PathAliasStorage $pas */
            $pas = \Drupal::entityTypeManager()->getStorage('path_alias');
            /** @var \Drupal\path_alias\AliasRepositoryInterface $alias_repository */
            $alias_repository = \Drupal::service('path_alias.repository');

            // Check of the alias is an alias or path.
            $alias_details = $alias_repository->lookupByAlias($grouptab_alias, $entity_language);

            // Create a new alias when it does not exist.
            if ($alias_details === NULL) {
              // Insert the alias for the other views.
              $pas->create([
                'path' => '/group/' . $entity->id() . '/' . $suffix,
                'alias' => $path['alias'] . '/' . $suffix,
                'langcode' => $entity_language,
              ])->save();
            }

            // Update alias by deleting the old one and creating a new one.
            // This is necessary because path alias doesn't update our
            // group view aliases whenever we update the Group entity.
            if ($op === 'update' || $op === 'all') {
              \Drupal::service('pathauto.alias_storage_helper')
                ->deleteBySourcePrefix('/group/' . $entity->id() . '/' . $suffix);

              $pas->create([
                'path' => '/group/' . $entity->id() . '/' . $suffix,
                'alias' => $path['alias'] . '/' . $suffix,
                'langcode' => $entity_language,
              ])->save();
            }
          }

          // Clear cache of the group tag and rebuild routes.
          \Drupal::service('cache_tags.invalidator')->invalidateTags(['group:' . $entity->id()]);
          \Drupal::service('router.builder')->rebuild();
        }
        break;

      case 'delete':
        // Delete all the aliases of the deleted group.
        $storage_helper = \Drupal::service('pathauto.alias_storage_helper');
        $storage_helper->deleteBySourcePrefix('/group/' . $entity->id());
        break;
    }
  }
}

/**
 * Implements hook_preprocess_HOOK().
 */
function social_path_manager_preprocess_block(&$variables) {
  /** @var \Drupal\node\Entity\Node $node */
  $node = \Drupal::routeMatch()->getParameter('node');

  // Due to PR #1427 there are now two heroes shown on pages
  // with a certain url alias. Due to the refactor in socialpagetitleblock.
  if ($node !== NULL) {
    if (!empty($variables['elements']['#plugin_id']) &&
      $variables['elements']['#plugin_id'] === 'social_page_title_block' &&
      $variables['elements']['#id'] === 'socialblue_pagetitleblock_content') {
      $current_url = Url::fromRoute('<current>');
      $current_path = $current_url->toString();

      $paths_to_exclude = [
        'edit',
        'add',
        'delete',
      ];

      $in_path = str_replace($paths_to_exclude, '', $current_path) !== $current_path;

      // We make sure there are no two heroes shown only page titles.
      if ($in_path) {
        $variables['content']['#type'] = 'page_title';
        if (!empty($variables['content']['#hero_node'])) {
          unset($variables['content']['#hero_node']);
        }
      }
    }
  }
}
