<?php

declare(strict_types=1);

namespace mirzaev\ebala\controllers;

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

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

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

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

			foreach (['active', 'inactive'] as $name) {
				// Перебор фильтров статусов

				// Инициализация значения (приоритет у cookie)
				$value = $_COOKIE["administrators_filter_$name"] ?? @$this->session->buffer[$_SERVER['INTERFACE']]['administrators']['filters'][$name] ?? 0;

				// Инициализировано значение?
				if ($value === null || $value === 0) continue;

				// Генерация класса для HTML-элемента по его состоянию (0 - ОТСУТСТВУЕТ, 1 - И, 2 - ИЛИ)
				$this->view->{$name} = match ($value) {
					'0', 0 => 'earth',
					'1', 1 => 'sand',
					'2', 2 => 'river',
					default => 'earth'
				};
			}

			// Генерация представлениямя
			$main = $this->view->render(DIRECTORY_SEPARATOR . 'pages' . DIRECTORY_SEPARATOR . 'administrators.html');
		} else $main = $this->authorization();

		// Возврат (успех)
		if ($_SERVER['REQUEST_METHOD'] === 'GET') return $this->view->render(DIRECTORY_SEPARATOR . 'index.html', ['main' => $main]);
		else if ($_SERVER['REQUEST_METHOD'] === 'POST') return $main;

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

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

			// Реинициализация актуальной страницы
			if (isset($parameters['page'])) $this->session->write(['administrators' => ['page' => $parameters['page']]]);
			else if (empty($this->session->buffer[$_SERVER['INTERFACE']]['administrators']['page'])) $this->session->write(['administrators' => ['page' => 1]]);

			// Инициализация буферов AQL-выражений для инъекции фильтра по статусам
			$filters_statuses_and = '';
			$filters_statuses_or = '';

			foreach (['active', 'inactive'] as $name) {
				// Перебор фильтров статусов (И)

				// Инициализация значения (приоритет у cookie) (отсутствие значения или значение 0 вызывают continue)
				if (empty($value = $_COOKIE["administrators_filter_$name"] ?? @$this->session->buffer[$_SERVER['INTERFACE']]['administrators']['filters'][$name] ?? 0)) continue;

				// Конвертация ярлыков
				$converted = match ($name) {
					'inactive' => 'active',
					default => $name
				};

				// Генерация выражения
				$expression = "account.$converted == " . ($name === $converted ? 'true' : 'false');

				// Генерация AQL-выражения для инъекции в строку запроса
				if ($value === '1') $filters_statuses_and .= " && " . $expression;
				else if ($value === '2') $filters_statuses_or .= " || " . $expression;
			}

			// Очистка от бинарных операторов сравнения с только одним операндом (крайние)
			$filters_statuses_and = trim(trim(trim($filters_statuses_and), '&&'));
			$filters_statuses_or = trim(trim(trim($filters_statuses_or), '||'));

			// Инициализация буфера с объёдинёнными буферами c AQL-выражениям "И" и "ИЛИ"
			$filters_statuses_merged = (empty($filters_statuses_and) ? '' : "($filters_statuses_and)") . (empty($filters_statuses_or) ? '' : (empty($filters_statuses_and) ? '' : ' || ') . "($filters_statuses_or)");

			// Инициализация общего буфера с AQL-выражениями
			$filters = '';

			// Объединение фильтров в единую строку с AQL-выражениями для инъекции
			if (!empty($filters_statuses_merged)) $filters .= empty($filters) ? $filters_statuses_merged : " && ($filters_statuses_merged)";

			// Инициализация строки поиска
			$search = $_COOKIE["administrators_filter_search"] ?? @$this->session->buffer[$_SERVER['INTERFACE']]['administrators']['filters']['search'] ?? '';
			if (mb_strlen($search) < 3) $search = null;
			$search_query = empty($search)
				? null
				: <<<AQL
					SEARCH 
						account.commentary IN TOKENS(@search, 'text_ru') 
						|| STARTS_WITH(account._key, @search)
						|| STARTS_WITH(account.name.first, @search)
						|| STARTS_WITH(account.name.second, @search)
						|| STARTS_WITH(account.name.last, @search)
						|| STARTS_WITH(account.number, @search)
						|| STARTS_WITH(account.mail, @search)
						|| (LENGTH(@search) > 6 && LEVENSHTEIN_MATCH(account._key, TOKENS(@search, 'text_en')[0], 2, true))
						|| (LENGTH(@search) > 4 && LEVENSHTEIN_MATCH(account.name.first, TOKENS(@search, 'text_ru')[0], 2, true))
						|| (LENGTH(@search) > 4 && LEVENSHTEIN_MATCH(account.name.second, TOKENS(@search, 'text_ru')[0], 2, true))
						|| (LENGTH(@search) > 4 && LEVENSHTEIN_MATCH(account.name.last, TOKENS(@search, 'text_ru')[0], 2, true))
						|| (LENGTH(@search) > 6 && LEVENSHTEIN_MATCH(account.number, TOKENS(@search, 'text_en')[0], 2, true))
						|| (LENGTH(@search) > 6 && LEVENSHTEIN_MATCH(account.mail, TOKENS(@search, 'text_en')[0], 2, true))
					AQL;

			// Инициализация данных для генерации HTML-документа с таблицей
			$this->view->rows = registry::administrators(
				before: sprintf(
					<<<AQL
						%s
						FILTER account.type == 'administrator' && account.deleted != true%s
					AQL,
					$search_query,
					empty($filters) ? null : " && ($filters)"
				),
				page: (int) $this->session->buffer[$_SERVER['INTERFACE']]['administrators']['page'],
				target: empty($search) ? account::COLLECTION : 'registry_accounts',
				binds: empty($search) ? [] : [
					'search' => $search
				]
			);


			// Запись в cookie (только таким методом можно записать "hostonly: true")		
			setcookie(
				'administrators_page',
				(string) $this->session->buffer[$_SERVER['INTERFACE']]['administrators']['page'],
				[
					'expires' => strtotime('+1 hour'),
					'path' => '/',
					'secure' => true,
					'httponly' => false,
					'samesite' => 'strict'
				]
			);

			// Запись в глобальную переменную шаблонизатора обрабатываемой страницы
			$this->view->page = $parameters['page'];

			// Инициализация блока
			return $this->view->render(DIRECTORY_SEPARATOR . 'elements' . DIRECTORY_SEPARATOR . 'administrators.html');
		}

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

	/**
	 * Создать
	 *
	 * @param array $parameters Параметры запроса
	 *
	 * @return void В буфер вывода JSON-документ с запрашиваемыми параметрами
	 */
	public function create(array $parameters = []): void
	{
		if ($this->account->status() && $this->account->type === 'administrator') {
			// Авторизован аккаунт администратора

			// Инициализация буфера ошибок
			$this->errors['account'] ??= [];

			try {
				// Проверка наличия переданного пароля
				if (!isset($parameters['password'])) throw new exception('Не получен пароль');
				else if (strlen($parameters['password']) < 6) throw new exception('Пароль должен быть не менее 6 символов');
				else if (!empty($parameters['number']) && strlen($parameters['number']) < 11) throw new exception('Несоответствие формату SIM-номера');
				else if (!empty($parameters['mail']) && preg_match('/^.+@.+\.\w+$/', $parameters['mail']) === 0) throw new exception('Несоответствие формату почты');

				// Универсализация
				/* $parameters['number'] = (int) $parameters['number']; */

				// Создание аккаунта
				$account = account::create(
					data: [
						'type' => 'administrator',
						'name' => [
							'first' => $parameters['name_first'],
							'second' => $parameters['name_second'],
							'last' => $parameters['name_last']
						],
						'number' => $parameters['number'] === 0 ? '' : $parameters['number'],
						'mail' => $parameters['mail'],
						'password' => sodium_crypto_pwhash_str(
							$parameters['password'],
							SODIUM_CRYPTO_PWHASH_OPSLIMIT_SENSITIVE,
							SODIUM_CRYPTO_PWHASH_MEMLIMIT_SENSITIVE
						),
						'commentary' => $parameters['commentary']
					],
					errors: $this->errors['account']
				);

				// Проверка существования созданного аккаунта
				if (empty($account)) throw new exception('Не удалось создать аккаунт');
			} catch (exception $e) {
				// Write to the errors registry
				$this->errors['account'][] = [
					'text' => $e->getMessage(),
					'file' => $e->getFile(),
					'line' => $e->getLine(),
					'stack' => $e->getTrace()
				];
			}

			// Инициализация идентификатора аккаунта (ключ документа инстанции аккаунта в базе данных)
			$_key = preg_replace('/.+\//', '', $account ?? '');

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

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

			// Генерация ответа
			echo json_encode(
				[
					'clipboard' => empty($this->errors['account']) ? <<<TEXT
					Идентификатор: $_key
					Пароль: {$parameters['password']}
					TEXT : '',
					'errors' => self::parse_only_text($this->errors['account'])
				]
			);

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

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