<?php

namespace Modular\ConnectorDependencies\Illuminate\Http\Client;

use Closure;
use Modular\ConnectorDependencies\GuzzleHttp\Promise\PromiseInterface;
use Modular\ConnectorDependencies\GuzzleHttp\Psr7\Response as Psr7Response;
use Modular\ConnectorDependencies\GuzzleHttp\TransferStats;
use Modular\ConnectorDependencies\Illuminate\Contracts\Events\Dispatcher;
use Modular\ConnectorDependencies\Illuminate\Support\Str;
use Modular\ConnectorDependencies\Illuminate\Support\Traits\Macroable;
use Modular\ConnectorDependencies\PHPUnit\Framework\Assert as PHPUnit;
/**
 * @method \Illuminate\Http\Client\PendingRequest accept(string $contentType)
 * @method \Illuminate\Http\Client\PendingRequest acceptJson()
 * @method \Illuminate\Http\Client\PendingRequest asForm()
 * @method \Illuminate\Http\Client\PendingRequest asJson()
 * @method \Illuminate\Http\Client\PendingRequest asMultipart()
 * @method \Illuminate\Http\Client\PendingRequest async()
 * @method \Illuminate\Http\Client\PendingRequest attach(string|array $name, string|resource $contents = '', string|null $filename = null, array $headers = [])
 * @method \Illuminate\Http\Client\PendingRequest baseUrl(string $url)
 * @method \Illuminate\Http\Client\PendingRequest beforeSending(callable $callback)
 * @method \Illuminate\Http\Client\PendingRequest bodyFormat(string $format)
 * @method \Illuminate\Http\Client\PendingRequest contentType(string $contentType)
 * @method \Illuminate\Http\Client\PendingRequest dd()
 * @method \Illuminate\Http\Client\PendingRequest dump()
 * @method \Illuminate\Http\Client\PendingRequest \Modular\ConnectorDependencies\retry(int $times, int $sleep = 0, ?callable $when = null)
 * @method \Illuminate\Http\Client\PendingRequest sink(string|resource $to)
 * @method \Illuminate\Http\Client\PendingRequest stub(callable $callback)
 * @method \Illuminate\Http\Client\PendingRequest timeout(int $seconds)
 * @method \Illuminate\Http\Client\PendingRequest withBasicAuth(string $username, string $password)
 * @method \Illuminate\Http\Client\PendingRequest withBody(resource|string $content, string $contentType)
 * @method \Illuminate\Http\Client\PendingRequest withCookies(array $cookies, string $domain)
 * @method \Illuminate\Http\Client\PendingRequest withDigestAuth(string $username, string $password)
 * @method \Illuminate\Http\Client\PendingRequest withHeaders(array $headers)
 * @method \Illuminate\Http\Client\PendingRequest withMiddleware(callable $middleware)
 * @method \Illuminate\Http\Client\PendingRequest withOptions(array $options)
 * @method \Illuminate\Http\Client\PendingRequest withToken(string $token, string $type = 'Bearer')
 * @method \Illuminate\Http\Client\PendingRequest withUserAgent(string $userAgent)
 * @method \Illuminate\Http\Client\PendingRequest withoutRedirecting()
 * @method \Illuminate\Http\Client\PendingRequest withoutVerifying()
 * @method array pool(callable $callback)
 * @method \Illuminate\Http\Client\Response delete(string $url, array $data = [])
 * @method \Illuminate\Http\Client\Response get(string $url, array|string|null $query = null)
 * @method \Illuminate\Http\Client\Response \Modular\ConnectorDependencies\head(string $url, array|string|null $query = null)
 * @method \Illuminate\Http\Client\Response patch(string $url, array $data = [])
 * @method \Illuminate\Http\Client\Response post(string $url, array $data = [])
 * @method \Illuminate\Http\Client\Response put(string $url, array $data = [])
 * @method \Illuminate\Http\Client\Response send(string $method, string $url, array $options = [])
 *
 * @see \Illuminate\Http\Client\PendingRequest
 */
class Factory
{
    use Macroable {
        __call as macroCall;
    }
    /**
     * The event dispatcher implementation.
     *
     * @var \Illuminate\Contracts\Events\Dispatcher|null
     */
    protected $dispatcher;
    /**
     * The stub callables that will handle requests.
     *
     * @var \Illuminate\Support\Collection
     */
    protected $stubCallbacks;
    /**
     * Indicates if the factory is recording requests and responses.
     *
     * @var bool
     */
    protected $recording = \false;
    /**
     * The recorded response array.
     *
     * @var array
     */
    protected $recorded = [];
    /**
     * All created response sequences.
     *
     * @var array
     */
    protected $responseSequences = [];
    /**
     * Create a new factory instance.
     *
     * @param  \Illuminate\Contracts\Events\Dispatcher|null  $dispatcher
     * @return void
     */
    public function __construct(Dispatcher $dispatcher = null)
    {
        $this->dispatcher = $dispatcher;
        $this->stubCallbacks = \Modular\ConnectorDependencies\collect();
    }
    /**
     * Create a new response instance for use during stubbing.
     *
     * @param  array|string  $body
     * @param  int  $status
     * @param  array  $headers
     * @return \GuzzleHttp\Promise\PromiseInterface
     */
    public static function response($body = null, $status = 200, $headers = [])
    {
        if (is_array($body)) {
            $body = json_encode($body);
            $headers['Content-Type'] = 'application/json';
        }
        $response = new Psr7Response($status, $headers, $body);
        return class_exists(\Modular\ConnectorDependencies\GuzzleHttp\Promise\Create::class) ? \Modular\ConnectorDependencies\GuzzleHttp\Promise\Create::promiseFor($response) : \Modular\ConnectorDependencies\GuzzleHttp\Promise\promise_for($response);
    }
    /**
     * Get an invokable object that returns a sequence of responses in order for use during stubbing.
     *
     * @param  array  $responses
     * @return \Illuminate\Http\Client\ResponseSequence
     */
    public function sequence(array $responses = [])
    {
        return $this->responseSequences[] = new ResponseSequence($responses);
    }
    /**
     * Register a stub callable that will intercept requests and be able to return stub responses.
     *
     * @param  callable|array  $callback
     * @return $this
     */
    public function fake($callback = null)
    {
        $this->record();
        $this->recorded = [];
        if (is_null($callback)) {
            $callback = function () {
                return static::response();
            };
        }
        if (is_array($callback)) {
            foreach ($callback as $url => $callable) {
                $this->stubUrl($url, $callable);
            }
            return $this;
        }
        $this->stubCallbacks = $this->stubCallbacks->merge(\Modular\ConnectorDependencies\collect([function ($request, $options) use ($callback) {
            $response = $callback instanceof Closure ? $callback($request, $options) : $callback;
            if ($response instanceof PromiseInterface) {
                $options['on_stats'](new TransferStats($request->toPsrRequest(), $response->wait()));
            }
            return $response;
        }]));
        return $this;
    }
    /**
     * Register a response sequence for the given URL pattern.
     *
     * @param  string  $url
     * @return \Illuminate\Http\Client\ResponseSequence
     */
    public function fakeSequence($url = '*')
    {
        return \Modular\ConnectorDependencies\tap($this->sequence(), function ($sequence) use ($url) {
            $this->fake([$url => $sequence]);
        });
    }
    /**
     * Stub the given URL using the given callback.
     *
     * @param  string  $url
     * @param  \Illuminate\Http\Client\Response|\GuzzleHttp\Promise\PromiseInterface|callable  $callback
     * @return $this
     */
    public function stubUrl($url, $callback)
    {
        return $this->fake(function ($request, $options) use ($url, $callback) {
            if (!Str::is(Str::start($url, '*'), $request->url())) {
                return;
            }
            return $callback instanceof Closure || $callback instanceof ResponseSequence ? $callback($request, $options) : $callback;
        });
    }
    /**
     * Begin recording request / response pairs.
     *
     * @return $this
     */
    protected function record()
    {
        $this->recording = \true;
        return $this;
    }
    /**
     * Record a request response pair.
     *
     * @param  \Illuminate\Http\Client\Request  $request
     * @param  \Illuminate\Http\Client\Response  $response
     * @return void
     */
    public function recordRequestResponsePair($request, $response)
    {
        if ($this->recording) {
            $this->recorded[] = [$request, $response];
        }
    }
    /**
     * Assert that a request / response pair was recorded matching a given truth test.
     *
     * @param  callable  $callback
     * @return void
     */
    public function assertSent($callback)
    {
        PHPUnit::assertTrue($this->recorded($callback)->count() > 0, 'An expected request was not recorded.');
    }
    /**
     * Assert that the given request was sent in the given order.
     *
     * @param  array  $callbacks
     * @return void
     */
    public function assertSentInOrder($callbacks)
    {
        $this->assertSentCount(count($callbacks));
        foreach ($callbacks as $index => $url) {
            $callback = is_callable($url) ? $url : function ($request) use ($url) {
                return $request->url() == $url;
            };
            PHPUnit::assertTrue($callback($this->recorded[$index][0], $this->recorded[$index][1]), 'An expected request (#' . ($index + 1) . ') was not recorded.');
        }
    }
    /**
     * Assert that a request / response pair was not recorded matching a given truth test.
     *
     * @param  callable  $callback
     * @return void
     */
    public function assertNotSent($callback)
    {
        PHPUnit::assertFalse($this->recorded($callback)->count() > 0, 'Unexpected request was recorded.');
    }
    /**
     * Assert that no request / response pair was recorded.
     *
     * @return void
     */
    public function assertNothingSent()
    {
        PHPUnit::assertEmpty($this->recorded, 'Requests were recorded.');
    }
    /**
     * Assert how many requests have been recorded.
     *
     * @param  int  $count
     * @return void
     */
    public function assertSentCount($count)
    {
        PHPUnit::assertCount($count, $this->recorded);
    }
    /**
     * Assert that every created response sequence is empty.
     *
     * @return void
     */
    public function assertSequencesAreEmpty()
    {
        foreach ($this->responseSequences as $responseSequence) {
            PHPUnit::assertTrue($responseSequence->isEmpty(), 'Not all response sequences are empty.');
        }
    }
    /**
     * Get a collection of the request / response pairs matching the given truth test.
     *
     * @param  callable  $callback
     * @return \Illuminate\Support\Collection
     */
    public function recorded($callback = null)
    {
        if (empty($this->recorded)) {
            return \Modular\ConnectorDependencies\collect();
        }
        $callback = $callback ?: function () {
            return \true;
        };
        return \Modular\ConnectorDependencies\collect($this->recorded)->filter(function ($pair) use ($callback) {
            return $callback($pair[0], $pair[1]);
        });
    }
    /**
     * Create a new pending request instance for this factory.
     *
     * @return \Illuminate\Http\Client\PendingRequest
     */
    protected function newPendingRequest()
    {
        return new PendingRequest($this);
    }
    /**
     * Get the current event dispatcher implementation.
     *
     * @return \Illuminate\Contracts\Events\Dispatcher|null
     */
    public function getDispatcher()
    {
        return $this->dispatcher;
    }
    /**
     * Execute a method against a new pending request instance.
     *
     * @param  string  $method
     * @param  array  $parameters
     * @return mixed
     */
    public function __call($method, $parameters)
    {
        if (static::hasMacro($method)) {
            return $this->macroCall($method, $parameters);
        }
        return \Modular\ConnectorDependencies\tap($this->newPendingRequest(), function ($request) {
            $request->stub($this->stubCallbacks);
        })->{$method}(...$parameters);
    }
}
