<?php

declare(strict_types=1);

/**
 * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
 * SPDX-License-Identifier: AGPL-3.0-or-later
 */

namespace OCA\Circles;

use Exception;
use OCA\Circles\Exceptions\CircleNotFoundException;
use OCA\Circles\Exceptions\ContactAddressBookNotFoundException;
use OCA\Circles\Exceptions\ContactFormatException;
use OCA\Circles\Exceptions\ContactNotFoundException;
use OCA\Circles\Exceptions\FederatedEventException;
use OCA\Circles\Exceptions\FederatedItemException;
use OCA\Circles\Exceptions\FederatedUserException;
use OCA\Circles\Exceptions\FederatedUserNotFoundException;
use OCA\Circles\Exceptions\InitiatorNotConfirmedException;
use OCA\Circles\Exceptions\InitiatorNotFoundException;
use OCA\Circles\Exceptions\InvalidIdException;
use OCA\Circles\Exceptions\OwnerNotFoundException;
use OCA\Circles\Exceptions\RemoteInstanceException;
use OCA\Circles\Exceptions\RemoteNotFoundException;
use OCA\Circles\Exceptions\RemoteResourceNotFoundException;
use OCA\Circles\Exceptions\RequestBuilderException;
use OCA\Circles\Exceptions\ShareWrapperNotFoundException;
use OCA\Circles\Exceptions\SingleCircleNotFoundException;
use OCA\Circles\Exceptions\UnknownRemoteException;
use OCA\Circles\FederatedItems\Files\FileShare;
use OCA\Circles\FederatedItems\Files\FileUnshare;
use OCA\Circles\Model\Federated\FederatedEvent;
use OCA\Circles\Model\Member;
use OCA\Circles\Model\Probes\CircleProbe;
use OCA\Circles\Model\Probes\DataProbe;
use OCA\Circles\Model\ShareWrapper;
use OCA\Circles\Service\CircleService;
use OCA\Circles\Service\EventService;
use OCA\Circles\Service\FederatedEventService;
use OCA\Circles\Service\FederatedUserService;
use OCA\Circles\Service\ShareTokenService;
use OCA\Circles\Service\ShareWrapperService;
use OCA\Circles\Tools\Traits\TArrayTools;
use OCA\Circles\Tools\Traits\TNCLogger;
use OCA\Circles\Tools\Traits\TStringTools;
use OCP\Files\Folder;
use OCP\Files\InvalidPathException;
use OCP\Files\IRootFolder;
use OCP\Files\Node;
use OCP\Files\NotFoundException;
use OCP\IL10N;
use OCP\IURLGenerator;
use OCP\IUserManager;
use OCP\Share\Exceptions\AlreadySharedException;
use OCP\Share\Exceptions\IllegalIDChangeException;
use OCP\Share\Exceptions\ShareNotFound;
use OCP\Share\IPartialShareProvider;
use OCP\Share\IShare;
use OCP\Share\IShareProvider;
use OCP\Share\IShareProviderGetUsers;
use Psr\Log\LoggerInterface;

/**
 * Class ShareByCircleProvider
 *
 * @package OCA\Circles
 */
class ShareByCircleProvider implements IShareProvider, IPartialShareProvider, IShareProviderGetUsers {
	use TArrayTools;
	use TStringTools;
	use TNCLogger;

	public const IDENTIFIER = 'ocCircleShare';

	public function __construct(
		private IUserManager $userManager,
		private IRootFolder $rootFolder,
		private IL10N $l10n,
		private LoggerInterface $logger,
		private IURLGenerator $urlGenerator,
		private ShareWrapperService $shareWrapperService,
		private ShareTokenService $shareTokenService,
		private FederatedUserService $federatedUserService,
		private FederatedEventService $federatedEventService,
		private CircleService $circleService,
		private EventService $eventService,
	) {
	}

	public function identifier(): string {
		return self::IDENTIFIER;
	}

	/**
	 * @param IShare $share
	 *
	 * @return IShare
	 * @throws AlreadySharedException
	 * @throws CircleNotFoundException
	 * @throws FederatedEventException
	 * @throws FederatedItemException
	 * @throws InitiatorNotConfirmedException
	 * @throws OwnerNotFoundException
	 * @throws RemoteInstanceException
	 * @throws RemoteNotFoundException
	 * @throws RemoteResourceNotFoundException
	 * @throws UnknownRemoteException
	 * @throws FederatedUserException
	 * @throws FederatedUserNotFoundException
	 * @throws IllegalIDChangeException
	 * @throws InitiatorNotFoundException
	 * @throws InvalidIdException
	 * @throws InvalidPathException
	 * @throws NotFoundException
	 * @throws RequestBuilderException
	 * @throws ShareNotFound
	 * @throws SingleCircleNotFoundException
	 */
	public function create(IShare $share): IShare {
		if ($share->getShareType() !== IShare::TYPE_CIRCLE) {
			return $share;
		}

		$nodeId = $share->getNode()
			->getId();

		try {
			$knowShareWrapper = $this->shareWrapperService->searchShare($share->getSharedWith(), $nodeId);
			throw new AlreadySharedException(
				$this->l10n->t('This item is already shared with this team'),
				$knowShareWrapper->getShare($this->rootFolder, $this->userManager, $this->urlGenerator)
			);
		} catch (ShareWrapperNotFoundException $e) {
		}

		$this->federatedUserService->initCurrentUser();
		$circleProbe = new CircleProbe();
		$dataProbe = new DataProbe();
		$dataProbe->add(DataProbe::OWNER)
			->add(DataProbe::INITIATOR, [DataProbe::BASED_ON]);

		$circle = $this->circleService->probeCircle($share->getSharedWith(), $circleProbe, $dataProbe);
		$share->setMailSend(true);
		$owner = $circle->getInitiator();
		$this->shareWrapperService->save($share);

		try {
			$wrappedShare = $this->shareWrapperService->getShareById((int)$share->getId());
			$wrappedShare->setOwner($owner);
		} catch (ShareWrapperNotFoundException $e) {
			throw new ShareNotFound();
		}

		$event = new FederatedEvent(FileShare::class);
		$event->setCircle($circle);
		$event->getParams()->sObj('wrappedShare', $wrappedShare);

		$this->federatedEventService->newEvent($event);
		$this->eventService->localShareCreated($wrappedShare);

		return $wrappedShare->getShare($this->rootFolder, $this->userManager, $this->urlGenerator);
	}

	/**
	 * @param IShare $share
	 *
	 * @return IShare
	 * @throws IllegalIDChangeException
	 * @throws ShareWrapperNotFoundException
	 * @throws RequestBuilderException
	 */
	public function update(IShare $share): IShare {
		$wrappedShare = $this->shareWrapperService->getShareById((int)$share->getId());
		$wrappedShare->setPermissions($share->getPermissions())
			->setShareOwner($share->getShareOwner())
			->setAttributes($share->getAttributes())
			->setSharedBy($share->getSharedBy())
			->setExpirationDate($share->getExpirationDate())
			->setShareNote($share->getNote());

		$this->shareWrapperService->update($wrappedShare);
		$this->shareWrapperService->updateChildPermissions($wrappedShare);

		return $wrappedShare->getShare($this->rootFolder, $this->userManager, $this->urlGenerator);
	}

	/**
	 * @param IShare $share
	 *
	 * @throws FederatedEventException
	 * @throws FederatedItemException
	 * @throws FederatedUserException
	 * @throws FederatedUserNotFoundException
	 * @throws InitiatorNotConfirmedException
	 * @throws InvalidIdException
	 * @throws OwnerNotFoundException
	 * @throws RemoteInstanceException
	 * @throws RemoteNotFoundException
	 * @throws RemoteResourceNotFoundException
	 * @throws RequestBuilderException
	 * @throws SingleCircleNotFoundException
	 * @throws UnknownRemoteException
	 */
	public function delete(IShare $share): void {
		if ($share->getShareType() !== IShare::TYPE_CIRCLE) {
			return;
		}

		$this->federatedUserService->initCurrentUser();
		try {
			$wrappedShare = $this->shareWrapperService->getShareById((int)$share->getId());
		} catch (ShareWrapperNotFoundException $e) {
			return;
		}

		$this->shareWrapperService->delete($wrappedShare);

		try {
			$circle = $this->circleService->getCircle($share->getSharedWith());
		} catch (CircleNotFoundException $e) {
			return;
		} catch (InitiatorNotFoundException $e) {
			// force the unshare ?
			return;
		}

		$event = new FederatedEvent(FileUnshare::class);
		$event->setCircle($circle)
			->getParams()->sObj('wrappedShare', $wrappedShare);

		$this->federatedEventService->newEvent($event);
		$this->eventService->localShareDeleted($wrappedShare);
	}

	/**
	 * @param IShare $share
	 * @param string $recipient
	 *
	 * @throws ContactAddressBookNotFoundException
	 * @throws ContactFormatException
	 * @throws ContactNotFoundException
	 * @throws FederatedUserException
	 * @throws FederatedUserNotFoundException
	 * @throws InvalidIdException
	 * @throws NotFoundException
	 * @throws RequestBuilderException
	 * @throws ShareWrapperNotFoundException
	 * @throws SingleCircleNotFoundException
	 */
	public function deleteFromSelf(IShare $share, $recipient): void {
		$federatedUser = $this->federatedUserService->getLocalFederatedUser($recipient);
		$child = $this->shareWrapperService->getChild($share, $federatedUser);
		$this->debug('Shares::move()', ['federatedUser' => $federatedUser, 'child' => $child]);

		if ($child->getPermissions() > 0) {
			$child->setPermissions(0);
			$this->shareWrapperService->update($child);
		}
	}


	/**
	 * @param IShare $share
	 * @param string $recipient
	 *
	 * @return IShare
	 * @throws ContactAddressBookNotFoundException
	 * @throws ContactFormatException
	 * @throws ContactNotFoundException
	 * @throws FederatedUserException
	 * @throws FederatedUserNotFoundException
	 * @throws IllegalIDChangeException
	 * @throws InvalidIdException
	 * @throws NotFoundException
	 * @throws RequestBuilderException
	 * @throws ShareWrapperNotFoundException
	 * @throws SingleCircleNotFoundException
	 */
	public function move(IShare $share, $recipient): IShare {
		$federatedUser = $this->federatedUserService->getLocalFederatedUser($recipient);
		$child = $this->shareWrapperService->getChild($share, $federatedUser);
		$this->debug('Shares::move()', ['federatedUser' => $federatedUser, 'child' => $child]);

		if ($child->getFileTarget() !== $share->getTarget()) {
			$child->setFileTarget($share->getTarget());
			$this->shareWrapperService->update($child);
		}

		$wrappedShare = $this->shareWrapperService->getShareById((int)$share->getId(), $federatedUser);

		return $wrappedShare->getShare($this->rootFolder, $this->userManager, $this->urlGenerator);
	}


	/**
	 * @param IShare $share
	 * @param string $recipient
	 *
	 * @return IShare
	 */
	public function restore(IShare $share, string $recipient): IShare {
		$orig = $this->shareWrapperService->getShareById((int)$share->getId());

		$federatedUser = $this->federatedUserService->getLocalFederatedUser($recipient);
		$child = $this->shareWrapperService->getChild($share, $federatedUser);
		$this->debug('Shares::restore()', ['federatedUser' => $federatedUser, 'child' => $child]);

		if ($child->getPermissions() !== $orig->getPermissions()) {
			$child->setPermissions($orig->getPermissions());
			$this->shareWrapperService->update($child);
		}

		$wrappedShare = $this->shareWrapperService->getShareById((int)$share->getId(), $federatedUser);

		return $wrappedShare->getShare($this->rootFolder, $this->userManager, $this->urlGenerator);
	}


	/**
	 * @param string $userId
	 * @param Folder $node
	 * @param bool $reshares
	 * @param bool $shallow Whether the method should stop at the first level, or look into sub-folders.
	 *
	 * @return array
	 * @throws ContactAddressBookNotFoundException
	 * @throws ContactFormatException
	 * @throws ContactNotFoundException
	 * @throws FederatedUserException
	 * @throws FederatedUserNotFoundException
	 * @throws IllegalIDChangeException
	 * @throws InvalidIdException
	 * @throws InvalidPathException
	 * @throws NotFoundException
	 * @throws RequestBuilderException
	 * @throws SingleCircleNotFoundException
	 */
	public function getSharesInFolder($userId, Folder $node, $reshares, $shallow = true): array {
		$federatedUser = $this->federatedUserService->getLocalFederatedUser($userId);
		$wrappedShares = $this->shareWrapperService->getSharesInFolder(
			$federatedUser,
			$node,
			$reshares,
			$shallow
		);

		$result = [];
		foreach ($wrappedShares as $wrappedShare) {
			if (!array_key_exists($wrappedShare->getFileSource(), $result)) {
				$result[$wrappedShare->getFileSource()] = [];
			}
			if ($wrappedShare->getFileCache()->isAccessible()) {
				$result[$wrappedShare->getFileSource()][] =
					$wrappedShare->getShare($this->rootFolder, $this->userManager, $this->urlGenerator);
			} else {
				$this->logger->debug('shared document is not available anymore', ['wrappedShare' => $wrappedShare]);
				if ($wrappedShare->getFileCache()->getPath() === '') {
					$this->logger->notice('share is not available while path is empty. might comes from an unsupported storage.', ['wrappedShare' => $wrappedShare]);
				}
			}
		}

		return $result;
	}


	/**
	 * @param string $userId
	 * @param int $shareType
	 * @param Node|null $node
	 * @param bool $reshares
	 * @param int $limit
	 * @param int $offset
	 *
	 * @return IShare[]
	 * @throws ContactAddressBookNotFoundException
	 * @throws ContactFormatException
	 * @throws ContactNotFoundException
	 * @throws FederatedUserException
	 * @throws FederatedUserNotFoundException
	 * @throws IllegalIDChangeException
	 * @throws InvalidIdException
	 * @throws InvalidPathException
	 * @throws NotFoundException
	 * @throws RequestBuilderException
	 * @throws SingleCircleNotFoundException
	 */
	public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset): array {
		if ($shareType !== IShare::TYPE_CIRCLE) {
			return [];
		}

		$nodeId = (!is_null($node)) ? $node->getId() : 0;

		try {
			$federatedUser = $this->federatedUserService->getLocalFederatedUser($userId);
		} catch (Exception $e) {
			$this->e($e, ['userId' => $userId, 'shareType' => $shareType, 'nodeId' => $nodeId]);

			return [];
		}

		$wrappedShares = $this->shareWrapperService->getSharesBy(
			$federatedUser,
			$nodeId,
			$reshares,
			$limit,
			$offset,
			true
		);

		return array_filter(
			array_map(
				function (ShareWrapper $wrapper) {
					return $wrapper->getShare($this->rootFolder, $this->userManager, $this->urlGenerator);
				}, $wrappedShares
			)
		);
	}


	/**
	 * @param string $shareId
	 * @param string|null $recipientId
	 *
	 * @return IShare
	 * @throws FederatedUserException
	 * @throws FederatedUserNotFoundException
	 * @throws IllegalIDChangeException
	 * @throws InvalidIdException
	 * @throws ShareNotFound
	 * @throws SingleCircleNotFoundException
	 * @throws RequestBuilderException
	 */
	public function getShareById($shareId, $recipientId = null): IShare {
		if (is_null($recipientId)) {
			$federatedUser = null;
		} else {
			$federatedUser = $this->federatedUserService->getLocalFederatedUser($recipientId);
		}

		try {
			$wrappedShare = $this->shareWrapperService->getShareById((int)$shareId, $federatedUser);
		} catch (ShareWrapperNotFoundException $e) {
			throw new  ShareNotFound();
		}

		return $wrappedShare->getShare($this->rootFolder, $this->userManager, $this->urlGenerator);
	}


	/**
	 * @param Node $path
	 *
	 * @return IShare[]
	 * @throws InvalidPathException
	 * @throws NotFoundException
	 * @throws IllegalIDChangeException
	 * @throws RequestBuilderException
	 */
	public function getSharesByPath(Node $path): array {
		$wrappedShares = $this->shareWrapperService->getSharesByFileId($path->getId(), true);

		return array_filter(
			array_map(
				function (ShareWrapper $wrapper) {
					return $wrapper->getShare($this->rootFolder, $this->userManager, $this->urlGenerator);
				}, $wrappedShares
			)
		);
	}


	/**
	 * @param string $userId
	 * @param int $shareType
	 * @param Node|null $node
	 * @param int $limit
	 * @param int $offset
	 *
	 * @return IShare[]
	 * @throws ContactAddressBookNotFoundException
	 * @throws ContactFormatException
	 * @throws ContactNotFoundException
	 * @throws FederatedUserException
	 * @throws FederatedUserNotFoundException
	 * @throws IllegalIDChangeException
	 * @throws InvalidIdException
	 * @throws InvalidPathException
	 * @throws NotFoundException
	 * @throws RequestBuilderException
	 * @throws SingleCircleNotFoundException
	 */
	public function getSharedWith($userId, $shareType, $node, $limit, $offset): array {
		if ($shareType !== IShare::TYPE_CIRCLE) {
			return [];
		}

		try {
			$federatedUser = $this->federatedUserService->getLocalFederatedUser($userId);
		} catch (FederatedUserNotFoundException) {
			return [];
		}

		$probe = new CircleProbe();
		$probe->includePersonalCircles()
			->includeSystemCircles()
			->mustBeMember()
			->setItemsLimit((int)$limit)
			->setItemsOffset((int)$offset);

		$wrappedShares = $this->shareWrapperService->getSharedWith(
			$federatedUser,
			(!is_null($node)) ? $node->getId() : 0,
			$probe
		);

		return array_filter(
			array_map(
				function (ShareWrapper $wrapper) {
					return $wrapper->getShare(
						$this->rootFolder, $this->userManager, $this->urlGenerator, true
					);
				}, $wrappedShares
			)
		);
	}

	public function getSharedWithByPath(
		string $userId,
		int $shareType,
		string $path,
		bool $forChildren,
		int $limit,
		int $offset,
	): iterable {
		if ($shareType !== IShare::TYPE_CIRCLE) {
			return [];
		}

		$federatedUser = $this->federatedUserService->getLocalFederatedUser($userId);
		$probe = new CircleProbe();
		$probe->includePersonalCircles()
			->includeSystemCircles()
			->mustBeMember()
			->setItemsLimit($limit)
			->setItemsOffset($offset);

		$wrappedShares = $this->shareWrapperService->getSharedWithByPath(
			$federatedUser,
			$path,
			$forChildren,
			$probe,
		);

		/** @var array<string, IShare> */
		return array_filter(
			array_map(
				function (ShareWrapper $wrapper) {
					return $wrapper->getShare(
						$this->rootFolder, $this->userManager, $this->urlGenerator, true
					);
				}, $wrappedShares
			)
		);
	}

	/**
	 * @param string $token
	 *
	 * @return IShare
	 * @throws IllegalIDChangeException
	 * @throws RequestBuilderException
	 * @throws ShareNotFound
	 */
	public function getShareByToken($token): IShare {
		if (is_null($token)) {
			throw new ShareNotFound();
		}

		try {
			$wrappedShare = $this->shareWrapperService->getShareByToken($token);
		} catch (ShareWrapperNotFoundException $e) {
			throw new ShareNotFound();
		}

		$share = $wrappedShare->getShare($this->rootFolder, $this->userManager, $this->urlGenerator);
		if ($share->getPassword() !== '') {
			$this->logger->notice('share is protected by a password, hash: ' . $share->getPassword());
		}

		return $share;
	}


	public function formatShare(IShare $share): array {
		$this->federatedUserService->initCurrentUser();
		$circleProbe = new CircleProbe();
		$dataProbe = new DataProbe();

		$result = ['share_with' => $share->getSharedWith()];
		try {
			$circle = $this->circleService->probeCircle($share->getSharedWith(), $circleProbe, $dataProbe);
			$result['share_with_displayname'] = $circle->getDisplayName();
		} catch (Exception $e) {
			$this->logger->warning(
				'Circle not found while probeCircle',
				[
					'sharedWith' => $share->getSharedWith(),
					'exception' => $e
				]
			);
		}

		return $result;
	}


	/**
	 * @param string $uid
	 * @param int $shareType
	 */
	public function userDeleted($uid, $shareType): void {
	}


	/**
	 * @param string $gid
	 */
	public function groupDeleted($gid): void {
	}


	/**
	 * @param string $uid
	 * @param string $gid
	 */
	public function userDeletedFromGroup($uid, $gid): void {
	}


	/**
	 * if $currentAccess, returns long version of the access list:
	 * [
	 *   'users'  => [
	 *     'user1' => ['node_id' => 42, 'node_path' => '/fileA'],
	 *     'user4' => ['node_id' => 32, 'node_path' => '/folder2'],
	 *     'user2' => ['node_id' => 23, 'node_path' => '/folder (1)'],
	 *   ],
	 *   'remote' => [
	 *     'user1@server1' => ['node_id' => 42, 'token' => 'SeCr3t'],
	 *     'user2@server2' => ['node_id' => 23, 'token' => 'FooBaR'],
	 *   ],
	 *   'public' => bool,
	 *   'mail' => [
	 *     'email1@maildomain1' => ['node_id' => 42, 'token' => 'aBcDeFg'],
	 *     'email2@maildomain2' => ['node_id' => 23, 'token' => 'hIjKlMn'],
	 *   ]
	 * ]
	 *
	 *
	 *
	 *
	 * @param Node[] $nodes
	 * @param bool $currentAccess
	 *
	 * @return array
	 */
	public function getAccessList($nodes, $currentAccess): array {
		$ids = [];
		foreach ($nodes as $node) {
			$ids[] = $node->getId();
		}


		if (!$currentAccess) {
			return $this->getAccessListShort($ids);
		}

		$shareIds = $knownIds = $users = $remote = $mails = [];
		foreach ($this->shareWrapperService->getSharesByFileIds($ids, true, true) as $share) {
			if (!$share->hasCircle()) {
				continue;
			}

			$shareIds[] = $share->getId();
			$circle = $share->getCircle();
			foreach ($circle->getInheritedMembers() as $member) {
				if ($share->getParent() > 0 && in_array($member->getSingleId(), $knownIds[$share->getFileSource()] ?? [])) {
					continue;
				}
				$knownIds[$share->getFileSource()][] = $member->getSingleId();

				switch ($member->getUserType()) {
					case Member::TYPE_USER:
						if ($member->isLocal()) {
							$users[$member->getUserId()] = [
								'node_id' => $share->getFileSource(),
								'node_path' => $share->getFileTarget()
							];
						} else {
							// we only store temp value, as token is unknown at this point
							$remote[$member->getUserid() . '@' . $member->getInstance()] = [
								'node_id' => $share->getFileSource(),
								'shareId' => $share->getId(),
								'memberId' => $member->getId(),
							];
						}
						break;
					case Member::TYPE_MAIL:
						// we only store temp value, as token is unknown at this point
						$mails[$member->getUserId()] = [
							'node_id' => $share->getFileSource(),
							'shareId' => $share->getId(),
							'memberId' => $member->getId(),
						];
						break;
				}
			}
		}

		// list share tokens in an indexed array and update details for remote/mail entries with the correct token
		$shareTokens = [];
		foreach ($this->shareTokenService->getTokensFromShares(array_values(array_unique($shareIds))) as $shareToken) {
			$shareTokens[$shareToken->getShareId()][$shareToken->getMemberId()] = $shareToken->getToken();
		}

		return [
			'users' => $users,
			'remote' => $this->updateAccessListTokens($remote, $shareTokens),
			'email' => $this->updateAccessListTokens($mails, $shareTokens)
		];
	}

	/**
	 * returns short version of the access list:
	 * [
	 *   'users'  => ['user1', 'user2', 'user4'],
	 *   'remote' => bool,
	 *   'mail' => ['email1@maildomain1', 'email2@maildomain2']
	 * ]
	 *
	 * @param array $ids
	 *
	 * @return array
	 * @throws FederatedItemException
	 * @throws RemoteInstanceException
	 * @throws RemoteNotFoundException
	 * @throws RemoteResourceNotFoundException
	 * @throws RequestBuilderException
	 * @throws UnknownRemoteException
	 */
	private function getAccessListShort(array $ids): array {
		$users = $mails = [];
		$remote = false;
		foreach ($this->shareWrapperService->getSharesByFileIds($ids, true) as $share) {
			if (!$share->hasCircle()) {
				continue;
			}
			$circle = $share->getCircle();
			foreach ($circle->getInheritedMembers() as $member) {
				switch ($member->getUserType()) {
					case Member::TYPE_USER:
						if ($member->isLocal()) {
							if (!in_array($member->getUserId(), $users)) {
								$users[] = $member->getUserId();
							}
						} else {
							$remote = true;
						}
						break;
					case Member::TYPE_MAIL:
						if (!in_array($member->getUserId(), $mails)) {
							$mails[] = $member->getUserId();
						}
						break;
				}
			}
		}

		return [
			'users' => $users,
			'remote' => $remote,
			'email' => $mails
		];
	}

	/**
	 * @param array $list
	 * @param array<int, array<string, string>> $shareTokens
	 *
	 * @return array
	 */
	private function updateAccessListTokens(array $list, array $shareTokens): array {
		$result = [];
		foreach ($list as $id => $data) {
			$result[$id] = [
				'node_id' => $data['node_id'],
				'token' => $shareTokens[$data['shareId']][$data['memberId']]
			];
		}

		return $result;
	}

	/**
	 * We don't return a thing about children.
	 * The call to this function is deprecated and should be removed in next release of NC.
	 * Also, we get the children in the delete() method.
	 *
	 * @param IShare $parent
	 *
	 * @return IShare[]
	 */
	public function getChildren(IShare $parent): array {
		return [];
	}


	/**
	 * @return iterable
	 */
	public function getAllShares(): iterable {
		//		$qb = $this->dbConnection->getQueryBuilder();
		//
		//		$qb->select(' * ')
		//		   ->from('share')
		//		   ->where(
		//			   $qb->expr()
		//				  ->orX(
		//					  $qb->expr()
		//						 ->eq('share_type', $qb->createNamedParameter(IShare::TYPE_CIRCLE))
		//				  )
		//		   );
		//
		//		$cursor = $qb->executeQuery();
		//		while ($data = $cursor->fetch()) {
		//			try {
		//				yield $this->createShareObject($data);
		//			} catch (IllegalIDChangeException $e) {
		//			};
		//		}
		//		$cursor->closeCursor();
		return [];
	}

	public function getUsersForShare(IShare $share): iterable {
		$members = $this->shareWrapperService->getShareById((int)$share->getId())->getCircle()->getInheritedMembers();
		foreach ($members as $member) {
			if ($member->getUserType() === Member::TYPE_USER) {
				yield $this->userManager->getExistingUser($member->getUserId());
			}
		}
	}
}
