<?php

namespace Drupal\dkan_metastore_search;

use Drupal\search_api\Query\QueryInterface;
use RootedData\RootedJsonData;

/**
 * Trait used to generate facets from search API query conditions.
 *
 * @package Drupal\dkan_metastore_search
 */
trait FacetsFromContentTrait {
  use FacetsCommonTrait;

  /**
   * Private.
   *
   * @param array $params
   *   Array of search_api parameters.
   * @param \Drupal\search_api\Query\QueryInterface $query
   *   A search_api Query object.
   *
   * @return array
   *   Array of facets.
   */
  private function getFacetsFromContent(array $params, QueryInterface $query) : array {

    $results = $query->execute()->getResultItems();

    $datasets = array_map(function ($datasetId) {
      $cleanId = str_replace("dkan_dataset/", "", $datasetId);
      return $this->metastoreService->get('dataset', $cleanId);
    }, array_keys($results));

    $facetsTypes = $this->getFacetsTypes($params);

    $facets = $this->getAllFacetsWithZeroCounts($facetsTypes);

    $facetsKeys = array_reduce($datasets,
      function ($facetsKeys, $dataset) use ($facetsTypes) {
        $datasetFacetsKeys = $this->getDatasetFacetsKeys($dataset, $facetsTypes);
        return array_merge($facetsKeys, $datasetFacetsKeys);
      }, []);

    foreach ($facetsKeys as $key) {
      $facets[$key]->total++;
    }
    return array_values($facets);
  }

  /**
   * Private.
   *
   * A key is a unique identifier for a facet with the form
   * facet_type:facet_value.
   *
   * @param string $dataset
   *   A json string for a dataset.
   * @param array $facetsTypes
   *   The relevant facet types.
   *
   * @throws \JsonPath\InvalidJsonException
   */
  private function getDatasetFacetsKeys(string $dataset, array $facetsTypes) {
    $rooted = new RootedJsonData($dataset);

    // Map each facetType to an array of facetKeys of that type.
    $facetsKeysByType = array_map(function ($facetsType) use ($rooted) {
      $values = $this->getPropertyValuesFromFacetsType($rooted, $facetsType);
      return array_map(function ($value) use ($facetsType) {
        return "{$facetsType}:{$value}";
      }, $values);
    }, $facetsTypes);

    return array_reduce($facetsKeysByType, function ($carry, $facetsKeys) {
      return array_merge($carry, $facetsKeys);
    }, []);
  }

  /**
   * Private.
   *
   * Extracts values from a rooted json object by transforming a facet type
   * into a path to values.
   *
   * @param \RootedData\RootedJsonData $rooted
   *   RootedJson data object.
   * @param string $facetsType
   *   The facet type to transform to a path.
   *
   * @return array
   *   An array of values. It could be empty.
   */
  private function getPropertyValuesFromFacetsType(RootedJsonData $rooted, string $facetsType) {
    $property = str_replace('__', '.', $facetsType);
    $property = "$.{$property}";

    $values = $rooted->{$property};

    if (!isset($values)) {
      $values = [];
    }

    if (!is_array($values)) {
      $values = [$values];
    }

    return $values;
  }

  /**
   * Private.
   *
   * @param array $facetsTypes
   *   Relevant facet types.
   *
   * @return array
   *   An array with all facets for the given types represented.
   */
  private function getAllFacetsWithZeroCounts(array $facetsTypes) {
    $facets = [];
    foreach ($facetsTypes as $type) {

      [$schema, $field] = $this->getSchemaAndField($type);

      $allFacets = $this->metastoreService->getAll($schema);

      foreach ($allFacets as $facet) {

        $facet = isset($field) ? $facet->{"$.data." . $field} : $facet->{"$.data"};

        if (!empty($facet)) {
          $facets["{$type}:{$facet}"] = (object) [
            'type' => $type,
            'name' => $facet,
            'total' => 0,
          ];
        }
      }
    }

    return $facets;
  }

  /**
   * Private.
   *
   * @param string $facetsType
   *   The facet type.
   *
   * @return array
   *   [$schema, $field]
   */
  private function getSchemaAndField(string $facetsType): array {
    $field = NULL;
    if (preg_match('/(.*)__(.*)/', $facetsType, $matches)) {
      $schema = $matches[1];
      $field = $matches[2];
    }
    else {
      $schema = $facetsType;
    }
    return [$schema, $field];
  }

}
