<?php

declare(strict_types=1);

namespace mirzaev\huesos\controllers;

// Files of the project
use mirzaev\huesos\controllers\core,
	mirzaev\huesos\models\cart as model,
	mirzaev\huesos\models\product,
	mirzaev\huesos\models\menu,
	mirzaev\huesos\models\enumerations\language;

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

/**
 * Controller of cart
 *
 * @package mirzaev\huesos\controllers
 *
 * @param model|null $cart Instance of the cart
 * @param array $errors Registry of errors
 *
 * @method null index() HTML-document with shopping cart and delivery settings
 * @method null product(int|string|null $identifier, ?string $type, int|string|null $amount) Change status of the product in the cart
 * @method null summary() Information about products in the cart
 *
 * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 */
final class cart extends core
{
	/**
	 * Cart
	 *
	 * @var model|null $cart Instance of the cart
	 */
	protected readonly ?model $cart;

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

	/**
	 * Index
	 *
	 * HTML-document with shopping cart and delivery settings
	 *
	 * @param null
	 */
	public function index(): null
	{
		if (isset($menu)) {
			// 

		} else {
			// Not received ... menu

			// Search for filters and write to the buffer of global variables of view templater
			$this->view->menu = menu::_read(
				return: 'MERGE(d, { name: d.name.@language })',
				sort: 'd.style.order ASC, d.created DESC, d._key DESC',
				amount: 4,
				parameters: ['language' => $this->language->name],
				errors: $this->errors['menu']
			);

			// Universalizing
			if ($this->view->menu instanceof _document) $this->view->menu = [$this->view->menu];
		}

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

		// Initializing the cart data
		$this->view->cart = [
			'summary' => $this->cart?->summary(currency: $this->currency),
			'products' => $this->cart?->products(language: $this->language, currency: $this->currency)
		];

		// Initializing types of avaiabld deliveries
		$this->view->deliveries = [
			'cdek' => [
				'label' => 'CDEK'
			]
		];

		if (str_contains($this->request->headers['accept'], content::json->value)) {
			// Request for JSON response

			// Sending response
			$this->response
				->start()
				->clean()
				->sse()
				->json([
					'main' => '',
					'errors' => $this->errors
				])
				->validate($this->request)
				?->body()
				->end();
		} else if (str_contains($this->request->headers['accept'], content::any->value)) {
			// Request for any response

			// Render page
			$page = $this->view->render('cart/page.html', [
				'h2' => $this->language === language::ru ? 'Корзина' : 'Cart' // @see https://git.mirzaev.sexy/mirzaev/huesos/issues/1
			]);

			// Sending response
			$this->response
				->start()
				->clean()
				->sse()
				->write($page)
				->validate($this->request)
				?->body()
				->end();

			// Deinitializing rendered page
			unset($page);
		}

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

	/**
	 * Product
	 *
	 * Change status of the product in the cart
	 *
	 * @param int|string|null $identifier Product identifier
	 * @param string|null $type Action type (toggle, write, delete, set)
	 * @param int|string|null $amount Amount of actions with the product (for write, delete and differently used by set)
	 *
	 * @return null
	 *
	 * @todo
	 * 1. Add a limit on adding products to the cart based on the number of products in stock
	 */
	public function product(
		int|string|null $identifier = null,
		?string $type = null,
		int|string|null $amount = null
	): null {
		if (str_contains($this->request->headers['accept'], content::json->value)) {
			// Request for JSON response

			// Declaring buffer with amount of the product in the cart
			$cart = 0;

			// Validating @todo add throwing errors
			if (isset($identifier) && preg_match('/[\d]+/', urldecode($identifier), $matches)) $identifier = (int) $matches[0];
			else unset($identifier);

			if (isset($identifier)) {
				// Received and validated identfier of the product

				// Search for the product
				$product = product::read(
					filter: "d.identifier == @identifier && d.deleted != true && d.hidden != true",
					sort: 'd.created DESC',
					amount: 1,
					parameters: ['identifier' => $identifier],
					errors: $this->errors['cart']
				);

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

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

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

						// Initializing buffer with amount of the product in the cart 
						$cart = $this->cart->count(product: $product, limit: 100, errors: $this->errors['cart']) ?? 0;

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

							// Validating @todo add throwing errors
							if (isset($type) && preg_match('/[\w]+/', urldecode($type), $matches)) $type = $matches[0];
							else unset($type);

							if (isset($type)) {
								// Received and validated type of action with the product

								if ($type === 'toggle') {
									// Write the product to the cart if is not in the cart and vice versa

									if ($cart > 0) {
										// The cart has the product

										// Deleting the product from the cart
										$this->cart->delete(product: $product, amount: $cart, errors: $this->errors['cart']);

										// Reinitializing the buffer with amount of the product in the cart
										$cart = 0;
									} else {
										// The cart has no the product

										// Writing the product to the cart
										$this->cart->write(product: $product, amount: 1, errors: $this->errors['cart']);

										// Reinitializing the buffer with amount of the product in the cart
										$cart = 1;
									}
								} else {
									// Received not the "toggle" command

									// Validating @todo add throwing errors
									if (isset($amount) && preg_match('/[\d]+/', urldecode($amount), $matches)) $amount = (int) $matches[0];
									else unset($amount);

									if (isset($amount)) {
										// Received and validated amount parameter for action with the product

										if ($type === 'write') {
											// Increase amount of the product in the cart

											if ($cart + $amount < 101) {
												// Validated amount to wrting

												// Writing the product to the cart
												$this->cart->write(product: $product, amount: $amount, errors: $this->errors['cart']);

												// Reinitialize the buffer with amount of the product in the cart
												$cart += $amount;
											}
										} else if ($type === 'delete') {
											// Decrease amount of the product in the cart

											if ($cart - $amount > -1) {
												// Validated amount to deleting

												// Deleting the product from the cart
												$this->cart->delete(product: $product, amount: $amount, errors: $this->errors['cart']);

												// Reinitialize the buffer with amount of the product in the cart
												$cart -= $amount;
											}
										} else if ($type === 'set') {
											// Set amount of the product in the cart

											if ($amount > -1 && $amount < 101) {
												// Validated amount to setting

												if ($amount > $cart) {
													// Requested amount more than actual amount of the product in the cart

													// Writing the product to the cart
													$this->cart->write(product: $product, amount: $amount - $cart, errors: $this->errors['cart']);
												} else {
													// Requested amount less than actual amount of the product in the cart

													// Deleting the product from the cart
													$this->cart->delete(product: $product, amount: $cart - $amount, errors: $this->errors['cart']);
												}

												// Reinitializing the buffer with amount of the product in the cart
												$cart = $amount;
											}
										}
									}
								}
							}
						}
					}
				}
			}

			// Sending response
			$this->response
				->start()
				->clean()
				->sse()
				->json([
					'amount' => $cart, // $cart does not store a real value, but is calculated without a repeated request to ArangoDB
					'errors' => $this->errors
				])
				->validate($this->request)
				?->body()
				->end();

			// Deinitializing buffer with amount of the product in the cart 
			unset($cart);
		}

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

	/**
	 * Summary
	 *
	 * Information about products in the cart
	 *
	 * @return null
	 */
	public function summary(): null
	{
		if (str_contains($this->request->headers['accept'], content::json->value)) {
			// Request for JSON response

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

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

				// Initializing summary data of the cart
				$summary = $this->cart?->summary(currency: $this->currency, errors: $this->errors['cart']);

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

				// Deinitializing summary data of the cart 
				unset($summary);
			}
		}

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

	/**
	 * Share
	 *
	 * Share the cart
	 *
	 * @return null
	 */
	public function share(): null
	{
		if (str_contains($this->request->headers['accept'], content::json->value)) {
			// Request for JSON response

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

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

				if ($share = $this->cart->share ?? $this->cart->share()) {
					// The cart is available for sharing

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

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

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

	/**
	 * Pay
	 *
	 * Pay for the cart
	 *
	 * @return null
	 */
	public function pay(): null
	{
		if (str_contains($this->request->headers['accept'], content::json->value)) {
			// Request for JSON response

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

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

				if ($share = $this->cart->share ?? $this->cart->share()) {
					// The cart is available for sharing

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

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

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

	/**
	 * Robokassa
	 *
	 * HTML-document with robokassa iframe 
	 *
	 * @param null
	 *
	 * @todo THIS MUST BE A PAYMENT OF ORDER IN THE FUTURE, NOT CART
	 */
	public function robokassa(): null
	{
		// Initializing the cart
		$this->cart ??= $this->account?->cart() ?? $this->session?->cart();

		// Initializing the cart data
		$this->view->cart = $this->cart;
		$this->view->summary = $this->cart?->summary(currency: $this->currency);

		if (str_contains($this->request->headers['accept'], content::any->value)) {
			// Request for any response

			// Render page
			$page = $this->view->render('iframes/robokassa.html');

			// Sending response
			$this->response
				->start()
				->clean()
				->sse()
				->write($page)
				->validate($this->request)
				?->body()
				->end();

			// Deinitializing rendered page
			unset($page);
		}

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