<?php

/**
 * @file
 * The social profile fields module file.
 */

use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\ContentEntityFormInterface;
use Drupal\Core\Entity\Display\EntityFormDisplayInterface;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\profile\Entity\ProfileInterface;
use Drupal\profile\Entity\ProfileType;

/**
 * Implements hook_entity_field_access().
 */
function social_profile_fields_entity_field_access($operation, FieldDefinitionInterface $field_definition, AccountInterface $account, FieldItemListInterface $items = NULL) {

  // By default, we return Switzerland.
  $access_result = AccessResult::neutral();

  if ($field_definition->getTargetEntityTypeId() === 'profile') {
    $config = \Drupal::config('social_profile_fields.settings');
    $setting_name = $field_definition->getTargetEntityTypeId() . '_' . $field_definition->getTargetBundle() . '_' . $field_definition->getName();
    $setting_value = $config->get($setting_name);

    if (isset($setting_value) && !$setting_value) {
      // For the profile image field we have an exception. We'll fix it before
      // display.
      if ($setting_name === 'profile_profile_field_profile_image' && $operation === 'view') {
        $access_result = AccessResult::neutral();
      }
      else {
        $access_result = AccessResult::forbidden();
      }
    }
  }

  return $access_result;
}

/**
 * Implements hook_entity_form_display_alter().
 */
function social_profile_fields_entity_form_display_alter(EntityFormDisplayInterface $form_display, array $context) {
  if ($context['entity_type'] !== 'profile' || $context['form_mode'] !== 'edit') {
    return;
  }

  $form_display->setComponent('field_profile_nationality', [
    'weight' => 8,
    'settings' => [
      'match_operator' => 'CONTAINS',
      'size' => 60,
      'placeholder' => '',
      'match_limit' => 10,
    ],
    'third_party_settings' => [],
    'type' => 'entity_reference_autocomplete_tags',
    'region' => 'content',
  ]);
}

/**
 * Implements hook_entity_view_display_alter().
 */
function social_profile_fields_entity_view_display_alter(EntityViewDisplayInterface $display, array $context) {
  if (
    $context['entity_type'] === 'profile' &&
    $context['view_mode'] === EntityDisplayRepositoryInterface::DEFAULT_DISPLAY_MODE
  ) {
    $display->setComponent('field_profile_nationality', [
      'weight' => 0,
      'label' => 'above',
      'settings' => [
        'link' => FALSE,
      ],
      'third_party_settings' => [],
      'type' => 'entity_reference_label',
      'region' => 'content',
    ]);
  }
}

/**
 * Implements hook_form_alter().
 */
function social_profile_fields_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  $form_object = $form_state->getFormObject();

  if (
    $form_object instanceof ContentEntityFormInterface &&
    $form_object->getEntity()->getEntityTypeId() === 'profile' &&
    isset($form['field_profile_nationality']['widget']['target_id'])
  ) {
    $form['field_profile_nationality']['widget']['target_id']['#selection_settings']['hide_id'] = TRUE;
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function social_profile_fields_form_profile_profile_edit_form_alter(&$form, FormStateInterface $form_state) {
  /** @var \Drupal\Core\Entity\ContentEntityFormInterface $form_object */
  $form_object = $form_state->getFormObject();
  /** @var \Drupal\profile\Entity\ProfileInterface $form_entity */
  $form_entity = $form_object->getEntity();

  $user = \Drupal::currentUser();
  $hide_revision_field = FALSE;

  // Don't show revision field, if the user can't edit all profiles.
  if (!$user->hasPermission('update any profile profile')) {
    $hide_revision_field = TRUE;
  }

  // Don't show the revision field, if user is editing it's own profile.
  if (
    $form_object instanceof ContentEntityFormInterface &&
    $form_entity->getEntityTypeId() === 'profile' &&
    $user->id() == $form_entity->getOwnerId()
  ) {
    $hide_revision_field = TRUE;
  }

  if ($hide_revision_field) {
    $form['revision_log_message']['#access'] = FALSE;
  }

  $form['field_profile_nationality']['#group'] = 'group_profile_contact_info';
  $form['#group_children']['field_profile_nationality'] = 'group_profile_contact_info';
}

/**
 * Implements hook_social_user_name_display_suggestions().
 *
 * Adds the nickname as a possible display name.
 *
 * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
 * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
 */
function social_profile_fields_social_user_name_display_suggestions(AccountInterface $account) : array {
  $suggestions = [];

  $entityTypeManager = \Drupal::entityTypeManager();

  /** @var \Drupal\profile\ProfileStorageInterface $storage */
  $storage = $entityTypeManager->getStorage('profile');

  if (!$user_profile = $storage->loadByUser($account, 'profile', TRUE)) {
    return $suggestions;
  }

  if (_social_profile_fields_get_setting('profile_profile_field_profile_nick_name') && !$user_profile->get('field_profile_nick_name')->isEmpty()) {
    /** @var \Drupal\profile\ProfileAccessControlHandler $accessControlHandler */
    $accessControlHandler = $entityTypeManager->getAccessControlHandler('profile');

    $nick_name_field = $user_profile->get('field_profile_nick_name');

    $nick_name = $accessControlHandler->fieldAccess('view', $nick_name_field->getFieldDefinition(), NULL, $nick_name_field, FALSE)
      ? $nick_name_field->getString()
      : "";

    if ($nick_name !== '') {
      // Add the nickname with a low weight so it's shown before the full name.
      $suggestions['nickname'] = [
        'weight' => -100,
        'name' => $nick_name,
      ];
    }
  }

  return $suggestions;
}

/**
 * Implements hook_social_user_name_display_suggestions_alter().
 *
 * If the first name or last name (or both) is not enabled then we alter the
 * display of the full_name suggestion.
 *
 * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
 * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
 */
function social_profile_fields_social_user_name_display_suggestions_alter(array &$suggestions, AccountInterface $account) {
  social_profile_fields_censor_full_name_suggestion($suggestions, $account);
}

/**
 * Implements hook_social_user_name_display_suggestions_alter().
 *
 * Ensures that the full name suggestion doesn't contain disabled fields.
 *
 * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
 * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
 */
function social_profile_fields_censor_full_name_suggestion(array &$suggestions, AccountInterface $account) {
  // If there's no full name for us to censor then we're done.
  if (empty($suggestions['full_name'])) {
    return;
  }

  $use_first_name = _social_profile_fields_get_setting('profile_profile_field_profile_first_name');
  $use_last_name = _social_profile_fields_get_setting('profile_profile_field_profile_last_name');

  // If both fields are enabled then there is nothing for us to censor.
  if ($use_first_name && $use_last_name) {
    return;
  }

  // The field that is enabled must be loaded and checked to be non-empty.
  /** @var \Drupal\profile\ProfileStorageInterface $storage */
  $storage = \Drupal::entityTypeManager()->getStorage('profile');
  $user_profile = $storage->loadByUser($account, 'profile', TRUE);

  // If we don't have a user profile then someone else has probably found data
  // from elsewhere and we have no fields to use.
  if (!$user_profile) {
    return;
  }

  // If both fields are disabled then we can unset the suggestion and be done.
  // We do this only if there's a profile because otherwise we may throw away
  // someone else's full_name pulled from elsewhere.
  if (!$use_first_name && !$use_last_name) {
    unset($suggestions['full_name']);
    return;
  }

  // We know that only one field is active.
  // So check just the first name.
  if ($use_first_name) {
    $first_name = $user_profile->get('field_profile_first_name')->value;
    // If the only field we're allowed to use is empty then we remove the
    // suggestion altogether and fall back to something else.
    if (empty($first_name)) {
      unset($suggestions['full_name']);
    }
    // Otherwise we use it.
    else {
      $suggestions['full_name']['name'] = $first_name;
    }
  }
  else {
    $last_name = $user_profile->get('field_profile_last_name')->value;
    // If the only field we're allowed to use is empty then we remove the
    // suggestion altogether and fall back to something else.
    if (empty($last_name)) {
      unset($suggestions['full_name']);
    }
    // Otherwise we use it.
    else {
      $suggestions['full_name']['name'] = $last_name;
    }
  }
}

/**
 * Implements hook_ENTITY_TYPE_view_alter().
 */
function social_profile_fields_profile_view_alter(array &$build, EntityInterface $entity, EntityViewDisplay $display) {
  // Check if profile image is unset.
  if (!_social_profile_fields_get_setting('profile_profile_field_profile_image') && isset($display->get('content')['field_profile_image'])) {
    // Load default data.
    $replacement_data = social_profile_get_default_image();

    /** @var \Drupal\image\Plugin\Field\FieldType\ImageItem $image */
    $image = $build['field_profile_image'][0]['#item'];

    // Time to override the data that going to be rendered.
    $image->set('target_id', $replacement_data['id']);
    $image->set('width', $replacement_data['width']);
    $image->set('height', $replacement_data['height']);

    // Put replacement data back in the object that's about to be built.
    $build['field_profile_image'][0]['#item'] = $image;
  }
}

/**
 * Get the value for the key from the settings.
 *
 * @param string $key
 *   The field name to check.
 *
 * @return bool
 *   If the field is enabled or disabled.
 */
function _social_profile_fields_get_setting($key) {
  $config = \Drupal::config('social_profile_fields.settings');
  $setting_value = $config->get($key);

  if (!isset($setting_value) || $setting_value == TRUE) {
    return TRUE;
  }
  return FALSE;
}

/**
 * Remove user export plugins for fields which are disabled.
 *
 * @param array $plugins
 *   An array of all the existing plugin definitions, passed by reference.
 *
 * @see \Drupal\social_user_export\UserExportPluginManager
 */
function social_profile_fields_social_user_export_plugin_info_alter(array &$plugins) {
  /** @var \Drupal\profile\Entity\ProfileType $profile_type */
  foreach (ProfileType::loadMultiple() as $profile_type) {
    $type = $profile_type->id();

    $profile_fields = \Drupal::service('social_profile_fields.helper')->getProfileFields($type);
    $filtered = array_filter($profile_fields, function ($arr) {
      return $arr['id'] === 'profile_profile_field_profile_address';
    });
    if (!empty($filtered) || array_key_exists('profile_profile_field_profile_address', $profile_fields)) {
      $profile_fields[] = ['id' => 'profile_address_field_city'];
      $profile_fields[] = ['id' => 'profile_address_field_address'];
      $profile_fields[] = ['id' => 'profile_address_field_postalcode'];
      $profile_fields[] = ['id' => 'profile_address_field_administrative_area'];
    }

    foreach ($profile_fields as $field) {
      $config = \Drupal::config('social_profile_fields.settings');

      $setting_value = $config->get($field['id']);
      if (isset($setting_value) && !$setting_value) {
        $plugin_ids_for_fields = \Drupal::service('social_profile_fields.helper')->getUserExportPluginIdForField($field['id']);
        if (!empty($plugin_ids_for_fields)) {
          foreach ($plugin_ids_for_fields as $plugin_id) {
            if ($plugins[$plugin_id]) {
              unset($plugins[$plugin_id]);
            }
          }
        }
      }
    }
  }
}

/**
 * Implements hook_entity_bundle_field_info_alter().
 */
function social_profile_fields_entity_bundle_field_info_alter(&$fields, EntityTypeInterface $entity_type, $bundle) {
  if ($bundle === 'profile' && $entity_type->id() === 'profile') {
    $config = \Drupal::config('social_profile_fields.settings');

    // Add the unique nickname constraint.
    if (isset($fields['field_profile_nick_name']) && $config->get('nickname_unique_validation')) {
      $fields['field_profile_nick_name']->addConstraint('UniqueNickname', []);
    }

    // Change address fields override settings.
    if (isset($fields['field_profile_address'])) {
      $field_profile_address = $fields['field_profile_address'];

      if ($setting = $field_profile_address->getSetting('fields')) {
        $setting['locality'] = !$config->get('profile_address_field_city') ? '0' : 'locality';
        $setting['addressLine1'] = !$config->get('profile_address_field_address') ? '0' : 'addressLine1';
        $setting['postalCode'] = !$config->get('profile_address_field_postalcode') ? '0' : 'postalCode';
        $setting['administrativeArea'] = !$config->get('profile_address_field_administrative_area') ? '0' : 'administrativeArea';

        // Update the settings of address field.
        $fields['field_profile_address']->setSetting('fields', $setting);
      }
      elseif ($setting = $field_profile_address->getSetting('field_overrides')) {
        if (!$config->get('profile_address_field_city')) {
          $setting['locality'] = ['override' => 'hidden'];
        }
        else {
          unset($setting['locality']);
        }

        if (!$config->get('profile_address_field_address')) {
          $setting['addressLine1'] = ['override' => 'hidden'];
        }
        else {
          unset($setting['addressLine1']);
        }

        if (!$config->get('profile_address_field_postalcode')) {
          $setting['postalCode'] = ['override' => 'hidden'];
        }
        else {
          unset($setting['postalCode']);
        }

        if (!$config->get('profile_address_field_administrative_area')) {
          $setting['administrativeArea'] = ['override' => 'hidden'];
        }
        else {
          unset($setting['administrativeArea']);
        }

        // Update the settings of address field.
        $fields['field_profile_address']->setSetting('field_overrides', $setting);
      }
    }
  }
}

/**
 * Implements hook_preprocess_field().
 */
function social_profile_fields_preprocess_field(&$variables) {
  $formatter = $variables['element']['#formatter'];
  if (in_array($formatter, ['address_plain', 'address_default'])) {
    $entity = $variables['element']['#object'];
    if ($entity && $entity instanceof ProfileInterface && $entity->bundle() === 'profile') {
      $config = \Drupal::config('social_profile_fields.settings');
      switch ($formatter) {
        case 'address_plain':
          if (isset($variables['items'][0]['content']['#locality']) && !$config->get('profile_address_field_city')) {
            $variables['items'][0]['content']['#locality'] = NULL;
          }
          if (isset($variables['items'][0]['content']['#address_line1']) && !$config->get('profile_address_field_address')) {
            $variables['items'][0]['content']['#address_line1'] = NULL;
          }
          if (isset($variables['items'][0]['content']['#postal_code']) && !$config->get('profile_address_field_postalcode')) {
            $variables['items'][0]['content']['#postal_code'] = NULL;
          }
          if (isset($variables['items'][0]['content']['#administrative_area']) && !$config->get('profile_address_field_administrative_area')) {
            $variables['items'][0]['content']['#administrative_area'] = NULL;
          }
          break;

        case 'address_default':
          if (isset($variables['items'][0]['content']['locality']['#value']) && !$config->get('profile_address_field_city')) {
            $variables['items'][0]['content']['locality']['#value'] = '';
          }
          if (isset($variables['items'][0]['content']['address_line1']['#value']) && !$config->get('profile_address_field_address')) {
            $variables['items'][0]['content']['address_line1']['#value'] = '';
          }
          if (isset($variables['items'][0]['content']['postal_code']['#value']) && !$config->get('profile_address_field_postalcode')) {
            $variables['items'][0]['content']['postal_code']['#value'] = '';
          }
          if (isset($variables['items'][0]['content']['administrative_area']['#value']) && !$config->get('profile_address_field_administrative_area')) {
            $variables['items'][0]['content']['administrative_area']['#value'] = '';
          }
          break;
      }
    }
  }
}
