<?php

declare(strict_types=1);

namespace mirzaev\ebala\views;

// Файлы проекта
use mirzaev\ebala\models\session,
	mirzaev\ebala\models\account;

// Фреймворк PHP
use mirzaev\minimal\controller;

// Шаблонизатор представлений
use Twig\Loader\FilesystemLoader,
	Twig\Environment as twig,
	Twig\Extra\Intl\IntlExtension as intl,
	Twig\TwigFilter;


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

/**
 * Шаблонизатор представлений
 *
 * @package mirzaev\ebala\controllers
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 */
final class templater extends controller implements ArrayAccess
{
	/**
	 * Реестр глобальных переменных
	 */
	public array $variables = [];

	/**
	 * Инстанция окружения twig 
	 */
	public twig $twig;

	/**
	 * Конструктор
	 *
	 * @return void
	 */
	public function __construct(?session &$session = null, ?account &$account = null)
	{
		// Инициализация шаблонизатора
		$this->twig = new twig(new FilesystemLoader(VIEWS));

		// Инициализация глобальных переменных
		$this->twig->addGlobal('server', $_SERVER);
		$this->twig->addGlobal('cookie', $_COOKIE); // @todo DELETE THIS
		$this->twig->addGlobal('cookies', $_COOKIE);
		if (!empty($session->status())) {
			$this->twig->addGlobal('session', $session);
			$this->twig->addGlobal('buffer', $session->buffer[$_SERVER['INTERFACE']] ?? null);
		}
		if (!empty($account->status())) $this->twig->addGlobal('account', $account);

		// Инициализация фильтра
		$this->twig->addFilter(
			new TwigFilter(
				'storaged_number_to_readable',
				fn (int|string|null $number = null) => strlen($number = (string) $number) === 11 ? preg_replace('/(^\d)(\d{3})(\d{3})(\d{2})(\d{2}$)/', '+$1 ($2) $3-$4-$5', $number) : $number
			)
		);

		// Инициализация фильтра
		$this->twig->addFilter(
			new TwigFilter(
				'storaged_requisites_to_card',
				function (string|null $requisites = null) {
					preg_match('/^\d{4}\s\d{4}\s\d{4}\s\d{4}/', $requisites, $matches);
					return isset($matches[0]) ? $matches[0] : $requisites;
				}
			)
		);

		// Инициализация фильтра
		$this->twig->addFilter(
			new TwigFilter(
				'storaged_requisites_preview',
				fn (string|null $requisites = null) => preg_replace('/^(\d{4}\s\d{4}\s\d{4}\s)(\d{4})\s([A-zА-я]{4})([A-zА-я]+)\s(.+)$/u', '...$2 $3. $5', $requisites ?? '')
			)
		);

		// Инициализация фильтра
		$this->twig->addFilter(
			new TwigFilter(
				'date_to_russian',
				fn (string|null $date = null) => preg_replace('/^(\d{4})-(\d\d)-(\d\d)$/', '$3.$2.$1', $date ?? '')
			)
		);

		// Инициализация фильтра
		$this->twig->addFilter(
			new TwigFilter(
				'account_type_to_russian',
				fn (?string $type = null) => match ($type) {
					'worker' => 'Сотрудник',
					'market' => 'Магазин',
					'operator' => 'Оператор',
					'administrator' => 'Администратор',
					default => 'Аккаунт'
				}
			)
		);

		// Инициализация фильтра
		$this->twig->addFilter(
			new TwigFilter(
				'work',
				fn (string $work) => preg_replace('/^Мобильный/', 'Моб.', $work)
			)
		);

		// Инициализация расширений
		$this->twig->addExtension(new intl());
	}

	/**
	 * Отрисовка HTML-документа
	 *
	 * @param string $file Относительный директории представлений путь до файла представления
	 * @param ?array $variables Реестр переменных
	 *
	 * @return ?string HTML-документ
	 */
	public function render(string $file, ?array $variables = null): ?string
	{
		// Генерация представления
		return $this->twig->render($file, $variables ?? $this->variables);
	}

	/**
	 * Записать
	 *
	 * Записывает переменную в реестр глобальных переменных
	 *
	 * @param string $name Название
	 * @param mixed $value Содержимое
	 *
	 * @return void
	 */
	public function __set(string $name, mixed $value = null): void
	{
		$this->variables[$name] = $value;
	}

	/**
	 * Прочитать
	 *
	 * Читает переменную из реестра глобальных переменных
	 *
	 * @param string $name Название 
	 *
	 * @return mixed Данные переменной из реестра глобальных переменных
	 */
	public function __get(string $name): mixed
	{
		return $this->variables[$name];
	}

	/**
	 * Проверить инициализированность
	 *
	 * Проверяет инициализированность переменной в буфере переменных представления
	 *
	 * @param string $name Название
	 *
	 * @return bool Переменная инициализирована?
	 */
	public function __isset(string $name): bool
	{
		return isset($this->variables[$name]);
	}

	/**
	 * Удалить
	 *
	 * Деинициализирует переменную в реестре глобальных переменных 
	 *
	 * @param string $name Название
	 *
	 * @return void
	 */
	public function __unset(string $name): void
	{
		unset($this->variables[$name]);
	}

	/**
	 * Записать
	 *
	 * Записывает переменную в реестр глобальных переменных
	 *
	 * @param mixed $offset Сдвиг, либо идентификатор 
	 * @param mixed $value Содержимое
	 *
	 * @return void
	 */
	public function offsetSet(mixed $offset, mixed $value): void
	{
		$this->variables[$offset] = $value;
	}

	/**
	 * Прочитать
	 *
	 * Читает переменную из реестра глобальных переменных
	 *
	 * @param mixed $offset Сдвиг, либо идентификатор 
	 *
	 * @return mixed Данные переменной из реестра глобальных переменных
	 */
	public function offsetGet(mixed $offset): mixed
	{
		return $this->variables[$offset];
	}

	/**
	 * Проверить инициализированность
	 *
	 * Проверяет инициализированность переменной в реестре глобальных переменных 
	 *
	 * @param mixed $offset Сдвиг, либо идентификатор 
	 *
	 * @return bool Инициализирована переменная?
	 */
	public function offsetExists(mixed $offset): bool
	{
		return isset($this->variables[$offset]);
	}

	/**
	 * Удалить
	 *
	 * Деинициализирует переменную в реестре глобальных переменных 
	 *
	 * @param mixed $offset Сдвиг, либо идентификатор 
	 *
	 * @return void
	 */
	public function offsetUnset(mixed $offset): void
	{
		unset($this->variables[$offset]);
	}
}
