<?php

namespace CirrusSearch\Search;

use Elastica\Response;
use MediaWiki\Search\BaseSearchResultSet;
use MediaWiki\Search\ISearchResultSet;
use MediaWiki\Search\SearchResultSetTrait;
use MediaWiki\Title\Title;
use Wikimedia\HtmlArmor\HtmlArmor;

class InterleavedResultSet extends BaseSearchResultSet implements CirrusSearchResultSet, SearchMetricsProvider {
	use SearchResultSetTrait;

	/** @var string[] Doc ID's belonging to team A */
	private $teamA;
	/** @var string[] Doc ID's belonging to team B */
	private $teamB;
	/** @var int Offset to calculate next unused result in team A */
	private $offset;
	/** @var CirrusSearchResultSet */
	private $delegate;
	/** @var CirrusSearchResult[] */
	private $results;

	/**
	 * @param CirrusSearchResultSet $nested Original result set for team A (control)
	 * @param CirrusSearchResult[] $interleaved Interleaved results
	 * @param string[] $teamA Document id's belonging to team A
	 * @param string[] $teamB Document id's belonging to team B
	 * @param int $offset Offset to calculate next unused result in team A
	 */
	public function __construct(
		CirrusSearchResultSet $nested,
		array $interleaved,
		array $teamA,
		array $teamB,
		$offset
	) {
		$this->results = $interleaved;
		$this->teamA = $teamA;
		$this->teamB = $teamB;
		$this->offset = $offset;
		$this->delegate = $nested;
	}

	/** @inheritDoc */
	public function getMetrics() {
		return [
			'wgCirrusSearchTeamDraft' => [
				'a' => $this->teamA,
				'b' => $this->teamB,
			],
		];
	}

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

	/**
	 * @return CirrusSearchResult[]
	 */
	public function extractResults() {
		return $this->results;
	}

	/**
	 * @return Response|null
	 */
	public function getElasticResponse() {
		return $this->delegate->getElasticResponse();
	}

	/**
	 * @return \Elastica\ResultSet|null
	 */
	public function getElasticaResultSet() {
		return $this->delegate->getElasticaResultSet();
	}

	/**
	 * @param CirrusSearchResultSet $res
	 * @param int $type one of searchresultset::* constants
	 * @param string $interwiki
	 */
	public function addInterwikiResults( CirrusSearchResultSet $res, $type, $interwiki ) {
		$this->delegate->addInterwikiResults( $res, $type, $interwiki );
	}

	/**
	 * @param string $newQuery
	 * @param HtmlArmor|string|null $newQuerySnippet
	 */
	public function setRewrittenQuery( string $newQuery, $newQuerySnippet = null ) {
		$this->delegate->setRewrittenQuery( $newQuery, $newQuerySnippet );
	}

	/**
	 * Count elements of an object
	 * @link https://php.net/manual/en/countable.count.php
	 * @return int The custom count as integer.
	 * @since 5.1.0
	 */
	public function count(): int {
		return count( $this->results );
	}

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

	/**
	 * Some search modes return a total hit count for the query
	 * in the entire article database. This may include pages
	 * in namespaces that would not be matched on the given
	 * settings.
	 *
	 * Return null if no total hits number is supported.
	 *
	 * @return int|null
	 */
	public function getTotalHits() {
		return $this->delegate->getTotalHits();
	}

	/**
	 * Some search modes will run an alternative query that it thinks gives
	 * a better result than the provided search. Returns true if this has
	 * occurred.
	 *
	 * @return bool
	 */
	public function hasRewrittenQuery() {
		return $this->delegate->hasRewrittenQuery();
	}

	/**
	 * @return string|null The search the query was internally rewritten to,
	 *  or null when the result of the original query was returned.
	 */
	public function getQueryAfterRewrite() {
		return $this->delegate->getQueryAfterRewrite();
	}

	/**
	 * @return string|null Same as self::getQueryAfterRewrite(), but in HTML
	 *  and with changes highlighted. Null when the query was not rewritten.
	 */
	public function getQueryAfterRewriteSnippet() {
		return $this->delegate->getQueryAfterRewriteSnippet();
	}

	/**
	 * Some search modes return a suggested alternate term if there are
	 * no exact hits. Returns true if there is one on this set.
	 *
	 * @return bool
	 */
	public function hasSuggestion() {
		return $this->delegate->hasSuggestion();
	}

	/**
	 * @return string|null Suggested query, null if none
	 */
	public function getSuggestionQuery() {
		return $this->delegate->getSuggestionQuery();
	}

	/**
	 * @return string HTML highlighted suggested query, '' if none
	 */
	public function getSuggestionSnippet() {
		return $this->delegate->getSuggestionSnippet();
	}

	/**
	 * Return a result set of hits on other (multiple) wikis associated with this one
	 *
	 * @param int $type One of the ISearchResultSet::…_RESULTS constants
	 * @return ISearchResultSet[]
	 */
	public function getInterwikiResults( $type = self::SECONDARY_RESULTS ) {
		return $this->delegate->getInterwikiResults( $type );
	}

	/**
	 * Check if there are results on other wikis
	 *
	 * @param int $type One of the ISearchResultSet::…_RESULTS constants
	 * @return bool
	 */
	public function hasInterwikiResults( $type = self::SECONDARY_RESULTS ) {
		return $this->delegate->hasInterwikiResults( $type );
	}

	/**
	 * Did the search contain search syntax?  If so, Special:Search won't offer
	 * the user a link to a create a page named by the search string because the
	 * name would contain the search syntax.
	 * @return bool
	 */
	public function searchContainedSyntax() {
		return $this->delegate->searchContainedSyntax();
	}

	/**
	 * @return bool True when there are more pages of search results available.
	 */
	public function hasMoreResults() {
		return $this->delegate->hasMoreResults();
	}

	/**
	 * @inheritDoc
	 */
	public function isApproximateTotalHits(): bool {
		return $this->delegate->isApproximateTotalHits();
	}

	/**
	 * @param int $limit Shrink result set to $limit and flag
	 *  if more results are available.
	 */
	public function shrink( $limit ) {
		if ( $this->count() > $limit ) {
			$this->results = array_slice( $this->results, 0, $limit );
		}
		$this->delegate->shrink( $limit );
	}

	/**
	 * Extract all the titles in the result set.
	 * @return Title[]
	 */
	public function extractTitles() {
		return $this->delegate->extractTitles();
	}

	/**
	 * @param string $suggestionQuery
	 * @param HtmlArmor|string|null $suggestionSnippet
	 */
	public function setSuggestionQuery( string $suggestionQuery, $suggestionSnippet = null ) {
		$this->delegate->setSuggestionQuery( $suggestionQuery, $suggestionSnippet );
	}
}
