<?php

declare(strict_types=1);

namespace mirzaev\ebala\controllers;

// Файлы проекта
use mirzaev\ebala\controllers\core,
	mirzaev\ebala\controllers\traits\errors,
	mirzaev\ebala\models\account as model,
	mirzaev\ebala\models\registry,
	mirzaev\ebala\models\core as _core;

// Фреймворк ArangoDB
use mirzaev\arangodb\document;

// Встроенные библиотеки
use exception;

/**
 * Контроллер аккаунта
 *
 * @package mirzaev\ebala\controllers
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 */
final class account extends core
{
	use errors;

	/**
	 * Прочитать данные
	 *
	 * @param array $parameters Параметры запроса
	 */
	public function fields(array $parameters = []): ?string
	{
		if ($this->account->status() && ($this->account->type === 'administrator' || $this->account->type === 'operator')) {
			// Авторизован аккаунт администратора или оператора

			// Инициализация данных аккаунта
			$account = model::read('d._key == "' . $parameters['id'] . '"', return: '{ name: d.name, number: d.number, mail: d.mail, commentary: d.commentary }')->getAll();

			if (!empty($account)) {
				// Найдены данные аккаунта

				// Запись заголовков ответа
				header('Content-Type: application/json');
				header('Content-Encoding: none');
				header('X-Accel-Buffering: no');

				// Инициализация буфера вывода
				ob_start();

				// Генерация ответа
				echo json_encode($account);

				// Запись заголовков ответа
				header('Content-Length: ' . ob_get_length());

				// Отправка и деинициализация буфера вывода
				ob_end_flush();
				flush();
			}
		}

		// Возврат (провал)
		return null;
	}

	/**
	 * Обновить данные
	 *
	 * @param array $parameters Параметры запроса
	 */
	public function update(array $parameters = []): ?string
	{
		if ($this->account->status() && ($this->account->type === 'administrator' || $this->account->type === 'operator')) {
			// Авторизован аккаунт администратора или оператора

			// Инициализация данных аккаунта
			$account = model::read('d._key == "' . $parameters['id'] . '"');

			if (!empty($account)) {
				// Найден аккаунт

				// Инициализация буфера изменённости пароля
				$password = false;

				// Инициализация параметров (перезапись переданными значениями)
				if ($parameters['name_first'] !== $account->name['first']) $account->name = ['first' => $parameters['name_first']] + $account->name;
				if ($parameters['name_second'] !== $account->name['second']) $account->name = ['second' => $parameters['name_second']] + $account->name;
				if ($parameters['name_last'] !== $account->name['last']) $account->name = ['last' => $parameters['name_last']] + $account->name;
				if ($parameters['number'] !== $account->number)
					if (mb_strlen($parameters['number']) === 11) $account->number = $parameters['number'];
					else throw new exception('Номер должен состоять из 11 символов');
				if ($parameters['mail'] !== $account->mail) $account->mail = $parameters['mail'];
				if (!empty($parameters['password']) && !sodium_crypto_pwhash_str_verify($parameters['password'], $account->password) && $password = true)
					if (mb_strlen($parameters['password']) > 6) $account->password = sodium_crypto_pwhash_str(
						$parameters['password'],
						SODIUM_CRYPTO_PWHASH_OPSLIMIT_SENSITIVE,
						SODIUM_CRYPTO_PWHASH_MEMLIMIT_SENSITIVE
					);
					else throw new exception('Пароль должен быть длиннее 6 символов');
				if ($parameters['commentary'] !== $account->commentary) $account->commentary = $parameters['commentary'];

				if (_core::update($account)) {
					// Записаны данные аккаунта

					if ($account->type === 'worker') {
						// Сотрудник

						// Инициализация строки в глобальную переменную шаблонизатора
						$this->view->rows = registry::workers(
							before: sprintf(
								"FILTER a._id == '%s' && a.deleted != true",
								$account->getId()
							),
							after: <<<AQL
								let account = (IS_SAME_COLLECTION('account', a) ? a : b)
								let worker = (IS_SAME_COLLECTION('worker', a) ? a : b)
								FILTER account.type == 'worker' && b.deleted != true
							AQL,
							amount: 1
						);
					} else if ($account->type === 'market') {
						// Магазин

						// Инициализация строки в глобальную переменную шаблонизатора
						$this->view->rows = registry::markets(
							before: sprintf(
								"FILTER a._id == '%s' && a.deleted != true",
								$account->getId()
							),
							after: <<<AQL
								let account = (IS_SAME_COLLECTION('account', a) ? a : b)
								let market = (IS_SAME_COLLECTION('market', a) ? a : b)
								FILTER account.type == 'market' && b.deleted != true
							AQL,
							amount: 1
						);
					} else {
						// Администратор или оператор (подразумевается)

						// Инициализация строки в глобальную переменную шаблонизатора
						$this->view->rows = [['account' => $account->getAll()]];
					}

					// Запись в глобальную переменную шаблонизатора обрабатываемой страницы (отключение)
					$this->view->page = null;

					// Запись заголовков ответа
					header('Content-Type: application/json');
					header('Content-Encoding: none');
					header('X-Accel-Buffering: no');

					// Инициализация буфера вывода
					ob_start();

					// Инициализация буфера ответа
					$return = [
						'updated' => true,
						'row' => $this->view->render(DIRECTORY_SEPARATOR . 'elements' . DIRECTORY_SEPARATOR . $account->type . 's.html'),
						'errors' => self::parse_only_text($this->errors)
					];

					if ($password) $return['clipboard'] =  match ($account->type) {
						'worker' => 'Номер: ' . model::worker($account->getId())?->number,
						'market' => 'Идентификатор: ' . model::market($account->getId())?->id,
						'operator' => "Идентификатор: {$account->getKey()}",
						'administrator' => "Идентификатор: {$account->getKey()}",
						default => "Идентификатор: {$account->getKey()}"
					}
						. "\nПароль: {$parameters['password']}";

					// Генерация ответа
					echo json_encode($return);

					// Запись заголовков ответа
					header('Content-Length: ' . ob_get_length());

					// Отправка и деинициализация буфера вывода
					ob_end_flush();
					flush();
				} else throw new exception('Не удалось записать изменения в базу данных');
			} else throw new exception('Не удалось найти аккаунт');
		}

		// Возврат (провал)
		return null;
	}

	/**
	 * Пометить удалённым
	 *
	 * @param array $parameters Параметры запроса
	 */
	public function delete(array $parameters = []): ?string
	{
		if ($this->account->status() && ($this->account->type === 'administrator' || $this->account->type === 'operator')) {
			// Авторизован аккаунт администратора или оператора

			// Инициализация данных аккаунта
			$account = model::read('d._key == "' . $parameters['id'] . '"');

			if (!empty($account)) {
				// Найден аккаунт

				// Удаление
				$account->active = false;
				$account->deleted = true;

				if ($account->type === 'worker') {
					// Сотрудник

					// Инициализация сотрудника
					if (empty($worker = model::worker($account->getId()))) throw new exception('Не удалось инициализировать сотрудника');

					// Удаление
					$worker->active = false;
					$worker->deleted = true;

					// Запись в ArangoDB
					if (!_core::update($worker)) throw throw new exception('Не удалось записать изменения в базу данных');
				} else if ($account->type === 'market') {
					// Магазин

					// Инициализация магазина
					if (empty($market = model::market($account->getId()))) throw new exception('Не удалось инициализировать магазин');

					// Удаление
					$market->active = false;
					$market->deleted = true;

					// Запись в ArangoDB
					if (!_core::update($market)) throw throw new exception('Не удалось записать изменения в базу данных');
				}

				if (_core::update($account)) {
					// Записаны данные аккаунта

					// Запись в глобальную переменную шаблонизатора обрабатываемой страницы (отключение)
					$this->view->page = null;

					// Запись заголовков ответа
					header('Content-Type: application/json');
					header('Content-Encoding: none');
					header('X-Accel-Buffering: no');

					// Инициализация буфера вывода
					ob_start();

					// Генерация ответа
					echo json_encode([
						'deleted' => true,
						'errors' => self::parse_only_text($this->errors)
					]);

					// Запись заголовков ответа
					header('Content-Length: ' . ob_get_length());

					// Отправка и деинициализация буфера вывода
					ob_end_flush();
					flush();
				} else throw new exception('Не удалось записать изменения в базу данных');
			} else throw new exception('Не удалось найти аккаунт');
		}

		// Возврат (провал)
		return null;
	}

	/**
	 * Пометить заблокированным
	 *
	 * @param array $parameters Параметры запроса
	 */
	public function ban(array $parameters = []): ?string
	{
		if ($this->account->status() && ($this->account->type === 'administrator' || $this->account->type === 'operator')) {
			// Авторизован аккаунт администратора или оператора

			// Инициализация данных аккаунта
			$account = model::read('d._key == "' . $parameters['id'] . '"');

			if (!empty($account)) {
				// Найден аккаунт

				// Блокирование
				$account->active = false;
				$account->banned = true;

				if (_core::update($account)) {
					// Записаны данные аккаунта

					// Запись заголовков ответа
					header('Content-Type: application/json');
					header('Content-Encoding: none');
					header('X-Accel-Buffering: no');

					// Инициализация буфера вывода
					ob_start();

					// Генерация ответа
					echo json_encode([
						'banned' => true,
						'errors' => self::parse_only_text($this->errors)
					]);

					// Запись заголовков ответа
					header('Content-Length: ' . ob_get_length());

					// Отправка и деинициализация буфера вывода
					ob_end_flush();
					flush();
				} else throw new exception('Не удалось записать изменения в базу данных');
			} else throw new exception('Не удалось найти аккаунт');
		}

		// Возврат (провал)
		return null;
	}

	/**
	 * Снять пометку заблокированного (разблокировать)
	 *
	 * @param array $parameters Параметры запроса
	 */
	public function unban(array $parameters = []): ?string
	{
		if ($this->account->status() && ($this->account->type === 'administrator' || $this->account->type === 'operator')) {
			// Авторизован аккаунт администратора или оператора

			// Инициализация данных аккаунта
			$account = model::read('d._key == "' . $parameters['id'] . '"');

			if (!empty($account)) {
				// Найден аккаунт

				// Блокирование
				$account->active = true;
				$account->banned = false;

				if (_core::update($account)) {
					// Записаны данные аккаунта

					// Запись заголовков ответа
					header('Content-Type: application/json');
					header('Content-Encoding: none');
					header('X-Accel-Buffering: no');

					// Инициализация буфера вывода
					ob_start();

					// Генерация ответа
					echo json_encode([
						'unbanned' => true,
						'errors' => self::parse_only_text($this->errors)
					]);

					// Запись заголовков ответа
					header('Content-Length: ' . ob_get_length());

					// Отправка и деинициализация буфера вывода
					ob_end_flush();
					flush();
				} else throw new exception('Не удалось записать изменения в базу данных');
			} else throw new exception('Не удалось найти аккаунт');
		}

		// Возврат (провал)
		return null;
	}
}
