<?php

namespace Mnv\Core\Utilities\Cookie;

/**
 * Управление сеансами с улучшенной обработкой файлов `cookie`
 * Вы можете запустить сеанс, используя статический метод `Session::start(...)`, который совместим со встроенной в PHP функцией `session_start'()`
 * Обратите внимание, что сеансы всегда должны запускаться до отправки HTTP-заголовков клиенту, т.е. до начала фактического вывода
 */
final class Session {

	private function __construct() { }

	/**
	 * Запускает или возобновляет сеанс способом, совместимым со встроенной в PHP функцией `session_start()`
	 *
	 * @param string|null $sameSiteRestriction указывает, что файл cookie не следует отправлять вместе с межсайтовыми запросами (либо "null", либо "None", либо "Lax", либо "Strict").
	 */
	public static function start($sameSiteRestriction = Cookie::SAME_SITE_RESTRICTION_LAX)
    {
		// run PHP's built-in equivalent
		\session_start();

		// intercept the cookie header (if any) and rewrite it
		self::rewriteCookieHeader($sameSiteRestriction);
	}

	/**
	 * Возвращает или задает идентификатор текущего сеанса
	 *
	 * Чтобы изменить идентификатор текущего сеанса, передайте новый идентификатор в качестве единственного аргумента этому методу
	 * Пожалуйста, обратите внимание, что редко возникает необходимость в версии этого метода, которая * обновляет * идентификатор
	 * Для большинства целей вы можете счесть метод `regenerate` из этого же класса более полезным
	 *
	 * @param string|null $newId (optional) новый ID сеанса для замены текущего ID сеанса
	 * @return string (старый) ID сеанса или пустая строка
	 */
	public static function id($newId = null)
    {
		if ($newId === null) {
			return \session_id();
		}
		else {
			return \session_id($newId);
		}
	}

	/**
	 * Восстанавливает идентификатор сеанса способом, совместимым со встроенной функцией PHP `session_regenerate_id()`
	 *
	 * @param bool $deleteOldSession следует ли удалять старую сессию или нет
	 * @param string|null $sameSiteRestriction указывает, что файл cookie не следует отправлять вместе с межсайтовыми запросами (либо "null", либо "None", либо "Lax", либо "Strict").
	 */
	public static function regenerate($deleteOldSession = false, $sameSiteRestriction = Cookie::SAME_SITE_RESTRICTION_LAX)
    {
		// запустите встроенный эквивалент `PHP`
		\session_regenerate_id($deleteOldSession);

		// intercept the cookie header (if any) and rewrite it
		self::rewriteCookieHeader($sameSiteRestriction);
	}

	/**
	 * Проверяет, существует ли значение для указанного ключа в сеансе
	 *
	 * @param string $key ключ для проверки
	 * @return bool существует ли значение для указанного ключа или нет
	 */
	public static function has($key)
    {
		return isset($_SESSION[$key]);
	}

	/**
	 * Возвращает запрошенное значение из сеанса или, если не найдено, указанное значение по умолчанию
	 *
	 * @param string $key ключ для получения значения
	 * @param mixed $defaultValue значение по умолчанию, которое возвращается, если запрошенное значение не может быть найдено
	 * @return mixed запрошенное значение или значение по умолчанию
	 */
	public static function get($key, $defaultValue = null)
    {
		if (isset($_SESSION[$key])) {
			return $_SESSION[$key];
		}
		else {
			return $defaultValue;
		}
	}

	/**
     * Возвращает запрошенное значение и удаляет его из сеанса
     * Это идентично вызову сначала `get`, а затем "remove" для одного и того же ключа
	 *
	 * @param string $key ключ для извлечения и удаления значения для
	 * @param mixed $defaultValue значение по умолчанию, которое возвращается, если запрошенное значение не может быть найдено
	 * @return mixed запрошенное значение или значение по умолчанию
	 */
	public static function take($key, $defaultValue = null)
    {
		if (isset($_SESSION[$key])) {
			$value = $_SESSION[$key];

			unset($_SESSION[$key]);

			return $value;
		}
		else {
			return $defaultValue;
		}
	}

	/**
	 * Устанавливает значение для указанного ключа на заданное значение
	 * Все данные, которые уже существуют для указанного ключа, будут перезаписаны
	 *
	 * @param string $key ключ для установки значения
	 * @param mixed $value значение, которое нужно установить
	 */
	public static function set($key, $value)
    {
		$_SESSION[$key] = $value;
	}

	/**
	 * Удаляет значение для указанного ключа из сеанса
	 *
	 * @param string $key ключ для удаления значения
	 */
	public static function delete($key)
    {
		unset($_SESSION[$key]);
	}

	/**
	 * Перехватывает и перезаписывает заголовок файла cookie сеанса
	 *
	 * @param string|null $sameSiteRestriction указывает, что файл cookie не следует отправлять вместе с межсайтовыми запросами (либо "null", либо "None", либо "Lax", либо "Strict").
	 */
	private static function rewriteCookieHeader($sameSiteRestriction = Cookie::SAME_SITE_RESTRICTION_LAX)
    {
		// получить и удалить исходный заголовок файла cookie, установленный PHP
		$originalCookieHeader = ResponseHeader::take('Set-Cookie', \session_name() . '=');

		// если был найден заголовок файла cookie
		if (isset($originalCookieHeader)) {
			// проанализируйте его в экземпляр файла cookie
			$parsedCookie = Cookie::parse($originalCookieHeader);

			// если файл cookie был успешно проанализирован
			if (isset($parsedCookie)) {
				// примените прилагаемое ограничение для одного и того же сайта
				$parsedCookie->setSameSiteRestriction($sameSiteRestriction);

				if ($parsedCookie->getSameSiteRestriction() === Cookie::SAME_SITE_RESTRICTION_NONE && !$parsedCookie->isSecureOnly()) {
					\trigger_error('You may have to enable the \'session.cookie_secure\' directive in the configuration in \'php.ini\' or via the \'ini_set\' function', \E_USER_WARNING);
				}

				// сохраните файл cookie
				$parsedCookie->save();
			}
		}
	}

}
