<?php

declare(strict_types=1);

final class User extends MySqlTable implements JsonSerializable
{
	public const FIELD_ID = 'UserId';
	public const FIELD_USERNAME = 'Username';
	public const FIELD_PASSWORD = 'Password';
	public const FIELD_EMAIL = 'Email';
	public const FIELD_JABBER_ADDRESS = 'JabberAddress';
	public const FIELD_ADMIN = 'IsAdmin';

	public function __construct($id = null, DatabaseInterface &$database = null)
	{
		parent::__construct(self::class, $id, $database);
	}

	public function getUserId(): ?int
	{
		if ($this->getPrimaryKey() === null) {
			return null;
		}

		return (int)$this->getPrimaryKey();
	}

	public function getUsername(): string
	{
		return $this->getField(self::FIELD_USERNAME);
	}

	public function getPassword(): string
	{
		return $this->getField(self::FIELD_PASSWORD);
	}

	public function getEmail(): string
	{
		return $this->getField(self::FIELD_EMAIL);
	}

	public function getJabberAddress(): string
	{
		return $this->getField(self::FIELD_JABBER_ADDRESS);
	}

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

	public function setUsername(string $username): void
	{
		$this->setField(self::FIELD_USERNAME, $username);
	}

	public function setPassword(string $password): void
	{
		$this->setField(self::FIELD_PASSWORD, $password);
	}

	public function setEmail(string $email): void
	{
		$this->setField(self::FIELD_EMAIL, $email);
	}

	public function setJabberAddress(string $jabberAddress): void
	{
		$this->setField(self::FIELD_JABBER_ADDRESS, $jabberAddress);
	}

	public function setAdmin(bool $isAdmin): void
	{
		$this->setField(self::FIELD_ADMIN, $isAdmin);
	}

	public static function getFromUsername(string $username, DatabaseInterface &$database = null): self
	{
		$databaseGiven = true;

		if ($database === null) {
			$database = new MySqlDatabase();
			$databaseGiven = false;
		}

		if ($database->Count(self::class, [self::FIELD_USERNAME => $username]) === 0) {
			throw new UserException(sprintf('No user with name %s found!', $username));
		}

		$id = $database->Select(self::class, [self::FIELD_ID], [self::FIELD_USERNAME => $username])[0][self::FIELD_ID];

		$user = $databaseGiven ? new User((int)$id, $database) : new User((int)$id);

		return $user;
	}

	public static function getFromEmail(string $email, DatabaseInterface &$database = null): self
	{
		$databaseGiven = true;

		if ($database === null) {
			$database = new MySqlDatabase();
			$databaseGiven = false;
		}

		if ($database->Count(self::class) === 0) {
			throw new UserException(sprintf('No user with email %s found!', $email));
		}

		$id = $database->Select(self::class, [self::FIELD_ID], [self::FIELD_EMAIL => $email])[0][self::FIELD_ID];

		$user = $databaseGiven ? new User((int)$id, $database) : new User((int)$id);

		return $user;
	}

	public function getFingerprintIds(): array
	{
		$result = $this->database->Select(
			Fingerprint::class,
			[Fingerprint::FIELD_ID],
			[Fingerprint::FIELD_USER => $this->getUserId()]
		);

		$ids = [];

		foreach ($result as $record) {
			$ids[] = (int)$record[Fingerprint::FIELD_ID];
		}

		return $ids;
	}

	public function isSharingWith(int $userId): bool
	{
		return (bool)$this->database->Count(
			Sharing::class,
			[Sharing::FIELD_USER => $this->getUserId(), Sharing::FIELD_USER_SHARED => $userId]
		);
	}

	public function jsonSerialize()
	{
		return [
			'userId' => $this->getUserId(),
			'username' => $this->getUsername(),
			'jabberAddress' => $this->getJabberAddress(),
			'isAdmin' => $this->isAdmin(),
			'fingerprintIds' => $this->getFingerprintIds()
		];
	}
}