<?php
/*
**** COPYRIGHT & LICENSE NOTICE *** DO NOT REMOVE ****
* 
* Xentral (c) Xentral ERP Sorftware GmbH, Fuggerstrasse 11, D-86150 Augsburg, * Germany 2019
*
* This file is licensed under the Embedded Projects General Public License *Version 3.1. 
*
* You should have received a copy of this license from your vendor and/or *along with this file; If not, please visit www.wawision.de/Lizenzhinweis 
* to obtain the text of the corresponding license version.  
*
**** END OF COPYRIGHT & LICENSE NOTICE *** DO NOT REMOVE ****
*/
?>
<?php

use Xentral\Components\Http\JsonResponse;
use Xentral\Modules\SuperSearch\Scheduler\SuperSearchFullIndexTask;
use Xentral\Modules\SuperSearch\SearchIndex\Data\IndexIdentifier;
use Xentral\Modules\SuperSearch\SuperSearchEngine;
use Xentral\Modules\SuperSearch\SuperSearchIndexer;
use Xentral\Modules\SuperSearch\SuperSearchService;
use Xentral\Widgets\SuperSearch\Query\DetailQuery;
use Xentral\Widgets\SuperSearch\Result\ResultDetail;

class SuperSearch
{
  /** @var string MODULE_NAME */
  const MODULE_NAME = 'SuperSearch';

  /** @var ApplicationCore $app */
  public $app;

  /** @var array $javascript */
  public $javascript = [
    './classes/Modules/SuperSearch/www/js/supersearch_ui.js',
  ];

  /**
   * @param ApplicationCore $app
   * @param bool            $intern
   */
  public function __construct($app, $intern = false)
  {
    $this->app = $app;

    if($intern !== false){
      return;
    }

    $this->app->ActionHandlerInit($this);
    $this->app->DefaultActionHandler('settings');
    $this->app->ActionHandler('settings', 'SuperSearchSettings');
    $this->app->ActionHandler('ajax', 'SuperSearchAjax');
    $this->app->ActionHandlerListen($app);
  }

  /**
   * @return void
   */
  public function Install()
  {
    $this->app->erp->CheckTable('supersearch_index_item');
    $this->app->erp->CheckColumn('id', 'INT(10) UNSIGNED', 'supersearch_index_item', 'NOT NULL AUTO_INCREMENT');
    $this->app->erp->CheckColumn('index_name', 'VARCHAR(16)', 'supersearch_index_item', 'NOT NULL');
    $this->app->erp->CheckColumn('index_id', 'VARCHAR(38)', 'supersearch_index_item', 'NOT NULL');
    $this->app->erp->CheckColumn('project_id', 'INT(10) UNSIGNED', 'supersearch_index_item', 'NOT NULL DEFAULT 0');
    $this->app->erp->CheckColumn('title', 'VARCHAR(128)', 'supersearch_index_item', 'NOT NULL');
    $this->app->erp->CheckColumn('subtitle', 'VARCHAR(128)', 'supersearch_index_item', 'NULL DEFAULT NULL');
    $this->app->erp->CheckColumn('additional_infos', 'VARCHAR(255)', 'supersearch_index_item', 'NULL DEFAULT NULL');
    $this->app->erp->CheckColumn('link', 'VARCHAR(128)', 'supersearch_index_item', 'NOT NULL');
    $this->app->erp->CheckColumn('search_words', 'TEXT', 'supersearch_index_item', "NOT NULL DEFAULT ''");
    $this->app->erp->CheckColumn('outdated', 'TINYINT(1) UNSIGNED', 'supersearch_index_item', "NOT NULL DEFAULT '0'");
    $this->app->erp->CheckColumn('created_at', 'TIMESTAMP', 'supersearch_index_item', 'NOT NULL DEFAULT CURRENT_TIMESTAMP');
    $this->app->erp->CheckColumn('updated_at', 'TIMESTAMP', 'supersearch_index_item', 'NULL DEFAULT NULL');
    $this->app->erp->CheckIndex('supersearch_index_item', 'project_id');
    $this->app->erp->CheckFulltextIndex('supersearch_index_item', 'search_words');
    $this->app->erp->CheckAlterTable('ALTER TABLE `supersearch_index_item` ADD UNIQUE KEY `index_identifier` (`index_name`, `index_id`)');

    $this->app->erp->CheckTable('supersearch_index_group');
    $this->app->erp->CheckColumn('id', 'INT(10) UNSIGNED', 'supersearch_index_group', 'NOT NULL AUTO_INCREMENT');
    $this->app->erp->CheckColumn('name', 'VARCHAR(16)', 'supersearch_index_group', 'NOT NULL');
    $this->app->erp->CheckColumn('title', 'VARCHAR(32)', 'supersearch_index_group', 'NOT NULL');
    $this->app->erp->CheckColumn('module', 'VARCHAR(38)', 'supersearch_index_group', 'NULL DEFAULT NULL');
    $this->app->erp->CheckColumn('active', 'TINYINT(1) UNSIGNED', 'supersearch_index_group', "NOT NULL DEFAULT '1'");
    $this->app->erp->CheckColumn('last_full_update', 'TIMESTAMP', 'supersearch_index_group', 'NULL DEFAULT NULL');
    $this->app->erp->CheckColumn('last_diff_update', 'TIMESTAMP', 'supersearch_index_group', 'NULL DEFAULT NULL');
    $this->app->erp->CheckAlterTable('ALTER TABLE `supersearch_index_group` ADD UNIQUE KEY `name` (`name`)');

    $this->app->erp->CheckProzessstarter('SuperSearch Index-Full', 'uhrzeit', '', '2017-01-01 02:30:00', 'cronjob', 'supersearch_index_full', 1);
    $this->app->erp->CheckProzessstarter('SuperSearch Index-Diff', 'periodisch', '3600', '', 'cronjob', 'supersearch_index_diff', 1);

    $this->app->erp->RegisterHook('article_delete', 'supersearch', 'SuperSearchOnArticleDelete');
    $this->app->erp->RegisterHook('address_delete', 'supersearch', 'SuperSearchOnAddressDelete');
  }

  /**
   * @param int $articleId
   */
  public function SuperSearchOnArticleDelete($articleId)
  {
    /** @var SuperSearchIndexer $indexer */
    $indexer = $this->app->Container->get('SuperSearchIndexer');
    $identifier = new IndexIdentifier('articles', (int)$articleId);
    $indexer->deleteIndexItem($identifier);
  }

  /**
   * @param int $addressId
   */
  public function SuperSearchOnAddressDelete($addressId)
  {
    /** @var SuperSearchIndexer $indexer */
    $indexer = $this->app->Container->get('SuperSearchIndexer');
    $identifier = new IndexIdentifier('addresses', (int)$addressId);
    $indexer->deleteIndexItem($identifier);
  }

  /**
   * @return void
   */
  public function SuperSearchMenu()
  {
    $this->app->erp->MenuEintrag('index.php?module=supersearch&action=settings', '&Uuml;bersicht');
  }

  /**
   * @return void
   */
  public function SuperSearchSettings()
  {
    $cmd = $this->app->Secure->GetPOST('cmd');
    switch ($cmd) {

      case 'run-full-index-task':
        // Full-Index-Task ausführen
        $response = $this->HandleSuperSearchSettingsFullIndexTask();
        $response->send();
        $this->app->ExitXentral();
        break;

      case 'activate-provider':
        $response = $this->HandleSuperSearchSettingsActivateProvider();
        $response->send();
        $this->app->ExitXentral();
        break;

      case 'deactivate-provider':
        $response = $this->HandleSuperSearchSettingsDeactivateProvider();
        $response->send();
        $this->app->ExitXentral();
        break;
    }

    // Prüfen ob beide Cronjobs aktiv sind
    $this->app->erp->checkActiveCronjob('supersearch_index_full');
    $this->app->erp->checkActiveCronjob('supersearch_index_diff');

    /** @var SuperSearchService $searchService */
    $searchService = $this->app->Container->get('SuperSearchService');
    if ($searchService->isIndexEmpty()) {
      $searchIndexEmptyMessage = 'Der Such-Index ist leer. Die Einstellungen stehen erst zur Verfügung wenn der Such-Index einmalig gefüllt wurde.';
      $this->app->Tpl->Add('MESSAGE', sprintf('<div class="info">%s</div>', $searchIndexEmptyMessage));
    }

    // Tabelle mit Index-Statistik zusammenbauen
    $table = '<table class="mkTable">';
    $table .= '<tr><th align="left">Index</th>';
    $table .= '<th>Index-Größe (aktuell)</th>';
    $table .= '<th>Index-Größe (potentiell)</th>';
    $table .= '<th>Letzes Index-Update</th>';
    $table .= '<th>Provider-Status</th>';
    $table .= '<th>Aktionen</th>';
    $table .= '</tr>';
    $stats = $searchService->getIndexStats();
    foreach ($stats as $info) {
      $lastUpdate = $this->getGreatestDateTime($info['last_full_update'], $info['last_diff_update']);
      $table .= '<tr>';
      $table .= '<td><label>' . $info['title'] . ' (' . $info['name'] .')</label></td>';
      $table .= '<td>' . ($info['index_size_current'] !== null ? $info['index_size_current'] : '<em>Unbekannt</em>') . '</td>';
      $table .= '<td>' . ($info['index_size_potential'] !== null ? $info['index_size_potential'] : '<em>Unbekannt</em>') . '</td>';
      $table .= '<td>' . ($lastUpdate !== null ? $lastUpdate->format('d.m.Y H:i:s') : '<em>NULL</em>') . '</td>';
      $table .= '<td>';
      if ($info['active'] === true) { $table .= 'Aktiv'; }
      if ($info['active'] === false) { $table .= 'Inaktiv'; }
      if ($info['active'] === null) { $table .= '<em>Unbekannt</em>'; }
      $table .= '</td>';
      $buttonTemplate = '<td><button type="button" data-index-name="%s" class="button button-secondary %s">%s</button></td>';
      if ($info['active'] === true){
        $table .= sprintf($buttonTemplate, $info['name'], 'button-provider-deactivate', 'Such-Index deaktivieren');
      }
      if ($info['active'] === false) {
        $table .= sprintf($buttonTemplate, $info['name'], 'button-provider-activate', 'Such-Index aktivieren');
      }
      if ($info['active'] === null) {
        $table .= '<td></td>';
      }
      $table .= '</tr>';
    }
    $table .= '</table>';
    // ENDE Tabelle mit Index-Statistik zusammenbauen

    $this->SuperSearchMenu();
    $this->app->Tpl->Set('UEBERSCHRIFT', 'SuperSearch');
    $this->app->Tpl->Set('KURZUEBERSCHRIFT', 'SuperSearch');
    $this->app->Tpl->Set('TABTEXT', 'SuperSearch');

    $this->app->Tpl->Set('INDEXSTATSTABLE', $table);
    $this->app->Tpl->Parse('PAGE', 'supersearch_settings.tpl');
  }

  /**
   * @return void
   */
  public function SuperSearchAjax()
  {
    $cmd = $this->app->Secure->GetGET('cmd');
    switch ($cmd) {

      case 'search':
        // Suchergebnisse ermitteln
        $response = $this->HandleSuperSearchAjaxSearch();
        break;

      case 'detail':
        // Detail-Informationen nachladen: Bei Klick auf ein einzelnes Ergebnis
        $response = $this->HandleSuperSearchAjaxDetail();
        break;
    }

    if (!isset($response)) {
      $response = new JsonResponse(
        ['success' => false, 'error' => 'Parameter \'cmd\' ungültig.'],
        JsonResponse::HTTP_NOT_FOUND
      );
    }

    // Ausgabe
    $response->send();
    $this->app->erp->ExitWawi();
  }

  /**
   * @return JsonResponse
   */
  protected function HandleSuperSearchSettingsFullIndexTask()
  {
    /** @var SuperSearchFullIndexTask $indexTask */
    $indexTask = $this->app->Container->get('SuperSearchFullIndexTask');

    try {
      $indexTask->execute();
      $indexTask->cleanup();
    } catch (Exception $exception) {
      $indexTask->cleanup();
      return new JsonResponse(
        ['success' => false, 'error' => $exception->getMessage()],
        JsonResponse::HTTP_INTERNAL_SERVER_ERROR
      );
    }

    return new JsonResponse(['success' => true]);
  }

  /**
   * @return JsonResponse
   */
  protected function HandleSuperSearchSettingsActivateProvider()
  {
    $indexName = $this->app->Secure->GetPOST('index_name');

    try {
      /** @var SuperSearchService $searchService */
      $searchService = $this->app->Container->get('SuperSearchService');
      $searchService->activateIndex($indexName);
    } catch (Exception $exception) {
      return new JsonResponse(
        ['success' => false, 'error' => $exception->getMessage()],
        JsonResponse::HTTP_INTERNAL_SERVER_ERROR
      );
    }

    return new JsonResponse(['success' => true]);
  }

  /**
   * @return JsonResponse
   */
  protected function HandleSuperSearchSettingsDeactivateProvider()
  {
    $indexName = $this->app->Secure->GetPOST('index_name');

    try {
      /** @var SuperSearchService $searchService */
      $searchService = $this->app->Container->get('SuperSearchService');
      $searchService->deactivateIndex($indexName);
    } catch (Exception $exception) {
      return new JsonResponse(
        ['success' => false, 'error' => $exception->getMessage()],
        JsonResponse::HTTP_INTERNAL_SERVER_ERROR
      );
    }

    return new JsonResponse(['success' => true]);
  }

  /**
   * @return JsonResponse
   */
  protected function HandleSuperSearchAjaxSearch()
  {
    $searchTerm = stripslashes($this->app->Secure->GetPOST('search_query'));
    if (strlen(trim($searchTerm)) >= 3) {

      /** @var SuperSearchEngine $searchEngine */
      $searchEngine = $this->app->Container->get('SuperSearchEngine');
      $projectIds = $this->app->User->GetType() !== 'admin' ? $this->app->User->getUserProjects() : null;
      $moduleNames = $this->app->User->GetType() !== 'admin' ? $this->app->erp->getUserModules() : null;
      $searchResult = $searchEngine->search($searchTerm, $projectIds, $moduleNames);

      // Ergebnis leer > Prüfen ob SuchIndex gefüllt
      if ($searchResult->isEmpty() && $searchResult->getLastIndexUpdateTime() === null) {
        return new JsonResponse(
          [
            'success' => false,
            'error' =>
              '<p>Such-Index ist nicht gefüllt. Bitte prüfen ob Prozessstarter aktiv sind.</p>'.
              '<a class="button button-secondary" href="index.php?module=supersearch&amp;action=settings">'.
              'Zu den SuperSearch-Einstellungen'.
              '</a>',
            'data' => 'index-empty',
          ],
          JsonResponse::HTTP_INTERNAL_SERVER_ERROR
        );
      }
    }


    return new JsonResponse(['success' => true, 'data' => $searchResult]);
  }

  /**
   * @return JsonResponse
   */
  protected function HandleSuperSearchAjaxDetail()
  {
    $detailGroup = $this->app->Secure->GetPOST('detail_group');
    $detailIdentifier = $this->app->Secure->GetPOST('detail_identifier');

    $detailQuery = new DetailQuery($detailGroup, $detailIdentifier);
    $detailResult = new ResultDetail();
    $this->app->erp->RunHook('supersearch_detail', 2, $detailQuery, $detailResult);
    return new JsonResponse(['success' => true, 'data' => $detailResult]);
  }

  /**
   * @param string|null $lastFullUpdate
   * @param string|null $lastDiffUpdate
   *
   * @return DateTimeImmutable|null
   */
  protected function getGreatestDateTime($lastFullUpdate = null, $lastDiffUpdate = null)
  {
    if (!empty($lastFullUpdate)){
      try {
        $lastFullUpdateTime = new \DateTimeImmutable($lastFullUpdate);
      } catch (Exception $exception) {
      }
    }
    if (!empty($lastDiffUpdate)) {
      try {
        $lastDiffUpdateTime = new \DateTimeImmutable($lastDiffUpdate);
      } catch (Exception $exception) {
      }
    }

    if (isset($lastFullUpdateTime) && !isset($lastDiffUpdateTime)) {
      return $lastFullUpdateTime;
    }
    if (!isset($lastFullUpdateTime) && isset($lastDiffUpdateTime)) {
      return $lastDiffUpdateTime;
    }
    if (isset($lastFullUpdateTime) && isset($lastDiffUpdateTime)) {
      if ($lastFullUpdateTime > $lastDiffUpdateTime) {
        return $lastFullUpdateTime;
      } else {
        return $lastDiffUpdateTime;
      }
    }

    return null;
  }
}