<?php

namespace SMW\MediaWiki\Search;

use MediaWiki\Content\Content;
use MediaWiki\Title\Title;
use SearchEngine;

/**
 * Facade to the MediaWiki `SearchEngine` which doesn't allow any factory
 * or callable to construct an instance.
 *
 * @license GPL-2.0-or-later
 * @since   2.1
 *
 * @author  Stephan Gambke
 */
class ExtendedSearchEngine extends SearchEngine {

	/**
	 * @var ExtendedSearch
	 */
	private $extendedSearch;

	/**
	 * @var SearchEngine
	 */
	private $fallbackSearchEngine;

	/**
	 * @see SearchEngineFactory::create
	 *
	 * @since 3.1
	 */
	public function __construct( $connection = null ) {
		// It is common practice to avoid construction work in the constructor
		// but we are unable to define a factory or callable and this is the only
		// place to create an instance.
		$searchEngineFactory = new SearchEngineFactory();

		$this->fallbackSearchEngine = $searchEngineFactory->newFallbackSearchEngine(
			$connection
		);

		$this->extendedSearch = $searchEngineFactory->newExtendedSearch(
			$this->fallbackSearchEngine
		);

		$this->extendedSearch->setPrefix( $this->prefix );
		$this->extendedSearch->setNamespaces( $this->namespaces );
	}

	/**
	 * @since 3.1
	 *
	 * @param ExtendedSearch $extendedSearch
	 */
	public function setExtendedSearch( ExtendedSearch $extendedSearch ) {
		$this->extendedSearch = $extendedSearch;
	}

	/**
	 * @since 2.1
	 *
	 * @param null|SearchEngine $fallbackSearchEngine
	 */
	public function setFallbackSearchEngine( ?SearchEngine $fallbackSearchEngine = null ) {
		$this->fallbackSearchEngine = $fallbackSearchEngine;
	}

	/**
	 * @since 2.1
	 *
	 * @return SearchEngine
	 */
	public function getFallbackSearchEngine() {
		return $this->fallbackSearchEngine;
	}

	/**
	 * @see SearchEngine::getValidSorts
	 *
	 * {@inheritDoc}
	 */
	public function getValidSorts() {
		return $this->extendedSearch->getValidSorts();
	}

	/**
	 * @see SearchEngine::searchTitle
	 *
	 * {@inheritDoc}
	 */
	public function searchTitle( $term ) {
		$this->extendedSearch->setNamespaces(
			$this->namespaces
		);

		return $this->extendedSearch->searchTitle( $term );
	}

	/**
	 * @see SearchEngine::searchText
	 *
	 * {@inheritDoc}
	 */
	public function searchText( $term ) {
		$this->extendedSearch->setNamespaces(
			$this->namespaces
		);

		return $this->extendedSearch->searchText( $term );
	}

	/**
	 * @see SearchEngine::supports
	 *
	 * {@inheritDoc}
	 */
	public function supports( $feature ) {
		return $this->fallbackSearchEngine->supports( $feature );
	}

	/**
	 * @see SearchEngine::normalizeText
	 *
	 * {@inheritDoc}
	 */
	public function normalizeText( $string ) {
		return $this->fallbackSearchEngine->normalizeText( $string );
	}

	/**
	 * @see SearchEngine::getTextFromContent
	 *
	 * {@inheritDoc}
	 */
	public function getTextFromContent( Title $t, ?Content $c = null ) {
		return $this->fallbackSearchEngine->getTextFromContent( $t, $c );
	}

	/**
	 * @see SearchEngine::textAlreadyUpdatedForIndex
	 *
	 * {@inheritDoc}
	 */
	public function textAlreadyUpdatedForIndex() {
		return $this->fallbackSearchEngine->textAlreadyUpdatedForIndex();
	}

	/**
	 * @see SearchEngine::update
	 *
	 * {@inheritDoc}
	 */
	public function update( $id, $title, $text ) {
		$this->fallbackSearchEngine->update( $id, $title, $text );
	}

	/**
	 * @see SearchEngine::updateTitle
	 *
	 * {@inheritDoc}
	 */
	public function updateTitle( $id, $title ) {
		$this->fallbackSearchEngine->updateTitle( $id, $title );
	}

	/**
	 * @see SearchEngine::delete
	 *
	 * {@inheritDoc}
	 */
	public function delete( $id, $title ) {
		$this->fallbackSearchEngine->delete( $id, $title );
	}

	/**
	 * @see SearchEngine::setFeatureData
	 *
	 * {@inheritDoc}
	 */
	public function setFeatureData( $feature, $data ) {
		parent::setFeatureData( $feature, $data );
		$this->fallbackSearchEngine->setFeatureData( $feature, $data );
	}

	/**
	 * @see SearchEngine::getFeatureData
	 *
	 * @param string $feature
	 *
	 * @return array|null
	 */
	public function getFeatureData( $feature ) {
		if ( array_key_exists( $feature, $this->features ) ) {
			return $this->features[$feature];
		}

		return null;
	}

	/**
	 * @see SearchEngine::replacePrefixes
	 *
	 * {@inheritDoc}
	 */
	public function replacePrefixes( $query ) {
		return $query;
	}

	/**
	 * @see SearchEngine::transformSearchTerm
	 *
	 * {@inheritDoc}
	 */
	public function transformSearchTerm( $term ) {
		return $term;
	}

	/**
	 * @see SearchEngine::setLimitOffset
	 *
	 * {@inheritDoc}
	 */
	public function setLimitOffset( $limit, $offset = 0 ) {
		parent::setLimitOffset( $limit, $offset );
		$this->extendedSearch->setLimitOffset( $limit, $offset );
		$this->fallbackSearchEngine->setLimitOffset( $limit, $offset );
	}

	/**
	 * @see SearchEngine::setNamespaces
	 *
	 * {@inheritDoc}
	 */
	public function setNamespaces( $namespaces ) {
		parent::setNamespaces( $namespaces );

		$this->extendedSearch->setNamespaces(
			$this->namespaces
		);

		$this->fallbackSearchEngine->setNamespaces( $namespaces );
	}

	/**
	 * @see SearchEngine::setShowSuggestion
	 *
	 * {@inheritDoc}
	 */
	public function setShowSuggestion( $showSuggestion ) {
		parent::setShowSuggestion( $showSuggestion );
		$this->fallbackSearchEngine->setShowSuggestion( $showSuggestion );
	}

	/**
	 * @see SearchEngine::completionSearchWithVariants
	 *
	 * Called by `ApiQueryPrefixSearch.php`, `ApiOpenSearch.php`
	 *
	 * {@inheritDoc}
	 */
	public function completionSearchWithVariants( $search ) {
		// #4342
		//
		// This method runs before `SearchEngine::completionSearchBackend`.
		//
		// Hold on to the original search term because `SearchEngine::completionSearch`
		// and `SearchEngine::completionSearchWithVariants` do normalize the string
		// and remove any namespace prefix they encounter.
		//
		// We may need the orginal search term when it is required to run the
		// fallbacksearch to complete the search request and no SMW specific query
		// component was detected.
		$this->extendedSearch->setCompletionSearchTerm(
			$search
		);

		return parent::completionSearchWithVariants( $search );
	}

	/**
	 * @see SearchEngine::completionSearchBackend
	 *
	 * {@inheritDoc}
	 */
	protected function completionSearchBackend( $search ) {
		$this->extendedSearch->setNamespaces(
			$this->namespaces
		);

		return $this->extendedSearch->completionSearch( $search );
	}

	/**
	 * @since 3.0
	 *
	 * @return
	 */
	public function getErrors() {
		return $this->extendedSearch->getErrors();
	}

	/**
	 * @since 3.0
	 *
	 * @return string
	 */
	public function getQueryString() {
		return $this->extendedSearch->getQueryString();
	}

	/**
	 * @since 3.0
	 *
	 * @return string
	 */
	public function getQueryLink() {
		return $this->extendedSearch->getQueryLink();
	}

	/**
	 * @return int
	 */
	public function getLimit() {
		return $this->limit;
	}

	/**
	 * @return int
	 */
	public function getOffset() {
		return $this->offset;
	}

	/**
	 * @return bool
	 */
	public function getShowSuggestion() {
		return $this->showSuggestion;
	}

}

class_alias( ExtendedSearchEngine::class, 'SMWSearch' );
