<?php

declare(strict_types=1);

final class Session
{
	public const TYPE_INT = 1;
	public const TYPE_STRING = 2;
	public const TYPE_BOOL = 3;

	private const IS_LOGGED_IN = 'is_logged_in';
	private const USER_ID = 'account_id';
	private const USERNAME = 'username';
	private const IS_ADMIN = 'admin';
	private const EMAIL = 'email';
	private const JABBER_ADDRESS = 'jabber';

	public function __construct()
	{
		@session_start();

		if (!$this->HasValue(self::IS_LOGGED_IN)) {
			$this->SetBool(self::IS_LOGGED_IN, false);
		}
	}

	public function Destroy(): bool
	{
		return session_unset() && session_destroy();
	}

	public function Login(string $usernameOrEmail, string $password): bool
	{
		try {
			$user = User::getFromUsername($usernameOrEmail);
		} catch (Throwable $e) {
			$user = User::getFromEmail($usernameOrEmail);
		}

		if ($user === null || !Password::IsValid($password, $user->getPassword())) {
			return false;
		}

		$this->SetBool(self::IS_LOGGED_IN, true);
		$this->SetInt(self::USER_ID, $user->getUserId());
		$this->SetString(self::USERNAME, $user->getUsername());
		$this->SetString(self::EMAIL, $user->getEmail());
		$this->SetString(self::JABBER_ADDRESS, $user->getJabberAddress());
		$this->SetBool(self::IS_ADMIN, $user->isAdmin());

		return true;
	}

	public function HasValue(string $key): bool
	{
		return self::HasSession() && isset($_SESSION[$key]);
	}

	public function SetBool(string $key, bool $value): void
	{
		$_SESSION[$key] = $value;
	}

	public function SetString(string $key, string $value): void
	{
		$_SESSION[$key] = $value;
	}

	public function SetInt(string $key, int $value): void
	{
		$_SESSION[$key] = $value;
	}

	public function IsLoggedIn(): bool
	{
		return self::HasSession() && $this->GetBool(self::IS_LOGGED_IN);
	}

	public function GetInt(string $key): ?int
	{
		return $this->HasValue($key) ? (int)$_SESSION[$key] : null;
	}

	public function GetString(string $key): ?string
	{
		return $this->HasValue($key) ? (string)$_SESSION[$key] : null;
	}

	public function GetBool(string $key): ?bool
	{
		return $this->HasValue($key) ? (bool)$_SESSION[$key] : null;
	}

	public function getUserId(): ?int
	{
		return $this->GetInt(self::USER_ID);
	}

	public function isAdmin(): bool
	{
		return $this->GetBool(self::IS_ADMIN);
	}

	public static function HasSession(): bool
	{
		return isset($_SESSION);
	}
}