<?php

/**
 * @file
 * The Social topic module.
 */

use Drupal\block\Entity\Block;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Access\AccessResultNeutral;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\ReplaceCommand;
use Drupal\Core\Block\BlockPluginInterface;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Link;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Url;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\group\Entity\GroupInterface;
use Drupal\menu_link_content\MenuLinkContentInterface;
use Drupal\node\NodeInterface;
use Drupal\social_topic\Controller\SocialTopicController;
use Drupal\taxonomy\TermInterface;
use Drupal\views\Plugin\views\query\QueryPluginBase;
use Drupal\views\ViewExecutable;

/**
 * Prepares variables for node templates.
 *
 * Default template: node.html.twig.
 *
 * Most themes use their own copy of node.html.twig. The default is located
 * inside "/core/modules/node/templates/node.html.twig". Look in there for the
 * full list of variables.
 *
 * @param array $variables
 *   An associative array containing:
 *   - elements: An array of elements to display in view mode.
 *   - node: The node object.
 *   - view_mode: View mode; e.g., 'full', 'teaser', etc.
 */
function social_topic_preprocess_node(array &$variables) {
  /** @var \Drupal\node\NodeInterface $node */
  $node = $variables['node'];
  if ($node->getType() === 'topic') {
    /** @var \Drupal\Core\Field\EntityReferenceFieldItemListInterface $topic_type_field */
    $topic_type_field = $node->get('field_topic_type');
    $topic_types = $topic_type_field->referencedEntities();
    $variables['metadata'] = NULL;
    $variables['topic_type'] = NULL;
    $topic_type = empty($topic_types) ? NULL : reset($topic_types);
    if ($topic_type instanceof TermInterface) {
      $curr_langcode = \Drupal::languageManager()
        ->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)
        ->getId();
      $variables['metadata'] = t('in @topic', [
        '@topic' => $topic_type->toLink()->toString(),
      ]);
      if ($topic_type->isTranslatable() && $topic_type->hasTranslation($curr_langcode)) {
        $topic_type = $topic_type->getTranslation($curr_langcode);
      }

      // Set topic type link.
      $topic_type_url = Url::fromRoute('view.latest_topics.page_latest_topics', ['field_topic_type_target_id' => $topic_type->id()]);
      $topic_type_link = Link::fromTextAndUrl($topic_type->label() ?? '', $topic_type_url)
        ->toString();
      $variables['topic_type'] = $topic_type_link;
      if (
        $topic_type->hasField('field_topic_type_icon') &&
        !$topic_type->get('field_topic_type_icon')->isEmpty()
      ) {
        $variables['topic_type_icon'] = $topic_type->get('field_topic_type_icon')->getString();
      }
      $variables['#cache']['tags'][] = $topic_type->getEntityTypeId() . ':' . $topic_type->id();
    }
  }
}

/**
 * Implements hook_form_form_ID_alter().
 *
 * Enhance the exposed filter form of the topic overview.
 */
function social_topic_form_views_exposed_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  // On user topics overview.
  if ($form['#id'] === 'views-exposed-form-topics-page-profile') {
    $form['type']['#options']['All'] = t('All types');

    $form['status']['#options'][0] = t('Unpublished');
    $form['status']['#options'][1] = t('Published');

    $account_uid = \Drupal::routeMatch()->getParameter('user');
    $current_uid = \Drupal::currentUser()->id();

    if ($account_uid !== $current_uid) {
      $form['status']['#access'] = FALSE;
    }

    // Enable the reset button.
    // @todo make sure the block content refreshes on submit as well (AJAX).
    $form['actions']['reset']['#access'] = TRUE;
    // @todo make sure exposed form filtering redirects to the proper view
    // page, when views is updated.
    $form['#action'] = '/user/' . $account_uid . '/topics';
  }
  // On group topics overview.
  if ($form['#id'] === 'views-exposed-form-group-topics-page-group-topics') {
    $group_from_route = _social_group_get_current_group();

    $current_user = \Drupal::currentUser();
    $membership = FALSE;
    $group_membership = NULL;
    if ($group_from_route instanceof GroupInterface) {
      $group_membership = $group_from_route->getMember($current_user);
    }
    if ($group_membership) {
      $membership = TRUE;
    }

    if (!empty($form['status'])) {
      $form['status']['#options']['All'] = t('All statuses');
      $form['status']['#options'][0] = t('Unpublished');
      $form['status']['#options'][1] = t('Published');

      // Only show the unpublished option when you are member of the group.
      // You can't place content in a group you are not a member of anyway.
      if (!$membership) {
        $form['status']['#access'] = FALSE;
      }
    }

    $form['type']['#options']['All'] = t('All types');

    // Get group from route.
    if (!empty($group_from_route)) {
      $group_id = $group_from_route->id();
    }

    $form['actions']['reset']['#access'] = TRUE;

    // Make sure we redirect to the current group page.
    $form['#action'] = '/group/' . $group_id . '/topics';
  }
}

/**
 * Implements hook_block_view_alter().
 *
 * Add a title to the exposed filter block on the topics overview.
 */
function social_topic_block_view_alter(array &$build, BlockPluginInterface $block) {
  // @todo check out why this happens, is this is a views bug?
  if (isset($build['#plugin_id']) && $build['#plugin_id'] === 'views_exposed_filter_block:topics-page_profile') {
    $build['#configuration']['label'] = $build['#configuration']['views_label'];
  }
}

/**
 * Implements hook_views_query_alter().
 */
function social_topic_views_query_alter(ViewExecutable $view, QueryPluginBase $query) {
  if ($view->id() == 'topics' && $view->getDisplay()->display['id'] == 'page_profile') {
    $account_uid = \Drupal::routeMatch()->getParameter('user');
    $current_uid = \Drupal::currentUser()->id();
    if ($view->exposed_raw_input['status'] == NodeInterface::PUBLISHED || $account_uid !== $current_uid) {
      $query->where[1]['conditions'][] = [
        'field' => 'node_field_data.status',
        'value' => NodeInterface::PUBLISHED,
        'operator' => '=',
      ];
    }
  }
  // Only show the unpublished option when you are member of the group.
  // You can't place content in a group you are not a member of anyway.
  if ($view->id() == 'group_topics' && $view->getDisplay()->display['id'] == 'page_group_topics') {
    $group_from_route = _social_group_get_current_group();
    $membership = FALSE;

    if ($group_from_route instanceof GroupInterface) {
      $current_user = \Drupal::currentUser();
      $group_membership = $group_from_route->getMember($current_user);
      if ($group_membership) {
        $membership = TRUE;
      }
    }

    // Needs 1 and NODE_PUBLISHED, because it can be TRUE ( default ) or "1"
    // (see form alter when published is selected).
    if ($view->exposed_raw_input['status'] == "1" || $view->exposed_raw_input['status'] == NodeInterface::PUBLISHED || !$membership) {
      $query->where[1]['conditions'][] = [
        'field' => 'node_field_data_group_content_field_data.status',
        'value' => NodeInterface::PUBLISHED,
        'operator' => '=',
      ];
    }
  }
}

/**
 * Implements hook_social_tagging_type_alter().
 */
function social_topic_social_tagging_type_alter(array &$items): void {
  $items['node']['sets'][0]['bundles'][] = 'topic';
}

/**
 * Implements hook_social_user_account_header_create_links().
 *
 * Adds the "Create Topic" link to the content creation menu.
 */
function social_topic_social_user_account_header_create_links($context) {
  $link = Link::createFromRoute((new TranslatableMarkup('New Topic')), 'node.add', [
    'node_type' => 'topic',
  ]);

  return [
    'add_topic' => [
      '#type' => 'link',
      '#attributes' => [
        'title' => new TranslatableMarkup('Create New Topic'),
      ],
      '#weight' => 200,
      '#access' => $link->getUrl()->access(),
    ] + $link->toRenderable(),
  ];
}

/**
 * Implements hook_social_user_account_header_account_links().
 *
 * Adds the "View my topics" link to the user menu.
 */
function social_topic_social_user_account_header_account_links(array $context) {
  // We require a user for this link.
  if (empty($context['user']) || !($context['user'] instanceof AccountInterface)) {
    return [];
  }

  $link = Link::createFromRoute((new TranslatableMarkup('My topics')), 'view.topics.page_profile', [
    'user' => $context['user']->id(),
  ]);

  return [
    'my_topics' => [
      '#type' => 'link',
      '#attributes' => [
        'title' => new TranslatableMarkup('View my topics'),
      ],
      '#weight' => 700,
      '#access' => $link->getUrl()->access(),
    ] + $link->toRenderable(),
  ];
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function social_topic_form_node_topic_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  social_topic_widget_alter($form);
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function social_topic_form_node_topic_edit_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  social_topic_widget_alter($form);
}

/**
 * Function that does some magic to the event type field.
 *
 * @param array $form
 *   Form array.
 */
function social_topic_widget_alter(array &$form) {
  /** @var \Drupal\Core\Config\ImmutableConfig $config */
  $config = \Drupal::config('social_topic.settings');

  // Change the widget if more than X.
  if (count($form['field_topic_type']['widget']['#options']) >= $config->get('social_topic_type_select_changer')) {
    $form['field_topic_type']['widget']['#type'] = 'select2';
    $form['field_topic_type']['widget']['#multiple'] = FALSE;
    $topic_field_definitions = \Drupal::service('entity_field.manager')->getFieldDefinitions('node', 'topic');
    if (($topic_type_definition = $topic_field_definitions['field_topic_type']) !== NULL) {
      /** @var \Drupal\field\FieldConfigInterface $topic_type_definition */
      $form['field_topic_type']['widget']['#multiple'] = $topic_type_definition->getFieldStorageDefinition()->isMultiple();
    }
  }
}

/**
 * Custom permission check, to see if people have access to users' topics.
 *
 * Implements hook_block_access().
 */
function social_topic_block_access(Block $block, $operation, AccountInterface $account) {

  if ($operation === 'view' && ($block->getPluginId() === 'views_exposed_filter_block:topics-page_profile' || $block->getPluginId() === 'views_block:topics-block_user_topics')) {
    // Here we're going to assume by default access is not granted.
    $topicController = SocialTopicController::create(\Drupal::getContainer());
    $access = $topicController->myTopicAccess($account);
    // If the 'myTopicAccess' returns 'AccessResultNeutral', we have to assume
    // that access must be denied.
    if ($access instanceof AccessResultNeutral) {
      // Return forbidden, since access was not explicitly granted.
      return AccessResult::forbidden();
    }

    return $access;
  }

  // No opinion.
  return AccessResult::neutral();
}

/**
 * Implements hook_social_follow_content_types_alter().
 */
function social_topic_social_follow_content_types_alter(array &$types) {
  $types[] = 'topic';
}

/**
 * Sets dynamic allowed values for the topic type icon field.
 */
function social_topic_allowed_values_function(FieldStorageConfig $definition, ContentEntityInterface $entity = NULL, $cacheable) {
  $theme_path = \Drupal::service('extension.list.theme')->getPath('socialbase');
  $icon_files = \Drupal::service('file_system')->scanDirectory(DRUPAL_ROOT . '/' . $theme_path . '/components/06-libraries/icons/source', '/^icon-.*\.svg$/', ['recurse' => FALSE]);

  $options = [];
  foreach ($icon_files as $file) {
    $options[$file->name] = $file->name;
  }

  return $options;
}

/**
 * Sets the default value for the topic type icon field.
 */
function social_topic_default_value_function(ContentEntityInterface $entity, FieldDefinitionInterface $definition) {
  return [
    ['value' => 'icon-topic'],
  ];
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function social_topic_form_taxonomy_term_topic_types_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  if (!isset($form['field_topic_type_icon'])) {
    return;
  }

  $form['#attached']['library'][] = 'social_topic/admin';

  $default_icon = $form['field_topic_type_icon']['widget']['#default_value'][0];

  $form['field_topic_type_icon']['preview'] = [
    '#type' => 'inline_template',
    '#template' => '<div class="icon-svg-wrapper" data-href="#' . $default_icon . '"><svg class="selected-svg"><use xlink:href="#' . $default_icon . '"></use></svg>{{ svg_icons|raw }}</div>',
    '#context' => [
      'svg_icons' => file_get_contents(\Drupal::service('extension.list.theme')->getPath('socialbase') . '/assets/icons/icons.svg'),
    ],
  ];

  $form['field_topic_type_icon']['widget']['#ajax'] = [
    'callback' => '_social_topic_change_topic_icon_ajax',
    'event' => 'change',
  ];
}

/**
 * Ajax callback to change topic type icon.
 */
function _social_topic_change_topic_icon_ajax(array $form, FormStateInterface $form_state) {
  $response = new AjaxResponse();

  $icon = $form_state->getValue('field_topic_type_icon')[0]['value'];
  $replaced = [
    '#type' => 'inline_template',
    '#template' => '<div class="icon-svg-wrapper" data-href="#' . $icon . '"><svg class="selected-svg"><use xlink:href="#' . $icon . '"></use></svg>{{ svg_icons|raw }}</div>',
    '#context' => [
      'svg_icons' => file_get_contents(\Drupal::service('extension.list.theme')->getPath('socialbase') . '/assets/icons/icons.svg'),
    ],
  ];

  $response->addCommand(new ReplaceCommand('.icon-svg-wrapper', $replaced));

  return $response;
}

/**
 * Implements hook_social_core_default_main_menu_links_alter().
 */
function social_topic_social_core_default_main_menu_links_alter(array &$links) {
  $link = \Drupal::entityTypeManager()->getStorage('menu_link_content')
    ->loadByProperties([
      'title' => 'All topics',
      'menu_name' => 'main',
      'link' => 'internal:/all-topics',
    ]);
  $link = end($link);

  // Ensure that the end() doesn't return FALSE, and we have link instance.
  if ($link instanceof MenuLinkContentInterface) {
    $links[] = $link;
  }
}
