<?php

declare(strict_types=1);

namespace mirzaev\arming_bot\controllers;

// Files of the project
use mirzaev\arming_bot\controllers\core,
	mirzaev\arming_bot\models\entry,
	mirzaev\arming_bot\models\category,
	mirzaev\arming_bot\models\product,
	mirzaev\arming_bot\models\menu;

// Library for ArangoDB
use ArangoDBClient\Document as _document;

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

	/**
	 * Catalog
	 *
	 * @param array $parameters Parameters of the request (POST + GET)
	 */
	public function index(array $parameters = []): ?string
	{
		// Validating
		if (!empty($parameters['category']) && preg_match('/[\d]+/', $parameters['category'], $matches)) $category = (int) $matches[0];

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

			// Initialize of category
			$category = category::_read('d.identifier == @identifier', parameters: ['identifier' => $category], errors: $this->errors['catalog']);

			if ($category instanceof category) {
				// Found the category

				// Write to the response buffer
				$response['category'] = ['name' => $category->name ?? null];

				// Search for categories that are descendants of $category
				$entries = entry::search(
					document: $category,
					amount: 30,
					errors: $this->errors['catalog']
				);

				// Initialize buffers of entries (in singular, by parameter from ArangoDB)
				$category = $product = [];

				foreach ($entries as $entry) {
					// Iterate over entries (descendands)

					// Write entry to the buffer of entries (sort by $category and $product)
					${$entry->_type}[] = $entry;
				}

				// Write to the buffer of global variables of view templater
				$this->view->categories = $category;

				// Write to the buffer of global variables of view templater
				$this->view->products = $product;

				// Delete buffers
				unset($category, $product);
			}
		} else if (!isset($parameters['category'])) {
			// Not received identifie of the category

			// Search for root ascendants categories
			$this->view->categories = entry::ascendants(descendant: new category, errors: $this->errors['catalog']);
		}

		// Validating
		if (!empty($parameters['product']) && preg_match('/[\d]+/', $parameters['product'], $matches)) $product = (int) $matches[0];

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

			// Search for product and write to the buffer of global variables of view templater
			$this->view->product =  product::read(
				filter: "d.identifier == @identifier && d.deleted != true && d.hidden != true",
				sort: 'd.created DESC',
				amount: 1,
				return: '{name: d.name.@language, description: d.description.@language, cost: d.cost, weight: d.weight, dimensions: d.dimensions, brand: d.brand.@language, compatibility: d.compatibility.@language, images: d.images[*].storage}',
				parameters: ['identifier' => $product],
				language: $this->language,
				errors: $this->errors['catalog']
			)[0]?->getAll() ?? null;
		}

		// Validating
		if (!empty($parameters['text']) && preg_match('/[\w\s]+/u', $parameters['text'], $matches)) $text = $matches[0];

		if (isset($text)) {
			// Received and validated text for search 

			// Intialize buffer of query parameters
			$_parameters = [];

			// Initialize buffer of filters query (AQL)
			$_filters = 'd.deleted != true && d.hidden != true';

			if (isset($this->view->products) && count($this->view->products) > 0) {
				// Amount of rendered products is more than 0

				// Write to the buffer of filters query (AQL)
				$_filters .= ' && POSITION(["' . implode('", "', array_map(fn(_document $document): string => $document->getId(), $this->view->products)) . '"], d._id)';
			}

			// Validating
			if (!empty($parameters['brand']) && preg_match('/[\w]+/', $parameters['brand'], $matches)) $brand = $matches[0];

			if (isset($brand)) {
				// Received and validated filter by brand

				// Write to the buffer of filters query (AQL)
				$_filters .= ' && d.brand.@language == @brand';

				// Write to the buffer of query parameters
				$_parameters['brand'] = $brand;
			}

			// Initialize buffer of filters query (AQL)
			$_sort = 'd.position ASC, d.name ASC, d.created DESC';

			// Validating
			if (!empty($parameters['sort']) && preg_match('/[\w]+/', $parameters['sort'], $matches)) $sort = $matches[0];

			if (isset($sort)) {
				// Received and validated sort

				// Write to the buffer of sort query (AQL)
				$_sort = "d.@sort DESC, $_sort";

				// Write to the buffer of query parameters
				$_parameters['sort'] = $sort;
			}

			// Search for products and write to the buffer of global variables of view templater
			$this->view->products = product::read(
				search: $text,
				filter: $_filters,
				sort: $_sort,
				amount: 30,
				language: $this->language,
				parameters: $_parameters,
				errors: $this->errors['catalog']
			);
		}

		if (isset($this->view->products) && count($this->view->products) > 0) {
			// Amount of rendered products is more than 0

			// Search for filters and write to the buffer of global variables of view templater
			$this->view->filters = [
				'brands' => product::collect(
					return: 'd.brand.@language',
					products: array_map(fn(_document $document): string => $document->getId(), $this->view->products),
					language: $this->language,
					errors: $this->errors['catalog']
				)
			];
		}

		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.position ASC, d.created DESC, d._key DESC',
				amount: 4,
				parameters: ['language' => $this->language->name],
				errors: $this->errors['menu']
			);
		}

		if ($_SERVER['REQUEST_METHOD'] === 'GET') {
			// GET request

			// Exit (success)
			return $this->view->render('catalog/page.html');
		} else if ($_SERVER['REQUEST_METHOD'] === 'POST') {
			// POST request

			// Initializing the buffer of response
			$response = [
				'title' => $title ?? null
			];

			if (isset($this->view->categories)) {
				// Initialized categories

				// Render HTML-code of categories and write to the response buffer
				$response['html'] ??= [];
				$response['html']['categories'] = $this->view->render('catalog/elements/categories.html');
			}

			if (isset($this->view->product)) {
				// Initialized product

			}

			if (isset($this->view->products)) {
				// Initialized products

				// Render HTML-code of products and write to the response buffer
				$response['html'] ??= [];
				$response['html']['products'] = $this->view->render('catalog/elements/products/2columns.html');
			}

			if (isset($this->view->filters)) {
				// Initialized filters

				// Render HTML-code of filters and write to the response buffer
				$response['html'] ??= [];
				$response['html']['filters'] = $this->view->render('catalog/elements/filters.html');
			}

			// 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(
				$response + [
					'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;
	}
}
