<?php

namespace Kendox;

require_once "class.kendox-token-generator.php";

class Client {
	/**
	 * Generated token for user.
	 *
	 * @var StdClass
	 */
	public $Token;

	/**
	 * URL of Kendox Service Endpoint.
	 *
	 * @var string
	 */
	public $ServiceEndpoint;

	/**
	 * Connection Id returned from service on successful login.
	 *
	 * @var string
	 */
	public $ConnectionId;

	public function __construct($serviceEndpoint) {
		if (!str_ends_with((string) $serviceEndpoint, '/')) {
			$serviceEndpoint .= '/';
		}
		$this->ServiceEndpoint = $serviceEndpoint;
	}

	/**
	 * Login to kendox service by creating a user based token, signed by Kendox trusted certificate.
	 *
	 * @param string $pfxFile     The full path and file name of the PFX-File (certificate)
	 * @param string $pfxPassword The password used for reading the PFX-File (certificate)
	 * @param string $userName    Username for token generation
	 *
	 * @return bool
	 */
	public function loginWithToken($pfxFile, $pfxPassword, $userName) {
		try {
			$issuer = gethostname();
			$tokenGenerator = new TokenGenerator($issuer, $pfxFile, $pfxPassword);
			$this->Token = $tokenGenerator->generateToken($userName);
			$logonParameters = [
				"tenantName" => "",
				"token" => $this->Token,
				"tokenType" => "InfoShareToken",
			];
			$result = $this->post("Authentication/LogonWithToken", $logonParameters);
			$this->ConnectionId = $result->LogonWithTokenResult->ConnectionId;

			return true;
		}
		catch (\Exception $ex) {
			throw new \Exception("Token-Login failed: " . $ex->getMessage());
		}
	}

	/**
	 * Logout from kendox.
	 *
	 * @return bool
	 */
	public function logout() {
		try {
			$logoutParameters = [
				"connectionId" => $this->ConnectionId,
			];
			$result = $this->post("Authentication/Logout", $logoutParameters);
			$this->ConnectionId = null;

			return true;
		}
		catch (\Exception $ex) {
			throw new \Exception("Token-Login failed: " . $ex->getMessage());
		}
	}

	/**
	 * Performs a user table query and fetch the result records.
	 *
	 * @param $userTableName       The name of the user table
	 * @param $whereClauseElements Array with fields "ColumnName", "RelationalOperator" and "Value" for filter definition of the query
	 * @param $addColumnHeaders    Add column headers to result?
	 *
	 * @return array The data result as an array
	 */
	public function userTableQuery($userTableName, $whereClauseElements, $addColumnHeaders) {
		try {
			$parameters = [
				"connectionId" => $this->ConnectionId,
				"userTable" => $userTableName,
				"whereClauseElements" => $whereClauseElements,
				"addColumnHeaders" => $addColumnHeaders,
			];
			$result = $this->post("UserTable/UserTableGetRecords", $parameters);
			if (!isset($result->UserTableGetRecordsResult)) {
				throw new \Exception("Unexpected result");
			}

			return $result->UserTableGetRecordsResult;
		}
		catch (\Exception $ex) {
			throw new \Exception("User table query failed: " . $ex->getMessage());
		}
	}

	/**
	 * Uploading a file.
	 *
	 * @param string $file Path and file name of file to upload
	 */
	public function uploadFile($file) {
		$content = file_get_contents($file);

		return $this->uploadContent($content);
	}

	/**
	 * Uploading a stream of data.
	 *
	 * @param Stream $stream Stream of content to upload
	 */
	public function uploadStream($stream) {
		$content = stream_get_contents($stream);

		return $this->uploadContent($content);
	}

	private function uploadContent($content) {
		$base64 = base64_encode((string) $content);
		$uploadParameters = [
			"connectionId" => $this->ConnectionId,
			"fileContentbase64" => $base64,
		];
		$result = $this->post("File/UploadFileBase64", $uploadParameters);

		return $result->UploadFileBase64Result;
	}

	/**
	 * Performing a post request to service.
	 *
	 * @param string $path the route to the API endpoint (without service endpoint url)
	 * @param string Associated array with data to post
	 * @param mixed $data
	 *
	 * @return object Returns object with data. If service returns an error an exception will be thrown with detailed information
	 */
	private function post($path, $data) {
		$ch = curl_init();
		// curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
		curl_setopt($ch, CURLOPT_URL, $this->ServiceEndpoint . $path);
		curl_setopt($ch, CURLOPT_POST, 1);
		curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
		curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
		$jsonResult = curl_exec($ch);
		if (curl_errno($ch)) {
			throw new \Exception("Error on post request: " . curl_errno($ch));
		}

		return $this->handleJsonResult($jsonResult);
	}

	private function handleJsonResult($json) {
		if ($json === false) {
			throw new \Exception("No valid JSON has been returned from service.");
		}
		$result = json_decode((string) $json);
		if (isset($result->ErrorNumber)) {
			throw new \Exception("(" . $result->ErrorNumber . ") " . $result->Message);
		}

		return $result;
	}
}
