<?php

/**
 * @file
 * Invokes the meta icons hook hook_rufi_meta_ENTITY_TYPE_ID_BUNDLE().
 */

use Drupal\comment\CommentFieldItemList;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\node\NodeInterface;
use Drupal\views\Views;

/**
 * Get meta properties of current entity.
 *
 * @param \Drupal\Core\Entity\EntityInterface $entity
 *   The entity to work on.
 * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display
 *   The entity view display.
 *
 * @return array
 *   A renderable chip_set for requested entity.
 */
function rufi_structure_meta_properties_load(EntityInterface $entity, EntityViewDisplayInterface $display): array {
  $callbackName = implode('_', [
    'rufi_meta',
    $entity->getEntityTypeId(),
    $entity->bundle(),
  ]);
  // Build cache tag and validate before hooking.
  $meta_props = [];
  // Invoke hooks for this entity (depends on type and bundle).
  $invoke_meta = Drupal::moduleHandler()->invokeAll($callbackName, [
    $entity,
    $display->getOriginalMode(),
  ]);
  if (count($invoke_meta)) {
    $meta_props['#theme'] = 'rufi_chip_set';
    foreach ($invoke_meta as $key => $meta) {
      $meta_props[$key] = $meta;
    }
  }

  // Arguments should not be displayed. No call to action included.
  // @todo remove hook from arguments.
  if ($meta_props['rufi_pro_con'] ?? NULL) {
    unset($meta_props['rufi_pro_con']);
  }
  return $meta_props;
}

/**
 * Implements hook_rufi_meta_ENTITY_TYPE().
 *
 * Get chips for nodes of bundle 'rule'.
 */
function rufi_structure_rufi_meta_node_rule(NodeInterface $node, $view_mode) :array {
  $result = [];
  if ($node->hasField('field_ratified')) {
    $value = $node->get('field_ratified')->getString();
    if (!$value) {
      $result['ratified'] = [
        '#theme' => 'rufi_chip',
        '#attributes' => ['title' => t('Rule will be ratified in next version.')],
        '#icon_before' => 'ratified',
        '#text' => t('No'),
        '#icon_after' => NULL,
        '#cache' => ['tags' => ["node:{$node->id()}:meta:ratified"]],
      ];
    }
  }
  if ($node->hasField('field_causal_problems') && $node->get('field_causal_problems')->isEmpty()) {
    $result['problems'] = [
      '#theme' => 'rufi_chip',
      '#help' => t('Number of problems this rule refers to.'),
      '#attributes' => ['title' => t('Number of problems this rule refers to.')],
      '#icon_before' => 'problem',
      '#text' => "0",
      '#icon_after' => NULL,
      '#cache' => ['tags' => ["node:{$node->id()}:meta:problems"]],
    ];
  }
  _rufi_structure_rufi_meta_change_requests($result, $node, $view_mode);
  return $result;
}

/**
 * Implements hook_rufi_meta_ENTITY_TYPE_BUNDLE().
 *
 * Get chips for nodes of bundle 'problem'.
 */
function rufi_structure_rufi_meta_node_problem(NodeInterface $node, $view_mode) :array {
  $nid = $node->id();
  $cache_tag = "node:{$node->id()}:meta:num_rule";
  $result = [];

  $view = Views::getView('rufi_rules_ref_this_problem');
  if (is_object($view)) {
    $view->setArguments([$nid]);
    $view->setDisplay('embed_1');
    $view->preExecute();
    $view->execute();

    if (!count($view->result)) {
      $result['rules'] = [
        '#theme' => 'rufi_chip',
        '#help' => t('Number of rules handling this problem'),
        '#attributes' => ['title' => t('Number of rules handling this problem.')],
        '#icon_before' => 'rule',
        '#text' => count($view->result),
        '#icon_after' => NULL,
        '#cache' => ['tags' => [$cache_tag]],
      ];
    }
  }
  _rufi_structure_rufi_meta_change_requests($result, $node, $view_mode);
  return array_merge($result, _rufi_structure_meta_props_from_reference_fields($node));
}

/**
 * Append number of change requests if exists.
 *
 * @param $result
 * @param NodeInterface $node
 * @param $view_mode
 *
 * @return void
 */
function _rufi_structure_rufi_meta_change_requests(&$result, NodeInterface $node, $view_mode): void
{
  if ($node->hasField('cr_count')) {
    $value = (int) $node->get('cr_count')->getString();
    if ($value > 0) {
      $result['cr_count'] = [
        '#theme' => 'rufi_chip',
        '#attributes' => ['title' => t('Rule has active or conflicted change requests.')],
        '#icon_before' => 'change_request',
        '#text' => $value > 9 ? '9+' : $value,
        '#icon_after' => NULL,
        '#cache' => ['tags' => ["node:{$node->id()}:meta:cr_count"]],
      ];
    }
  }

}

/**
 * Implements hook_rufi_meta_ENTITY_TYPE_BUNDLE().
 *
 * Get chips for nodes of bundle 'version'.
 */
function rufi_structure_rufi_meta_node_version(NodeInterface $node, $view_mode) :array {
  return _rufi_structure_meta_props_from_reference_fields($node);
}

/**
 * Implements hook_rufi_meta_ENTITY_TYPE_BUNDLE().
 *
 * Get chips for arguments.
 */
function rufi_structure_rufi_meta_argument_argument(EntityInterface $entity, $view_mode): array {
  return _rufi_structure_meta_props_from_reference_fields($entity);
}

/**
 * Simplifies the building of chips from reference fields.
 *
 * @param \Drupal\Core\Entity\EntityInterface $entity
 *   The entity to work on.
 *
 * @return array
 *   Returns a renderabble array of chips.
 *
 * @throws \Drupal\Core\TypedData\Exception\MissingDataException
 */
function _rufi_structure_meta_props_from_reference_fields(EntityInterface $entity) :array {
  $id = $entity->id();
  $entity_type = $entity->getEntityTypeId();
  $result = [];

  // Icon map used for chips. If not set, the entity type id is used.
  $chip_icon_map = [
    'patch' => 'change_request',
    'argument' => 'rufi_pro_con',
  ];

  /**
   * Loop over fields and append reference (revisions) fields.
   *
   * @var string $field_name
   * @var Drupal\Core\Field\FieldDefinitionInterface $field_definition
   */
  foreach ($entity->getFieldDefinitions() as $field_name => $field_definition) {
    $types = ['entity_reference', 'entity_reference_revisions'];
    $storage = $field_definition->getFieldStorageDefinition();
    if (!$storage->isBaseField() && in_array($field_definition->getType(), $types)) {
      $target_type = $field_definition->getItemDefinition()->getSetting('target_type');
      if ($target_type == 'node') {
        $handler_settings = $field_definition->getItemDefinition()->getSetting('handler_settings');
        $target_type = isset($handler_settings['target_bundles']) && count($handler_settings['target_bundles'])
          ? reset($handler_settings['target_bundles'])
          : $target_type;
      }
      $cache_tag = "{$entity_type}:{$id}:meta:{$target_type}";
      if ($storage->isMultiple()) {
        $items_count = (int) $entity->get($field_name)->count();
        if ($items_count > 0) {
          $result[$target_type] = [
            '#theme' => 'rufi_chip',
            '#attributes' => [
              'class' => ["rufi-chip--{$target_type}"],
              'title' => t('Number of %type on this entity.', [
                '%type' => $field_definition->getLabel(),
              ]),
            ],
            '#icon_before' => $chip_icon_map[$target_type] ?? $target_type,
            '#text' => $items_count,
            '#cache' => ['tags' => [$cache_tag]],
          ];
        }
      }
    }
    elseif ($field_definition->getType() == 'comment') {
      $cache_tag = "{$entity_type}:{$id}:meta:comment";
      $field_comment = $entity->get($field_name);
      if ($field_comment instanceof CommentFieldItemList) {
        $items_count = (int) $field_comment->first()->comment_count;
        if ($items_count > 0) {
          $result['comment'] = [
            '#theme' => 'rufi_chip',
            '#attributes' => [
              'class' => ['rufi-chip--comment'],
              'title' => t('Number of comments on this entity.'),
            ],
            '#icon_before' => 'comments',
            '#text' => $items_count,
            '#cache' => ['tags' => [$cache_tag]],
          ];
        }
      }
    }
  }

  return $result;
}
