<?php
// https://www.drupal.org/docs/8/modules/search-api/developer-documentation/executing-a-search-in-code

namespace Drupal\materio_sapi\Controller;

use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Drupal\Component\Utility\Tags;
use Drupal\Component\Utility\Unicode;
use Drupal\search_api\Entity\Index;

// https://drupal.stackexchange.com/questions/225008/programatically-use-search-api

/**
 * Defines a route controller for materio sapi search (regular and ajax).
 */
class Base extends ControllerBase {



  private $limit = 15;
  private $offset = 0;

  private function sapiQuery(){
    // https://www.drupal.org/docs/8/modules/search-api/developer-documentation/executing-a-search-in-code
    // https://www.hashbangcode.com/article/drupal-8-date-search-boosting-search-api-and-solr-search
    // https://kgaut.net/blog/2018/drupal-8-search-api-effectuer-une-requete-dans-le-code.html

    $this->index = Index::load('database');

    // // get solarium fileds name
    // $solrFields = $this->index->getServerInstance()
    //   ->getBackend()
    //   ->getSolrFieldNames($this->index);
    //
    // // tag_tid"itm_tag_tid"
    // // thesaurus_tid"itm_thesaurus_tid"
    // $taxoSolrFieldsName = [];
    // foreach (['tag_tid', 'thesaurus_tid'] as $f) {
    //   $taxoSolrFieldsName[] = $solrFields[$f];
    // }

    $this->query = $this->index->query();

    // Change the parse mode for the search.
    // Les différentes possibilités sont
    //  - « direct » => Requête directe
    //  - « terms » => Multiple words
    //  - « phrase » => Single phrase
    //  - " edismax " => ???
    $parse_mode = \Drupal::service('plugin.manager.search_api.parse_mode')
      ->createInstance('direct');
    $parse_mode->setConjunction('OR');
    $this->query->setParseMode($parse_mode);

    // Set fulltext search keywords and fields.
    $this->query->keys($this->keys);
    // $this->query->setFulltextFields(['field_reference']);

    // Set additional conditions.
    // in case we search for material references like W0117
    if (preg_match_all('/[WTRPCMFGSO]\d{4}/i', $this->keys, $matches)) {
      $ref_conditions = $this->query->createConditionGroup('OR');
      foreach ($matches[0] as $key => $value) {
        $ref_conditions->addCondition('field_reference', $value);
      }
      $this->query->addConditionGroup($ref_conditions);
    }
    // in case of term id provided restrict the keys to taxo fields
    if ($this->term) {
      // $term_conditions = $this->query->createConditionGroup('OR');
      // $term = (int) $this->term;
      // foreach (['tag_tid', 'thesaurus_tid'] as $field) {
      //   $term_conditions->addCondition($field, $term);
      // }
      // $this->query->addConditionGroup($term_conditions);

      // INSTEAD TRY TO BOOST TTHE TAG AND THESAURUS FIELDS
      // foreach ($taxoSolrFieldsName as $fname) {
      //   // $solarium_query->addParam('bf', "recip(abs(ms(NOW,{$solrField})),3.16e-11,10,0.1)");
      //   $bfparam = "if(gt(termfreq({$fname},{$this->term}),0),^21,0)";
      //   $this->query->addParam('bf', $bfparam);
      // }

      $this->query->setOption('termid', $this->term);
    }

    // filter the search
    if ($this->filters) {
      $filters_conditions = $this->query->createConditionGroup('OR');
      foreach ($this->filters as $filter) {
        $filter = (int) $filter;
        foreach (['tag_tid', 'thesaurus_tid'] as $field) {
          $filters_conditions->addCondition($field, $filter);
        }
      }
      $this->query->addConditionGroup($filters_conditions);
    }

    // Restrict the search to specific languages.
    $lang = \Drupal::languageManager()->getCurrentLanguage()->getId();
    $this->query->setLanguages([$lang]);

    // Do paging.
    $this->query->range($this->offset, $this->limit);

    // Add sorting.
    $this->query->sort('search_api_relevance', 'DESC');

    // Set one or more tags for the query.
    // @see hook_search_api_query_TAG_alter()
    // @see hook_search_api_results_TAG_alter()
    $this->query->addTag('materio_sapi_search');

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

  }

  private function defaultQuery(){
    $lang = \Drupal::languageManager()->getCurrentLanguage()->getId();
    $entity_storage = \Drupal::entityTypeManager()->getStorage('node');
    $this->query = $entity_storage->getQuery()
      ->condition('type', ['materiau', 'thematique'], 'IN')
      ->condition('status', '1')
      ->condition('langcode', $lang)
      ->range($this->offset, $this->limit)
      ->accessCheck(TRUE)
      ->sort('changed', 'DESC');
      // ->condition('field_example', 'test_value')
    $this->results = $this->query->execute();

    $this->count_query = $entity_storage->getQuery()
      ->condition('type', ['materiau', 'thematique'], 'IN')
      ->condition('langcode', $lang)
      ->accessCheck(TRUE)
      ->condition('status', '1')
      ->count();
    $this->count = $this->count_query->execute();
  }
  /**
   * get params from request
   */
  private function parseRequest(Request $request){
    // Get the typed string from the URL, if it exists.
    $this->keys = $request->query->get('keys');
    if($this->keys){
      $this->keys = Unicode::strtolower($this->keys);
      // $this->keys = Tags::explode($this->keys);
      \Drupal::logger('materio_sapi')->notice($this->keys);
    }
    // get the exacte term id in case of autocomplete
    $this->term = $request->query->get('term');
    // get the filters of advanced search
    $f = $request->query->get('filters');
    $this->filters = strlen($f) ? explode(',', $f) : null;
    // $this->allparams = $request->query->all();
    // $request->attributes->get('_raw_variables')->get('filters')
    //
    $this->offset = $request->query->get('offset') ?? $this->offset;
    $this->limit = $request->query->get('limit') ?? $this->limit;
  }

  /**
   * Handler for ajax search.
   */
  public function getResults(Request $request) {
    $this->parseRequest($request);

    $resp = [
      'range' => array(
        'offset' => $this->offset,
        'limit' => $this->limit
      ),
    ];

    if ($this->keys) {
      $this->sapiQuery();

      $resp['keys'] = $this->keys;
      $resp['term'] = $this->term;
      $resp['count'] = $this->results->getResultCount();
      $resp['infos'] = t('The search found @count result(s) with keywords @keys.', array(
        "@count" => $resp['count'],
        "@keys" => $this->keys
      ));


      $resp['options'] = $this->query->getOptions();

      // $items = [];
      $uuids = [];
      $nids = [];
      foreach ($this->results as $result) {
        // $nid = $result->getField('nid')->getValues()[0];
        // $uuid = $result->getField('uuid')->getValues()[0];
        // $title = $result->getField('title')->getValues()[0]->getText();
        // $items[] = [
        //   'nid' => $nid,
        //   'uuid' => $uuid,
        //   'title' => $title,
        // ];
        $uuids[] = $result->getField('uuid')->getValues()[0];
        $nids[] = $result->getField('nid')->getValues()[0];
      }
      // $resp['items'] = $items;
      $resp['uuids'] = $uuids;
      $resp['nids'] = $nids;
    } else {
      // no keys or terms to search for
      // display the default base page
      $this->defaultQuery();
      // $uuids = [];
      $nids = [];
      // Using entityTypeManager
      // Get a node storage object.
      // $node_storage = \Drupal::entityTypeManager()->getStorage('node');

      foreach ($this->results as $result) {
        // $lang = \Drupal::languageManager()->getCurrentLanguage()->getId();
        // Load a single node.
        // $node = $node_storage->load($result);
        // check if has translation
        // i used to filter like bellow because of a graphql probleme
        // if ($node->hasTranslation($lang)) {
          $nids[] = $result;
        // }

      }
      // $resp['uuids'] = $uuids;
      $resp['nids'] = $nids;
      $resp['count'] = $this->count;
      $resp['infos'] = t('Please use the search form to search from our @count materials.', array(
        "@count" => $resp['count']
      ));
    }

    return new JsonResponse($resp);
  }


  /**
   * Handler for regular page search.
   */
  function pageHandler(Request $request){
    // \Drupal::logger('materio_sapi')->notice(print_r($request, true));

    $this->parseRequest($request);

    $resp = [
      "#title" => 'Base'
    ];

    if ($this->keys) {
      $resp['#title'] = $this->keys;

      $this->sapiQuery();

      $node_view_builder = \Drupal::entityTypeManager()->getViewBuilder('node');

      $items = $this->results->getResultItems();
      $this->items = [];
      foreach ($items as $item) {
        // \Drupal::logger('materio_sapi')->notice(print_r($item, true));
        try {
          /** @var \Drupal\Core\Entity\EntityInterface $entity */
          $entity = $item->getOriginalObject()->getValue();
        }
        catch (SearchApiException $e) {
          continue;
        }
        if (!$entity) {
          continue;
        }
        // TODO: define dynamicly viewmode
        $this->items[] = $node_view_builder->view($entity, 'teaser');

      }

      $resp['items'] = $this->items;
    }else{
      $resp['#markup'] = t("no keys to search for");
    }

    return $resp;
  }
}
