<?php declare(strict_types=1);

namespace Neusta\IntexClient\Connection\Model;


use GuzzleHttp\Exception\RequestException;
use Psr\Http\Message\ResponseInterface;
use Psr\Log\LoggerInterface;
use Neusta\IntexClientConfig\Model\Config\Config;
use Neusta\IntexClientConfig\Provider\JsonConfigProvider;
use \GuzzleHttp\Client as GuzzleHttpClient;
use \GuzzleHttp\Event\BeforeEvent;
use \GuzzleHttp\Event\CompleteEvent;

use function sprintf;

abstract class Connection
{
    protected const HTTP_STATUS_CODE_OK = 200;

    /**
     * @var GuzzleHttpClient
     */
    protected $httpClient;

    /**
     * @var Config
     */
    protected $serverConfig;

    /**
     * @var LoggerInterface
     */
    protected $logger;

    public function __construct(JsonConfigProvider $configProvider, LoggerInterface $logger)
    {
        $this->logger = $logger;
        $this->serverConfig = $configProvider->getConfig('server');
        $this->httpClient = new GuzzleHttpClient(
            $this->getHttpClientOptions()
        );

        $this->initHttpEventEmitter();
    }

    protected function authenticate(): bool
    {
        $result = false;

        $response = $this->createPost(
            $this->serverConfig->getAuthUri()
        );

        $statusCode = $response->getStatusCode();

        if ($statusCode === self::HTTP_STATUS_CODE_OK) {
            $this->logger->info('authentication successful');
            $result = true;
        } else {
            $this->logger->info(
                sprintf('authentication failed with Code: %s', $statusCode)
            );
        }

        return $result;
    }

    protected function createPost(string $uri, array $options = []): ?ResponseInterface
    {
        $result = null;
        $request = $this->httpClient->postAsync($uri, $options);

        $request->then(
            static function (ResponseInterface $response) {
                $result = $response;
            },
            function (RequestException $exception) {
                $this->logger->info(
                    sprintf('Crucial Error: %s \n Trace: %u', $exception->getMessage(), $exception->getTraceAsString())
                );
            }
        );

        return $result;
    }

    protected function createGet(string $uri, array $options = []): ?ResponseInterface
    {
        $result = null;
        $request = $this->httpClient->getAsync($uri, $options);

        $request->then(
            static function (ResponseInterface $response) {
                $result = $response;
            },
            function (RequestException $exception) {
                $this->logger->info(
                    sprintf('Crucial Error: %s \n Trace: %u', $exception->getMessage(), $exception->getTraceAsString())
                );
            }
        );

        return $result;
    }

    protected function initHttpEventEmitter(): void
    {
        // initialize the EventEmitters to get a good Logging whether the Request was send
        // or failed
        $this->httpClient->getEmitter()->on('before', static function (BeforeEvent $event) {
            $this->logger->info(
                sprintf('About to send Request: %s', $event->getRequest())
            );
        });

        $this->httpClient->getEmitter()->on('complete', static function (CompleteEvent $event) {
            $this->logger->info(
                sprintf('Request %s finished', $event->getRequest())
            );
        });

        $this->httpClient->getEmitter()->on('error', static function (ErrorEvent $event) {
            $this->logger->info(
                sprintf('Request %s failed', $event->getRequest())
            );
        });
    }

    protected function getHttpClientOptions(): array
    {
        // Configure Base URL for all ongoing Requests and set Cookies to true,
        // so we can handle the auth cookie over and over again without using authenticate() multiple times
        return [
            'base_url' => $this->serverConfig->getBaseUrl(),
            'cookies' => true
        ];
    }
}
