<?php

namespace classes;

use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Psr7\Response;
use Matrix\Exception;


class ServiceClient
{
    private Client $httpClient;
    private string $serviceURI;
    private Response $response;

    /**
     * @throws Exception
     */
    public function __construct(string $serviceReference, int $timeout = 3)
    {
        // Parses service reference
        $this->serviceURI = $this->parseServiceReference($serviceReference);

        $clientConfig = [
            'base_uri' => $this->serviceURI,
            'timeout' => $timeout,
            'Accept' => 'application/json',
            'Content-Type' => 'application/json',
            'http_errors' => false
        ];

        $this->httpClient = new Client($clientConfig);
    }

    /**
     * Parses the given service reference to determine if it is a valid URI.
     * If it is a valid URI, it returns the service reference as is. Otherwise,
     * it retrieves the environment URL corresponding to the service reference.
     *
     * @param string $serviceReference The service reference to be parsed.
     * @return string The parsed service reference, either as the original URI or the retrieved environment URL.
     * @throws Exception
     */
    private function parseServiceReference(string $serviceReference): string
    {
        // Checks if the service reference is a URI
        $pattern = '/^https?:\/\//';

        return preg_match($pattern, $serviceReference) === 1
            ? $serviceReference
            : $this->getEnvironmentURL($serviceReference);
    }

    /**
     * Retrieves the environment URL for a given service reference.
     *
     * @param string $serviceReference The reference key for the desired service.
     * @return string|null Returns the service URL if found, or null otherwise.
     * @throws Exception Thrown if the service URL cannot be found in the environment variables.
     */
    private function getEnvironmentURL(string $serviceReference): ?string
    {
        $serviceURLKey = strtoupper($serviceReference) . '_SERVICE_URL';
        if (isset($_ENV['services'][$serviceURLKey]))
        {
            return $_ENV['services'][$serviceURLKey];
        }
        else
        {
            throw new Exception("Unable to find service URL");
        }
    }

    /**
     * Retrieves the service URI.
     *
     * @return string The service URI.
     */
    public function getServiceURI(): string
    {
        return $this->serviceURI;
    }

    /**
     * Provides the HTTP client instance.
     *
     * @return Client The instance of the HTTP client
     */
    public function getHttpClient(): Client
    {
        return $this->httpClient;
    }

    /**
     * Sends a POST request to the specified endpoint with the provided JSON data.
     *
     * @param string $endpoint The API endpoint to send the POST request to
     * @param array $jsonDataArray The associative array of JSON data to include in the request body
     * @return void
     * @throws GuzzleException
     */
    public function post(string $endpoint, array $jsonDataArray): void
    {
        $this->response = $this->httpClient->post($endpoint, ['json' => $jsonDataArray]);
    }

    /**
     * Sends a GET request to the specified endpoint with the provided JSON data.
     *
     * @param string $endpoint The API endpoint to send the request to.
     * @param array  $jsonDataArray The associative array containing JSON data to include in the request.
     * @return void
     * @throws GuzzleException
     */
    public function get(string $endpoint, array $queryArray): void
    {
        $this->response = $this->httpClient->get($endpoint, ['query' => $queryArray]);
    }

    /**
     * Retrieves the error message corresponding to the response status code.
     *
     * @return string The error message describing the issue based on the response status code
     */
    public function getResponseErrorMessage(): string
    {
        $responseStatusCode = $this->response->getStatusCode();

        switch (true)
        {
            case $responseStatusCode >= 500:
                $errorMessage = "Service server error $responseStatusCode";
                break;
            case $responseStatusCode >= 400:
                $errorMessage = "Unable to access resource: $responseStatusCode";
                break;
            default:
                $errorMessage = '';
        }

        return $errorMessage;
    }

    /**
     * Retrieves the decoded response body as an associative array.
     *
     * @return array The decoded response body
     */
    public function getResponseBody(): array
    {
        $responseBody = $this->response->getBody();
        $responseContent = $responseBody->getContents();
        $responseArray = json_decode($responseContent, true);

        if (json_last_error() === JSON_ERROR_NONE && is_array($responseArray))
        {
            return $responseArray;
        }
        else
        {
            return ["responseContentString" => $responseContent];
        }
    }
}
