<?php

namespace Api\PhpUtils\Http;

use GuzzleHttp\Client;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Handler\CurlHandler;
use GuzzleHttp\Handler\CurlMultiHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\RetryMiddleware;
use Api\PhpUtils\Log\FileLog;

class Base
{
    /**
     * 最大重试次数
     */
    private $retry = 0;

    /**
     * 配置（参照https://guzzle-cn.readthedocs.io/zh_CN/latest/request-options.html）
     */
    private $config = [
        'allow_redirects' => true, //允许302跳转
        'verify' => false, //禁用证书验证
        'connect_timeout' => 3, //连接超时毫秒数
        'timeout' => 3, //请求超时毫秒数
    ];

    private $log = [
        'url' => '',
        'params' => [],
        'response' => ''
    ];

    public function __construct()
    {
    }

    public function initClient($custom_config = [], $url = [], $params = [])
    {
        $this->log['url'] = $url;
        $this->log['params'] = $params;
        //
        if (isset($custom_config['retry']) && !empty($custom_config['retry'])) {
            $this->retry = intval($custom_config['retry']);
        }
        //创建Handler
        if (isset($custom_config['concurrent']) && !empty($custom_config['concurrent'])) {
            $handlerStack = HandlerStack::create(new CurlMultiHandler());
        } else {
            $handlerStack = HandlerStack::create(new CurlHandler());
        }
        //创建重试中间件，指定决策者为 $this->retryDecider(),指定重试延迟为 $this->retryDelay()
        $handlerStack->push(Middleware::retry($this->retryDecider(), $this->retryDelay()));
        //指定handler
        $this->config['handler'] = $handlerStack;
        //使用自定义timeout覆盖默认timeout
        if (is_array($custom_config) && !empty($custom_config)) {
            $this->config = array_merge($this->config, $custom_config);
        }
        return new Client($this->config);
    }

    /**
     * 确定重试逻辑：false表示不重试，否则重试
     * @return
     */
    protected function retryDecider()
    {
        return function (
            $retries,
            Request $request,
            Response $response = null,
            RequestException $exception = null
        ) {
            // 超过最大重试次数，不再重试
            if ($retries >= $this->retry) {
                return false;
            }
            // 请求失败，继续重试
            if ($exception instanceof ConnectException) {
                $this->log['response'] = $exception->getMessage();
                $this->addLog();
                return true;
            }
            if (!empty($response)) {
                // 如果请求有响应，但是状态码大于等于500，继续重试
                if ($response->getStatusCode() >= 500) {
                    $this->log['response'] = 'getStatusCode = ' . $response->getStatusCode();
                    $this->addLog();
                    return true;
                }
            }
            return false;
        };
    }

    /**
     * 返回一个匿名函数，该匿名函数拉开重试间隔（毫秒）
     * @return
     */
    protected function retryDelay()
    {
        return function ($numberOfRetries) {
            return RetryMiddleware::exponentialDelay($numberOfRetries) * 100;
        };
    }

    private function addLog(){
        FileLog::info('retry', json_encode($this->log));
    }
}
