<?php

namespace Opentalent\OtCore\Service;

use GuzzleHttp\Client;
use GuzzleHttp\Cookie\CookieJar;
use GuzzleHttp\Exception\GuzzleException;
use Opentalent\OtCore\Exception\ApiRequestException;
use Psr\Http\Message\ResponseInterface;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;

class OpentalentApiService implements LoggerAwareInterface
{
    use LoggerAwareTrait;

    const DEFAULT_BASE_URI = 'https://api.opentalent.fr';
    protected array $variants_uris = [
          "preprod.opentalent.fr" => "https://api.preprod.opentalent.fr",
          "local.sub.opentalent.fr" => "http://docker.nginx.opentalent.fr",
          "typo3" => "http://docker.nginx.opentalent.fr"
    ];

    protected object $client;
    protected object $context;

    /**
     * @param ObjectManagerInterface $objectManager
     * @param object|null $client  [For tests only]
     * @param object|null $context  [For tests only]
     */
    public function __construct(
        ?object $client = null,
        ?object $context = null,
        ?object $cookieJar = null
    ) {
        // Get the current context (prod, dev...)
        if ($context === null) {
            $this->context = \TYPO3\CMS\Core\Core\Environment::getContext();
        } else {
            $this->context = $context;
        }

        if ($client === null) {
            $args = ['base_uri' => $this->getApiUri()];
            if ($cookieJar != null) {
                $args['cookies'] = $cookieJar;
            }
            $this->client = new Client($args);
        } else {
            $this->client = $client;
        }
    }

    public function getClient() {
        return $this->client;
    }

    /**
     * @param Client $client
     */
    public function injectClient(Client $client) {
        $this->client = $client;
    }

    /**
     * Return the API URI for the current repository
     *
     * @param string $trailing_part
     * @return string
     */
    public function getApiUri(string $trailing_part = ""): string
    {
        $host = $_SERVER['HTTP_HOST'] ?? $_SERVER['VIRTUAL_HOST'];

        if (isset($this->variants_uris[$host])) {
            $uri = $this->variants_uris[$host];
        } else {
            $uri = self::DEFAULT_BASE_URI;
        }

        return rtrim($uri, '/') . '/' . ltrim($trailing_part, '/');
    }

    /**
     * Send a request to the API and
     * returns the Json response as an array
     *
     * @param string $uri
     * @param array $params
     * @return array
     * @throws ApiRequestException
     * @throws \JsonException
     */
    public function getJsonDecoded(
        string $uri,
        array $params = []
    ): array
    {
        $body = $this->getBody($uri, $params);
        $data = json_decode($body,true);
        if ($data !== null) {
            return json_decode($body,true);
        } else {
            throw new \JsonException('Response can not be decoded as json: ' . substr($body, 0, 300) . '...');
        }
    }

    /**
     * Send a request to the API and returns
     * the response's body as a string
     *
     * @param string $uri
     * @param array $params
     * @return string
     * @throws ApiRequestException
     */
    public function getBody(
        string $uri,
        array $params = []
    ): string
    {
        return (string)$this->get($uri, $params)->getBody();
    }

    /**
     * Send a GET request to the API and returns
     * the result as a Response object
     *
     * @param string $uri
     * @param array $params
     * @param array $options
     * @return ResponseInterface
     * @throws ApiRequestException
     */
    public function get(
        string $uri,
        array $params = [],
        array $options = []
    ): ResponseInterface
    {
        return $this->request('GET', $uri, $params, $options);
    }

    /**
     * Send a request to the API and returns
     * the result as a Response object
     *
     * @param string $httpMethod
     * @param string $uri
     * @param array $params
     * @param array $options
     * @return ResponseInterface
     * @throws ApiRequestException
     */
    public function request(
        string $httpMethod,
        string $uri,
        array $params = [],
        array $options = []
    ): ?ResponseInterface
    {
        $parsedUrl = parse_url($uri);
        $params += ($parsedUrl['query'] ?? []);

        $path = http_build_query($params);
        $uri = rtrim($uri, '/');
        if ($path) {
            $uri .= '?' . $path;
        }

        try {
            if ($this->context->isDevelopment()) {
                $this->logger->info('API Call: ' . $uri);
            }
            return $this->client->request($httpMethod, $uri, $options);
        } catch (GuzzleException $e) {
            throw ApiRequestException::from_exception($e);
        }
    }
}
