<?php

declare(strict_types=1);

namespace mirzaev\arming_bot\controllers;

// Files of the project
use mirzaev\arming_bot\controllers\core,
	mirzaev\arming_bot\models\session as model,
	mirzaev\arming_bot\models\account;

// Framework for ArangoDB
use mirzaev\arangodb\document;

/**
 * Controller of session
 *
 * @package mirzaev\arming_bot\controllers
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 */
final class session extends core
{
	/**
	 * Registry of errors
	 */
	protected array $errors = [
		'session' => [],
		'account' => []
	];

	/**
	 * Connect session to the telegram account
	 *
	 * @param array $parameters Parameters of the request (POST + GET)
	 */
	public function telegram(array $parameters = []): ?string
	{
		if ($_SERVER['REQUEST_METHOD'] === 'POST') {
			// POST request

			// Declaring variables in the correct scope
			$identifier = $domain = $language = null;

			if ($connected = isset($this->account)) {
				// Found the account

				// Initializing identifier of the account
				$identifier = $this->account->identifier;

				// Initializing language of the account
				$language = $this->account->language;

				// Initializing domain of the account
				$domain = $this->account->domain;
			} else {
				// Not found the account

				if (count($parameters) > 1 && isset($parameters['hash'])) {

					$buffer = $parameters;

					unset($buffer['hash']);
					ksort($buffer);

					$prepared = [];
					foreach ($buffer as $key => $value) {
						if (is_array($value)) {
							$prepared[] = $key . '=' . json_encode($value, JSON_UNESCAPED_UNICODE);
						} else {
							$prepared[] = $key . '=' . $value;
						}
					}

					$key = hash_hmac('sha256', require(SETTINGS . DIRECTORY_SEPARATOR . 'key.php'), 'WebAppData', true);
					$hash = bin2hex(hash_hmac('sha256', implode(PHP_EOL, $prepared), $key, true));

					if (hash_equals($hash, $parameters['hash'])) {
						// Data confirmed (according to telegram documentation)

						if (time() - $parameters['auth_date'] < 86400) {
							// Authorization date less than 1 day ago

							// Initializing data of the account
							$data = json_decode($parameters['user']);

							// Initializing of the account
							$account = account::initialize(
								$data->id,
								[
									'identifier' => $data->id,
									'name' => [
										'first' => $data->first_name,
										'last' => $data->last_name
									],
									'domain' => $data->username,
									'language' => $data->language_code,
									'messages' => $data->allows_write_to_pm
								],
								$this->errors['account']
							);

							if ($account instanceof account) {
								// Initialized the account

								// Connecting the account to the session
								$connected = $this->session->connect($account, $this->errors['session']);

								// Initializing identifier of the account
								$identifier = $account->identifier;

								// Initializing language of the account
								$language = $account->language;

								// Initializing domain of the account
								$domain = $account->domain;
							}
						}
					}
				}
			}

			// Initializing a response headers
			header('Content-Type: application/json');
			header('Content-Encoding: none');
			header('X-Accel-Buffering: no');

			// Initializing of the output buffer
			ob_start();

			// Generating the reponse
			echo json_encode(
				[
					'connected' => (bool) $connected,
					'identifier' => $identifier ?? null,
					'domain' => $domain ?? null,
					'language' => $language?->name ?? null,
					'errors' => $this->errors
				]
			);

			// Initializing a response headers
			header('Content-Length: ' . ob_get_length());

			// Sending and deinitializing of the output buffer
			ob_end_flush();
			flush();

			// Exit (success)
			return null;
		}

		// Exit (fail)
		return null;
	}

	/**
	 * Write to the buffer
	 *
	 * @param array $parameters Parameters of the request (POST + GET)
	 *
	 * @return void
	 */
	public function write(array $parameters = []): void
	{
		if (!empty($parameters) && $this->session instanceof model) {
			// Found data of the program and active session

			foreach ($parameters as $name => $value) {
				// Iterate over parameters

				// Validation of the parameter
				if (mb_strlen($value) > 4096) continue;

				// Convert name to multidimensional array
				foreach (array_reverse(explode('_', $name)) as $key) $parameter = [$key => $parameter ?? json_validate($value) ? json_decode($value, true, 10) : $value];

				// Write data of to the buffer parameter in the implement object of session document from ArangoDB
				$this->session->buffer = $parameter + ($this->session->buffer ?? []);
			}

			// Write from implement object to session document from ArangoDB
			document::update($this->session->__document(), $this->errors['session']);
		}
	}
}
