<?php
 namespace MongoDB\Operation;
 use MongoDB\Driver\Command;
 use MongoDB\Driver\ReadPreference;
 use MongoDB\Driver\Server;
 use MongoDB\Driver\Session;
 use MongoDB\Exception\InvalidArgumentException;
 use MongoDB\Exception\UnsupportedException;
 use function current;
 use function is_array;
 use function is_string;
 use function MongoDB\server_supports_feature;
 class Explain implements Executable {
 const VERBOSITY_ALL_PLANS = 'allPlansExecution';
 const VERBOSITY_EXEC_STATS = 'executionStats';
 const VERBOSITY_QUERY = 'queryPlanner';
 private static $wireVersionForDistinct = 4;
 private static $wireVersionForFindAndModify = 4;
 private $databaseName;
 private $explainable;
 private $options;
 public function __construct($databaseName, Explainable $explainable, array $options = []) {
 if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) {
 throw InvalidArgumentException::invalidType('"readPreference" option', $options['readPreference'], ReadPreference::class);
 }
 if (isset($options['session']) && ! $options['session'] instanceof Session) {
 throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class);
 }
 if (isset($options['typeMap']) && ! is_array($options['typeMap'])) {
 throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array');
 }
 if (isset($options['verbosity']) && ! is_string($options['verbosity'])) {
 throw InvalidArgumentException::invalidType('"verbosity" option', $options['verbosity'], 'string');
 }
 $this->databaseName = $databaseName;
 $this->explainable = $explainable;
 $this->options = $options;
 }
 public function execute(Server $server) {
 if ($this->explainable instanceof Distinct && ! server_supports_feature($server, self::$wireVersionForDistinct)) {
 throw UnsupportedException::explainNotSupported();
 }
 if ($this->isFindAndModify($this->explainable) && ! server_supports_feature($server, self::$wireVersionForFindAndModify)) {
 throw UnsupportedException::explainNotSupported();
 }
 $cmd = ['explain' => $this->explainable->getCommandDocument($server)];
 if (isset($this->options['verbosity'])) {
 $cmd['verbosity'] = $this->options['verbosity'];
 }
 $cursor = $server->executeCommand($this->databaseName, new Command($cmd), $this->createOptions());
 if (isset($this->options['typeMap'])) {
 $cursor->setTypeMap($this->options['typeMap']);
 }
 return current($cursor->toArray());
 }
 private function createOptions() {
 $options = [];
 if (isset($this->options['readPreference'])) {
 $options['readPreference'] = $this->options['readPreference'];
 }
 if (isset($this->options['session'])) {
 $options['session'] = $this->options['session'];
 }
 return $options;
 }
 private function isFindAndModify($explainable) {
 if ($explainable instanceof FindAndModify || $explainable instanceof FindOneAndDelete || $explainable instanceof FindOneAndReplace || $explainable instanceof FindOneAndUpdate) {
 return true;
 }
 return false;
 }
 }
 