<?php
namespace Planaday\Api\V1;

use CurlHandle;
use Planaday\Api\ApiException\ApiErrorResponseException;
use Planaday\Api\ApiException\ClientNotInitializedException;
use Planaday\Config\General\Api\ApiSettings;
use Planaday\Utility\Debug\Log\Logging;
use Planaday\Utility\PluginData;

class ClientV1
{
	use Logging;

	function __construct(
		protected ApiSettings $options,
		protected PluginData $pluginData
	) {}

	/**
	 * Call the planaday API
	 *
	 * @param string|null $baseUrl
	 * @param string|null $key
	 * @param string $call
	 * @param array<mixed> $args
	 * @param string $method
	 * @param null|mixed $body
	 *
	 * @return ClientV1Response
	 * @throws ApiErrorResponseException|ClientNotInitializedException
	 */
    public function call(?string $baseUrl, ?string $key, string $call, array $args = [], string $method = 'GET', mixed $body = null): ClientV1Response {
		if (empty($baseUrl)) {
			$this->logError("No base url provided, cannot execute call");
			throw new ClientNotInitializedException("No base url provided, cannot execute call");
		}
		if (empty($key)) $this->logWarning("No api key provided, call will not be authenticated");
	    set_time_limit(300);

        $url = $this->buildUrl($baseUrl, $call, $args);

        $ch = curl_init($url);

		if ($ch === false){
			$this->logError("could not initialize curl");
			throw new ClientNotInitializedException("Could not initialize curl");
		}

		$this->setCurlOptions($ch, $method, $body ?? [], $key);

		$this->logDebug("calling url: $url");
        $response = curl_exec($ch);
		$htmlCode = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
		$this->logDebug("response code: $htmlCode, response:", $response);

        if ($response === false) {
			$errorCode = curl_errno($ch);

            $error = sprintf(
                'Error: "%s", Code: %s, HTTP-response: %s',
                curl_error($ch),
                $errorCode,
                $htmlCode
            );

			$this->logError($error);

            curl_close($ch);

			throw new ApiErrorResponseException($error, $errorCode);
        }

        curl_close($ch);

		switch ($htmlCode) {
			case 400:
				$this->logError("Bad request to url: $url, with args: (data)", [
					"response" => $response,
					"url" => $url,
					"args" => $args,
					"method" => $method,
					"body" => $body
				]);

				throw new ApiErrorResponseException("Bad request", 400);
			case 500:
				$this->logWarning("Internal server error to url: $url, with args: (data)", [
					"response" => $response,
					"url" => $url,
					"args" => $args,
					"method" => $method,
					"body" => $body
				]);

				throw new ApiErrorResponseException("Internal server error", 500);
		}

		return (new ClientV1Response())
			->setStatus($htmlCode)
			->setData(json_decode($response, true));
    }

	/**
	 * @throws ApiErrorResponseException|ClientNotInitializedException
	 */
	public function callEndpoint(string $endpoint, array $args = [], string $method = 'GET', mixed $body = null): ClientV1Response {
		return $this->call(
			$this->options->getDomain(),
			$this->options->getApiKey(),
			$endpoint,
			$args,
			$method,
			$body
		);
	}

	private function buildUrl(string $baseUrl, string $call, array $arguments):string {
		$params = [];
		if (!empty($arguments)) {
			foreach ($arguments as $name => $value) {
				if (!empty($value)) {
					$params[] = urlencode($name) . '=' . urlencode($value);
				}
			}
		}

		$params[] = 'pluginVersion=' . urlencode(implode('.', $this->pluginData->getPluginVersion()));
		$params[] = 'phpversion=' . phpversion();

		return $baseUrl . '/v1/' . $call . '?' . implode('&', $params);
	}

	private function setCurlOptions( CurlHandle $curl, string $method, array $body, ?string $key):void {
		if ( $method === 'POST' ) {
			curl_setopt($curl, CURLOPT_POST, 1);
			curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($body));
		}

		curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
		curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
		curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
		curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
		curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
		curl_setopt($curl, CURLOPT_HTTPHEADER,
			[
				sprintf('X-Api-Key: %s', $key),
				sprintf('X-Plugin-Version: %s', implode('.', $this->pluginData->getPluginVersion())),
				sprintf('X-PHP-Version: %s', phpversion()),
				'Content-Type: application/json'
			]
		);
	}
}
