<?php

declare(strict_types=1);

namespace mirzaev\huesos\controllers;

// Files of the project
use mirzaev\huesos\controllers\core,
	mirzaev\huesos\models\cart,
	mirzaev\huesos\models\deliveries\cdek,
	mirzaev\huesos\models\enumerations\delivery as company;

// Framework for PHP
use mirzaev\minimal\http\enumerations\content,
	mirzaev\minimal\http\enumerations\protocol,
	mirzaev\minimal\http\request;

// Build-in libraries
use DateTime as datetime;

/**
 * Controller of delivery
 *
 * @package mirzaev\huesos\controllers
 *
 * @param model|null $cart Instance of the cart
 * @param array $errors Registry of errors
 *
 * @method null write(string|null $company, ?string $location, ?string $street) Validate and write delivery data to account and session buffers
 * @method null calculate() Calculate delivery by validated data from buffers
 *
 * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 */
final class delivery extends core
{
	/**
	 * Cart
	 *
	 * @var model|null $cart Instance of the cart
	 */
	protected readonly ?cart $cart;

	/**
	 * Errors
	 *
	 * @var array $errors Registry of errors
	 */
	protected array $errors = [
		'session' => [],
		'account' => [],
		'buffer' => [],
		'delivery' => []
	];

	/**
	 * Write
	 * 
	 * Validate and write delivery data to account and session buffers
	 *
	 * @param string|null $company Name of delivery company
	 * @param string|null $location Receiver location (city)
	 * @param string|null $street Receiver address (street with house)
	 * @param string|int|null $apartament Receiver apartament
	 * @param string|null $type Delivery type ('office', 'door')
	 * @param string|int|null $tariff Delivery tariff
	 * @param string|int|float|null $longitude Receiver longitude
	 * @param string|int|float|null $latitude Receiver latitude
	 *
	 * @return null
	 */
	public function write(
		string|null $company = null,
		?string $location = null,
		?string $street = null,
		string|int|null $apartament = null,
		?string $type = null,
		string|int|null $tariff = null,
		string|int|float|null $longitude = null,
		string|int|float|null $latitude = null
	): null {
		if (str_contains($this->request->headers['accept'], content::json->value)) {
			// Request for JSON response

			// Declaring response buffer
			$response = [
				'ready' => false
			];

			if (isset($company)) {
				// Received company name

				if (mb_strlen($company) < 65) {
					// Validated company name

					// Declating variable for normalized value of company namme
					$normalized = '';

					// Normalizing company name
					if (preg_match('/[\w]+/u', urldecode($company), $matches)) $normalized = $matches[0] ?? '';

					// Deinitializing unnecessary variables
					unset($matches);

					// Writing to the session buffer
					$this->core->request(new request('PATCH', '/session/write', protocol::http_3, parameters: ['delivery_company' => $normalized]));

					// Writing to the account buffer
					$this->core->request(new request('PATCH', '/account/write', protocol::http_3, parameters: ['delivery_company' => $normalized]));

					// Writing to the buffer of the response
					/* $response['company'] = $normalized; */

					// Initializing the cart
					$this->cart ??= $this->account?->cart() ?? $this->session?->cart();

					if ($this->cart instanceof cart) {
						// Initialized the cart

						// Initializing products in the cart
						$products = $this->cart->products(language: $this->language, currency: $this->currency);

						// Declaring buffer of formatted products
						$formatted = [];

						if (!empty($products)) {
							// Initialized products

							foreach ($products as $product) {
								// Iterating over products

								// Formatting products
								for ($i = 0; $i < $product['amount']; ++$i) {
									$formatted[] = [
										'weight' => $product['document']['weight'] ?? 0,
										...$product['document']['dimensions']
									];
								}
							}
						}

						// Deinitializing unnecessary variables
						unset($product);

						// Writing delivery section to the buffer of the response (@todo add check for exists)
						$response['delivery'] = [
							'html' => $this->view->render('cart/elements/deliveries/' . $normalized . '/section.html'),
							'javascript' => match ($normalized) {
								'cdek' => [
									[
										'code' => $this->view->render('cart/elements/deliveries/' . $normalized . '/javascript.html', [
											'formatted' => $formatted
										])
									]
								],
								default => null
							}
						];
					}

					// Initialization buffer of delivery parameters
					$delivery = $this->account?->buffer['delivery'] ?? $this->session?->buffer['delivery'] ?? [];

					// Writing readiness status to the buffer of the response
					$response['ready'] = $this->model::ready(company::{$normalized}, $delivery[$normalized] ?? []);

					if ($response['ready']) {
						// Initialized required parameters

						// Writing to the session buffer
						$this->core->request(new request('PATCH', '/session/write', protocol::http_3, parameters: [$name = 'delivery_' . $normalized . '_ready' => true]));

						// Writing to the account buffer
						$this->core->request(new request('PATCH', '/account/write', protocol::http_3, parameters: [$name => true]));

						// Deinitializing unnecessary variables
						unset($name);
					} else {
						// Not initialized required parameters

						// Writing to the session buffer
						$this->core->request(new request('PATCH', '/session/write', protocol::http_3, parameters: [
							$name_ready = 'delivery_' . $normalized . '_ready' => false,
							$name_cost = 'delivery_' . $normalized . '_cost' => null,
							$name_days = 'delivery_' . $normalized . '_days' => null
						]));

						// Writing to the account buffer
						$this->core->request(new request('PATCH', '/account/write', protocol::http_3, parameters: [
							$name_ready => false,
							$name_cost => null,
							$name_days => null
						]));

						// Deinitializing unnecessary variables
						unset($name_ready, $name_cost, $name_days);
					}

					// Deinitializing unnecessary variables
					unset($delivery, $normalized);

					// Writing status of execution to the buffer of the response
					$response['status'] = 'success';
				}
			} else if (isset($location)) {
				// Received location name

				if (mb_strlen($location) < 257) {
					// Validated location name

					// Declating variable for result value of location name
					$result = '';

					// Declating variable for separated values of location name
					$separated = '';

					// Separating location name 
					if (preg_match_all('/[^\W][\w\s\-]+/u', trim(urldecode($location)), $matches)) $separated = $matches[0];

					// Deinitializing unnecessary variables
					unset($matches);

					// Initialization buffer of delivery parameters
					$delivery = $this->account?->buffer['delivery'] ?? $this->session?->buffer['delivery'] ?? [];

					if (!empty($delivery['company'])) {
						// Initialized delivery company

						if (!empty($separated)) {
							// Serapated location name

							// Declaring variable for normalized separated values of location name
							$normalized = [];

							// Normalizing location name
							foreach ($separated as $value) $normalized[] = mb_ucfirst($value);
							/* foreach ($separated as $value) $normalized[] = $value; */

							// Deinitializing unnecessary variables
							unset($separated, $value);

							// Reinitializing result value of location name
							$result = $normalized[0];

							// Declaring of universalized locations buffer
							$locations = null;

							if ($delivery['company'] === 'cdek') {
								// CDEK

								// Searching for locations by name (first part with spaces before first comma or any non word symbol)
								$cdek = cdek::location($normalized[0], $this->errors['delivery'])?->items;

								foreach ($cdek ?? [] as $location) {
									// Iterating over found locations

									// Initializing structure
									$structure = [$location->country];

									// Initializing additional instances in the structure
									if (!empty($location->region) && $location->region !== $location->city)
										$structure[] = $location->region;
									if (!empty($location->sub_region) && $location->sub_region !== $location->region && $location->sub_region !== $location->city)
										$structure[] = $location->sub_region;

									// Universalizing and writing to locations buffer
									$locations[] = [
										'identifier' => $location->code,
										'name' => $location->city,
										'structure' => $structure,
										'longitude' => $location->longitude,
										'latitude' => $location->latitude,
										'data' => $location
									];
								}

								// Deinitializing of response data from CDEK
								unset($cdek);
							}

							if (!empty($locations)) {
								// Found locations

								// Declaring buffer of validated locations by input values
								$buffer = [];

								// Initialization of 80% of received input values (required minimum to match location characteristics)
								$minimum = count($normalized) * 80 / 100;

								foreach ($locations as $_location) {
									// Iterating over locations

									// Declaring variable with score of matching input values with location characteristics
									$score = 0;

									foreach ([$_location['name'], ...$_location['structure']] as $value) {
										// Iterating over location characteristics

										foreach ($normalized as $_value) {
											// Iterating over normalized parts of location name

											// Match normalized parts of location name with location characteristics using the Levenshtein algorithm
											$match = levenshtein($value, $_value);

											if ($match < 3) {
												// The values are approximately the same

												if (++$score > $minimum) {
													// More than $minimum matches found

													// Writing to buffer of validated locations by normalized parts of location name
													$buffer[] = $_location;

													// Exit from iteration of location characteristics
													break 2;
												}
											}
										}

										// Deinitializing unnecessary variables
										unset($_value);
									}

									// Deinitializing unnecessary variables
									unset($value);
								}

								// Deinitializing unnecessary variables
								unset($_location);

								// Reinitializating locations from buffer of validated locations by input values
								$locations = $buffer;

								/* if (count($locations) === 1)  */
								if (count($locations) > 0) {
									// Identificated location

									// Writing to the session buffer
									$this->core->request(new request('PATCH', '/session/write', protocol::http_3, parameters: [$name = 'delivery_' . $delivery['company'] . '_location' => $locations[0]]));

									// Writing to the account buffer
									$this->core->request(new request('PATCH', '/account/write', protocol::http_3, parameters: [$name => $locations[0]]));

									// Deinitializing unnecessary variables
									unset($name);

									// Writing result value of location name
									$result = $locations[0]['name'] . ', ' . implode(', ', array_reverse($locations[0]['structure']));

									// Writing to the actual buffer copy
									$delivery[$delivery['company']]['location'] = $result;

									// Writing location data to the buffer of the response
									/* $response['location'] = [
											'identifier' => $locations[0]['identifier'],
											'input' => $result
										]; */

									// Writing readiness status to the buffer of the response
									$response['ready'] = $this->model::ready(company::{$delivery['company']}, $delivery[$delivery['company']] ?? []);

									if ($response['ready']) {
										// Initialized required parameters

										// Writing to the session buffer
										$this->core->request(new request('PATCH', '/session/write', protocol::http_3, parameters: [$name = 'delivery_' . $delivery['company'] . '_ready' => true]));

										// Writing to the account buffer
										$this->core->request(new request('PATCH', '/account/write', protocol::http_3, parameters: [$name => true]));

										// Deinitializing unnecessary variables
										unset($name);
									} else {
										// Not initialized required parameters

										// Writing to the session buffer
										$this->core->request(new request('PATCH', '/session/write', protocol::http_3, parameters: [
											$name_ready = 'delivery_' . $delivery['company'] . '_ready' => false,
											$name_cost = 'delivery_' . $delivery['company'] . '_cost' => null,
											$name_days = 'delivery_' . $delivery['company'] . '_days' => null
										]));

										// Writing to the account buffer
										$this->core->request(new request('PATCH', '/account/write', protocol::http_3, parameters: [
											$name_ready => false,
											$name_cost => null,
											$name_days => null
										]));

										// Deinitializing unnecessary variables
										unset($name_ready, $name_cost, $name_days);
									}

									// Deinitializing unnecessary variables
									unset($delivery);

									// Writing locations into response buffer
									$response['locations'] = [];

									// Writing status of execution to the buffer of the response
									$response['status'] = 'success';
								} else {
									// Not identificated location

									// Writing to the session buffer
									$this->core->request(new request('PATCH', '/session/write', protocol::http_3, parameters: [
										$name_location = 'delivery_' . $delivery['company'] . '_location' => $result,
										$name_ready = 'delivery_' . $delivery['company'] . '_ready' => false,
										$name_cost = 'delivery_' . $delivery['company'] . '_cost' => null,
										$name_days = 'delivery_' . $delivery['company'] . '_days' => null
									]));

									// Writing to the account buffer
									$this->core->request(new request('PATCH', '/account/write', protocol::http_3, parameters: [
										$name_location => $result,
										$name_ready => false,
										$name_cost => null,
										$name_days => null
									]));

									// Deinitializing unnecessary variables
									unset($name_location, $name_ready, $name_cost, $name_days);

									// Writing status of execution to the buffer of the response (???)
									$response['status'] = 'success';

									// Declaring buffer of data to send
									$buffer = [];

									foreach ($locations as $_location) {
										// Iterating over locations

										// Writing to buffer of data to send
										$buffer[] = [
											'name' => $_location['name'],
											'structure' => $_location['structure']
										];
									}

									// Deinitializing unnecessary variables
									unset($_location);

									// Writing locations into response buffer
									$response['locations'] = $buffer;

									// Deinitializing buffer of data to send
									unset($buffer);
								}
							} else {
								// Not found locations

								// Writing to the session buffer
								$this->core->request(new request('PATCH', '/session/write', protocol::http_3, parameters: [
									$name_location = 'delivery_' . $delivery['company'] . '_location' => null,
									$name_ready = 'delivery_' . $delivery['company'] . '_ready' => false,
									$name_cost = 'delivery_' . $delivery['company'] . '_cost' => null,
									$name_days = 'delivery_' . $delivery['company'] . '_days' => null
								]));

								// Writing to the account buffer
								$this->core->request(new request('PATCH', '/account/write', protocol::http_3, parameters: [
									$name_location => null,
									$name_ready => false,
									$name_cost => null,
									$name_days => null
								]));

								// Deinitializing unnecessary variables
								unset($name_location, $name_ready, $name_cost, $name_days);
							}

							// Deinitializing unnecessary variables
							unset($normalized);
						} else {
							// Value is empty after separating 

							// Not initialized required parameters

							// Writing to the session buffer
							$this->core->request(new request('PATCH', '/session/write', protocol::http_3, parameters: [
								$name_location = 'delivery_' . $delivery['company'] . '_location' => null,
								$name_ready = 'delivery_' . $delivery['company'] . '_ready' => false,
								$name_cost = 'delivery_' . $delivery['company'] . '_cost' => null,
								$name_days = 'delivery_' . $delivery['company'] . '_days' => null
							]));

							// Writing to the account buffer
							$this->core->request(new request('PATCH', '/account/write', protocol::http_3, parameters: [
								$name_location => null,
								$name_ready => false,
								$name_cost => null,
								$name_days => null
							]));

							// Deinitializing unnecessary variables
							unset($name_location, $name_ready, $name_cost, $name_days);
						}
					}

					// Deinitializing unnecessary variables
					unset($delivery, $normalized);

					// Writing to the session buffer
					$this->core->request(new request('PATCH', '/session/write', protocol::http_3, parameters: ['delivery_location' => $result]));

					// Writing to the account buffer
					$this->core->request(new request('PATCH', '/account/write', protocol::http_3, parameters: ['delivery_location' => $result]));

					// Deinitializing unnecessary variables
					unset($location, $result, $normalized);
				}
			} else if (isset($street)) {
				// Received sreet

				if (mb_strlen($street) < 129) {
					// Validated street

					// Declating variable for normalized value of the parameter
					$normalized = '';

					// Normalizing street
					if (preg_match('/[\w\d\s\.\,\-]+/u', urldecode($street), $matches)) $normalized = mb_ucfirst($matches[0] ?? '');

					// Deinitializing unnecessary variables
					unset($matches);

					// Initialization buffer of delivery parameters
					$delivery = $this->account?->buffer['delivery'] ?? $this->session?->buffer['delivery'] ?? [];

					if (isset($delivery['company'])) {
						// Initialized delivery company

						// Writing to the session buffer
						$this->core->request(new request('PATCH', '/session/write', protocol::http_3, parameters: [$name = 'delivery_' . $delivery['company'] . '_street' => $normalized]));

						// Writing to the account buffer
						$this->core->request(new request('PATCH', '/account/write', protocol::http_3, parameters: [$name => $normalized]));

						// Deinitializing unnecessary variables
						unset($name);

						// Writing to the actual buffer copy
						$delivery[$delivery['company']]['street'] = $normalized;

						// Writing readiness status to the buffer of the response
						$response['ready'] = $this->model::ready(company::{$delivery['company']}, $delivery[$delivery['company']] ?? []);

						if ($response['ready']) {
							// Initialized required parameters

							// Writing to the session buffer
							$this->core->request(new request('PATCH', '/session/write', protocol::http_3, parameters: [$name = 'delivery_' . $delivery['company'] . '_ready' => true]));

							// Writing to the account buffer
							$this->core->request(new request('PATCH', '/account/write', protocol::http_3, parameters: [$name => true]));

							// Deinitializing unnecessary variables
							unset($name);
						} else {
							// Not initialized required parameters

							// Writing to the session buffer
							$this->core->request(new request('PATCH', '/session/write', protocol::http_3, parameters: [
								$name_ready = 'delivery_' . $delivery['company'] . '_ready' => false,
								$name_cost = 'delivery_' . $delivery['company'] . '_cost' => null,
								$name_days = 'delivery_' . $delivery['company'] . '_days' => null
							]));

							// Writing to the account buffer
							$this->core->request(new request('PATCH', '/account/write', protocol::http_3, parameters: [
								$name_ready => false,
								$name_cost => null,
								$name_days => null
							]));

							// Deinitializing unnecessary variables
							unset($name_ready, $name_cost, $name_days);
						}

						// Deinitializing unnecessary variables
						unset($delivery);

						// Writing status of execution to the buffer of the response
						$response['status'] = 'success';
					}

					// Deinitializing unnecessary variables
					unset($delivery);

					// Writing to the session buffer
					$this->core->request(new request('PATCH', '/session/write', protocol::http_3, parameters: ['delivery_street' => $normalized]));

					// Writing to the account buffer
					$this->core->request(new request('PATCH', '/account/write', protocol::http_3, parameters: ['delivery_street' => $normalized]));

					// Writing street to the buffer of the response
					/* $response['street'] = $normalized; */

					// Deinitializing unnecessary variables
					unset($normalized);
				}
			} else if (isset($type)) {
				// Received delivery type

				if (mb_strlen($type) < 30) {
					// Validated delivery type

					// Declating variable for result value of delivery type
					$result = '';

					// Declating variable for normalized value of delivery type
					$normalized = '';

					// Normalizing delivery type 
					if (preg_match('/[\w]+/', trim(urldecode($type)), $matches)) $normalized = $matches[0];

					// Deinitializing unnecessary variables
					unset($matches);

					// Initialization buffer of delivery parameters
					$delivery = $this->account?->buffer['delivery'] ?? $this->session?->buffer['delivery'] ?? [];

					if (isset($delivery['company'])) {
						// Initialized delivery company

						// Writing to the session buffer
						$this->core->request(new request('PATCH', '/session/write', protocol::http_3, parameters: [$name = 'delivery_' . $delivery['company'] . '_type' => $normalized]));

						// Writing to the account buffer
						$this->core->request(new request('PATCH', '/account/write', protocol::http_3, parameters: [$name => $normalized]));

						// Deinitializing unnecessary variables
						unset($name);

						// Writing to the actual buffer copy
						$delivery[$delivery['company']]['type'] = $normalized;

						// Writing result value of delivery type
						$result = $normalized;

						// Deinitializing unnecessary variables
						unset($normalized);

						// Writing readiness status to the buffer of the response
						$response['ready'] = $this->model::ready(company::{$delivery['company']}, $delivery[$delivery['company']] ?? []);

						if ($response['ready']) {
							// Initialized required parameters

							// Writing to the session buffer
							$this->core->request(new request('PATCH', '/session/write', protocol::http_3, parameters: [$name = 'delivery_' . $delivery['company'] . '_ready' => true]));

							// Writing to the account buffer
							$this->core->request(new request('PATCH', '/account/write', protocol::http_3, parameters: [$name => true]));

							// Deinitializing unnecessary variables
							unset($name);
						} else {
							// Not initialized required parameters

							// Writing to the session buffer
							$this->core->request(new request('PATCH', '/session/write', protocol::http_3, parameters: [
								$name_ready = 'delivery_' . $delivery['company'] . '_ready' => false,
								$name_cost = 'delivery_' . $delivery['company'] . '_cost' => null,
								$name_days = 'delivery_' . $delivery['company'] . '_days' => null
							]));

							// Writing to the account buffer
							$this->core->request(new request('PATCH', '/account/write', protocol::http_3, parameters: [
								$name_ready => false,
								$name_cost => null,
								$name_days => null
							]));

							// Deinitializing unnecessary variables
							unset($name_ready, $name_cost, $name_days);
						}

						// Deinitializing unnecessary variables
						unset($delivery);

						// Writing status of execution to the buffer of the response
						$response['status'] = 'success';
					}

					// Deinitializing unnecessary variables
					unset($delivery, $result, $normalized);
				}
			} else if (isset($tariff)) {
				// Received delivery tariff

				if ((is_string($tariff) && mb_strlen($tariff) < 30) || (is_int($tariff && $tariff < 10000))) {
					// Validated delivery tariff

					// Declating variable for result value of delivery tariff
					$result = '';

					// Declating variable for normalized value of delivery tariff
					$normalized = '';

					// Normalizing delivery tariff 
					if (preg_match('/[\w\d]+/', trim(urldecode($tariff)), $matches)) $normalized = $matches[0];

					// Deinitializing unnecessary variables
					unset($matches);

					// Initialization buffer of delivery parameters
					$delivery = $this->account?->buffer['delivery'] ?? $this->session?->buffer['delivery'] ?? [];

					if (isset($delivery['company'])) {
						// Initialized delivery company

						// Writing to the session buffer
						$this->core->request(new request('PATCH', '/session/write', protocol::http_3, parameters: [$name = 'delivery_' . $delivery['company'] . '_tariff' => $normalized]));

						// Writing to the account buffer
						$this->core->request(new request('PATCH', '/account/write', protocol::http_3, parameters: [$name => $normalized]));

						// Deinitializing unnecessary variables
						unset($name);

						// Writing to the actual buffer copy
						$delivery[$delivery['company']]['tariff'] = $normalized;

						// Writing result value of delivery tariff
						$result = $normalized;

						// Deinitializing unnecessary variables
						unset($normalized);

						// Writing readiness status to the buffer of the response
						$response['ready'] = $this->model::ready(company::{$delivery['company']}, $delivery[$delivery['company']] ?? []);

						if ($response['ready']) {
							// Initialized required parameters

							// Writing to the session buffer
							$this->core->request(new request('PATCH', '/session/write', protocol::http_3, parameters: [$name = 'delivery_' . $delivery['company'] . '_ready' => true]));

							// Writing to the account buffer
							$this->core->request(new request('PATCH', '/account/write', protocol::http_3, parameters: [$name => true]));

							// Deinitializing unnecessary variables
							unset($name);
						} else {
							// Not initialized required parameters

							// Writing to the session buffer
							$this->core->request(new request('PATCH', '/session/write', protocol::http_3, parameters: [
								$name_ready = 'delivery_' . $delivery['company'] . '_ready' => false,
								$name_cost = 'delivery_' . $delivery['company'] . '_cost' => null,
								$name_days = 'delivery_' . $delivery['company'] . '_days' => null
							]));

							// Writing to the account buffer
							$this->core->request(new request('PATCH', '/account/write', protocol::http_3, parameters: [
								$name_ready => false,
								$name_cost => null,
								$name_days => null
							]));

							// Deinitializing unnecessary variables
							unset($name_ready, $name_cost, $name_days);
						}

						// Deinitializing unnecessary variables
						unset($delivery);

						// Writing status of execution to the buffer of the response
						$response['status'] = 'success';
					}

					// Deinitializing unnecessary variables
					unset($delivery, $result, $normalized);
				}
			} else if (isset($longitude)) {
				// Received delivery receiver longitude

				// Declating variable for result value of delivery receiver longitude
				$result = '';

				// Declating variable for normalized value of delivery receiver longitude
				$normalized = '';

				// Normalizing receiver longitude 
				$normalized = filter_var($longitude, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);

				if ($normalized >= -180 && $normalized <= 180) {
					// Validated delivery receiver longitude

					// Initialization buffer of delivery parameters
					$delivery = $this->account?->buffer['delivery'] ?? $this->session?->buffer['delivery'] ?? [];

					if (isset($delivery['company'])) {
						// Initialized delivery company

						// Writing to the session buffer
						$this->core->request(new request('PATCH', '/session/write', protocol::http_3, parameters: [$name = 'delivery_' . $delivery['company'] . '_longitude' => $normalized]));

						// Writing to the account buffer
						$this->core->request(new request('PATCH', '/account/write', protocol::http_3, parameters: [$name => $normalized]));

						// Deinitializing unnecessary variables
						unset($name);

						// Writing to the actual buffer copy
						$delivery[$delivery['company']]['longitude'] = $normalized;

						// Writing result value of delivery receiver longitude
						$result = $normalized;

						// Deinitializing unnecessary variables
						unset($normalized);

						// Writing readiness status to the buffer of the response
						$response['ready'] = $this->model::ready(company::{$delivery['company']}, $delivery[$delivery['company']] ?? []);

						if ($response['ready']) {
							// Initialized required parameters

							// Writing to the session buffer
							$this->core->request(new request('PATCH', '/session/write', protocol::http_3, parameters: [$name = 'delivery_' . $delivery['company'] . '_ready' => true]));

							// Writing to the account buffer
							$this->core->request(new request('PATCH', '/account/write', protocol::http_3, parameters: [$name => true]));

							// Deinitializing unnecessary variables
							unset($name);
						} else {
							// Not initialized required parameters

							// Writing to the session buffer
							$this->core->request(new request('PATCH', '/session/write', protocol::http_3, parameters: [
								$name_ready = 'delivery_' . $delivery['company'] . '_ready' => false,
								$name_cost = 'delivery_' . $delivery['company'] . '_cost' => null,
								$name_days = 'delivery_' . $delivery['company'] . '_days' => null
							]));

							// Writing to the account buffer
							$this->core->request(new request('PATCH', '/account/write', protocol::http_3, parameters: [
								$name_ready => false,
								$name_cost => null,
								$name_days => null
							]));

							// Deinitializing unnecessary variables
							unset($name_ready, $name_cost, $name_days);
						}

						// Deinitializing unnecessary variables
						unset($delivery);

						// Writing status of execution to the buffer of the response
						$response['status'] = 'success';
					}

					// Deinitializing unnecessary variables
					unset($delivery, $result, $normalized);
				}
			} else if (isset($latitude)) {
				// Received delivery receiver latitude

				// Declating variable for result value of delivery receiver latitude
				$result = '';

				// Declating variable for normalized value of delivery receiver latitude
				$normalized = '';

				// Normalizing receiver latitude 
				$normalized = filter_var($latitude, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);

				if ($normalized >= -90 && $normalized <= 90) {
					// Validated delivery receiver latitude

					// Initialization buffer of delivery parameters
					$delivery = $this->account?->buffer['delivery'] ?? $this->session?->buffer['delivery'] ?? [];

					if (isset($delivery['company'])) {
						// Initialized delivery company

						// Writing to the session buffer
						$this->core->request(new request('PATCH', '/session/write', protocol::http_3, parameters: [$name = 'delivery_' . $delivery['company'] . '_latitude' => $normalized]));

						// Writing to the account buffer
						$this->core->request(new request('PATCH', '/account/write', protocol::http_3, parameters: [$name => $normalized]));

						// Deinitializing unnecessary variables
						unset($name);

						// Writing to the actual buffer copy
						$delivery[$delivery['company']]['latitude'] = $normalized;

						// Writing result value of delivery receiver latitude
						$result = $normalized;

						// Deinitializing unnecessary variables
						unset($normalized);

						// Writing readiness status to the buffer of the response
						$response['ready'] = $this->model::ready(company::{$delivery['company']}, $delivery[$delivery['company']] ?? []);

						if ($response['ready']) {
							// Initialized required parameters

							// Writing to the session buffer
							$this->core->request(new request('PATCH', '/session/write', protocol::http_3, parameters: [$name = 'delivery_' . $delivery['company'] . '_ready' => true]));

							// Writing to the account buffer
							$this->core->request(new request('PATCH', '/account/write', protocol::http_3, parameters: [$name => true]));

							// Deinitializing unnecessary variables
							unset($name);
						} else {
							// Not initialized required parameters

							// Writing to the session buffer
							$this->core->request(new request('PATCH', '/session/write', protocol::http_3, parameters: [
								$name_ready = 'delivery_' . $delivery['company'] . '_ready' => false,
								$name_cost = 'delivery_' . $delivery['company'] . '_cost' => null,
								$name_days = 'delivery_' . $delivery['company'] . '_days' => null
							]));

							// Writing to the account buffer
							$this->core->request(new request('PATCH', '/account/write', protocol::http_3, parameters: [
								$name_ready => false,
								$name_cost => null,
								$name_days => null
							]));

							// Deinitializing unnecessary variables
							unset($name_ready, $name_cost, $name_days);
						}

						// Deinitializing unnecessary variables
						unset($delivery);

						// Writing status of execution to the buffer of the response
						$response['status'] = 'success';
					}

					// Deinitializing unnecessary variables
					unset($delivery, $result, $normalized);
				}
			} else if (isset($apartament)) {
				// Received delivery receiver apartament

				// Declating variable for result value of delivery receiver apartament
				$result = '';

				// Declating variable for normalized value of delivery receiver apartament
				$normalized = '';

				// Normalizing receiver apartament 
				$normalized = filter_var($apartament, FILTER_SANITIZE_NUMBER_INT, FILTER_FLAG_ALLOW_FRACTION);

				if ($normalized > 0 && $normalized <= 1000) {
					// Validated delivery receiver apartament

					// Initialization buffer of delivery parameters
					$delivery = $this->account?->buffer['delivery'] ?? $this->session?->buffer['delivery'] ?? [];

					if (isset($delivery['company'])) {
						// Initialized delivery company

						// Writing to the session buffer
						$this->core->request(new request('PATCH', '/session/write', protocol::http_3, parameters: [$name = 'delivery_' . $delivery['company'] . '_apartament' => $normalized]));

						// Writing to the account buffer
						$this->core->request(new request('PATCH', '/account/write', protocol::http_3, parameters: [$name => $normalized]));

						// Deinitializing unnecessary variables
						unset($name);

						// Writing to the actual buffer copy
						$delivery[$delivery['company']]['apartment'] = $normalized;

						// Writing result value of delivery receiver apartament
						$result = $normalized;

						// Deinitializing unnecessary variables
						unset($normalized);

						// Writing readiness status to the buffer of the response
						$response['ready'] = $this->model::ready(company::{$delivery['company']}, $delivery[$delivery['company']] ?? []);

						if ($response['ready']) {
							// Initialized required parameters

							// Writing to the session buffer
							$this->core->request(new request('PATCH', '/session/write', protocol::http_3, parameters: [$name = 'delivery_' . $delivery['company'] . '_ready' => true]));

							// Writing to the account buffer
							$this->core->request(new request('PATCH', '/account/write', protocol::http_3, parameters: [$name => true]));

							// Deinitializing unnecessary variables
							unset($name);
						} else {
							// Not initialized required parameters

							// Writing to the session buffer
							$this->core->request(new request('PATCH', '/session/write', protocol::http_3, parameters: [
								$name_ready = 'delivery_' . $delivery['company'] . '_ready' => false,
								$name_cost = 'delivery_' . $delivery['company'] . '_cost' => null,
								$name_days = 'delivery_' . $delivery['company'] . '_days' => null
							]));

							// Writing to the account buffer
							$this->core->request(new request('PATCH', '/account/write', protocol::http_3, parameters: [
								$name_ready => false,
								$name_cost => null,
								$name_days => null
							]));

							// Deinitializing unnecessary variables
							unset($name_ready, $name_cost, $name_days);
						}

						// Deinitializing unnecessary variables
						unset($delivery);

						// Writing status of execution to the buffer of the response
						$response['status'] = 'success';
					}

					// Deinitializing unnecessary variables
					unset($delivery, $location, $result, $normalized);
				}
			}

			// Sending response
			$this->response
				->start()
				->clean()
				->sse()
				->json($response + [
					'errors' => $this->errors
				])
				->validate($this->request)
				?->body()
				->end();

			// Deinitializing response buffer
			unset($response);
		}

		// Exit (success/fail)
		return null;
	}

	/**
	 * Calculate
	 * 
	 * Calculate delivery by validated data from buffers
	 * and write to the cart buffer
	 *
	 * @return null
	 */
	public function calculate(): null
	{
		if (str_contains($this->request->headers['accept'], content::json->value)) {
			// Request for JSON response

			// Initialization buffer of delivery parameters
			$delivery = $this->account?->buffer['delivery'] ?? $this->session?->buffer['delivery'] ?? [];

			if (isset($delivery['company'])) {
				// Initialized delivery company

				// Declaring response buffer
				$response = [];

				if (!empty($delivery[$delivery['company']]['location']) && !empty($delivery[$delivery['company']]['street'])) {
					// Required parameters initialized: company, location, street

					if ($delivery['company'] === 'cdek') {
						// Delivery by CDEK

						// Initializing the cart
						$this->cart ??= $this->account?->cart() ?? $this->session?->cart();

						// Initializing products in the cart
						$products = $this->cart?->products(language: $this->language, currency: $this->currency);

						if (!empty($products)) {
							// Initialized products

							// Declaring buffer of formatted products
							$formatted = [];

							foreach ($products as $product) {
								// Iterating over products

								// Formatting products
								for ($i = 0; $i < $product['amount']; ++$i) {
									$formatted[] = [
										'weight' => $product['document']['weight'] ?? 0,
										...$product['document']['dimensions']
									];
								}
							}

							// Deinitializing unnecessary variables
							unset($products, $product);

							if (!empty($formatted)) {
								// Formatted products

								// Calculating delivery by validated data from buffers
								$cdek = @cdek::calculate(
									from_location: 248,
									from_street: 'Екатерининская 116', // @todo issues #13 
									to_location: $delivery[$delivery['company']]['location']['identifier'],
									to_street: $delivery[$delivery['company']]['street'],
									tariff: $delivery[$delivery['company']]['tariff'] ?? 368,
									products: $formatted,
									date: new datetime(), // @todo weekdays only? + timezones
									errors: $this->errors['delivery']
								);

								// Deinitializing unnecessary variables
								unset($formatted);

								if ($cdek) {
									// Calculated delivery 

									// Writing to the session buffer
									$this->core->request(
										new request(
											'PATCH',
											'/session/write',
											protocol::http_3,
											parameters: [
												'delivery_' . $delivery['company'] . '_cost' => $cdek->total_sum,
												'delivery_' . $delivery['company'] . '_days' => $cdek->calendar_max
											]
										)
									);

									// Writing to the account buffer
									$this->core->request(
										new request(
											'PATCH',
											'/account/write',
											protocol::http_3,
											parameters: [
												'delivery_' . $delivery['company'] . '_cost' => $cdek->total_sum,
												'delivery_' . $delivery['company'] . '_days' => $cdek->calendar_max
											]
										)
									);

									// Writing to the cart buffer
									$this->cart->buffer_write(['delivery' => [
										'company' => 'cdek',
										'location' => [
											'identifier' => $delivery[$delivery['company']]['location']['identifier'],
											'name' => $delivery[$delivery['company']]['location']['name'],
										],
										'street' => $delivery[$delivery['company']]['street'],
										'cost' => $cdek->total_sum,
										'days' => $cdek->calendar_max,
										'type' => $delivery[$delivery['company']]['type'] ?? 368,
									]], $this->errors['buffer']);

									// Writing to response buffer
									$response['cost'] = $cdek->total_sum;
									$response['days'] = $cdek->calendar_max;
									$response['longitude'] = $delivery[$delivery['company']]['location']['data']['longitude'];
									$response['latitude'] = $delivery[$delivery['company']]['location']['data']['latitude'];
								}
							}

							// Deinitializing unnecessary variables
							unset($formatted);
						}
					}
				}

				// Sending response
				$this->response
					->start()
					->clean()
					->sse()
					->json($response + [
						'errors' => $this->errors
					])
					->validate($this->request)
					?->body()
					->end();

				// Deinitializing response buffer
				unset($response);
			}

			// Deinitializing unnecessary variables
			unset($delivery);
		}

		// Exit (success/fail)
		return null;
	}
}
