<?php
/**
 * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
 * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
 * SPDX-License-Identifier: AGPL-3.0-only
 */
namespace Test\Files\Mount;

use OC\Files\Mount\ObjectHomeMountProvider;
use OCP\Files\Storage\IStorageFactory;
use OCP\IConfig;
use OCP\IUser;

class ObjectHomeMountProviderTest extends \Test\TestCase {
	/** @var ObjectHomeMountProvider */
	protected $provider;

	/** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
	protected $config;

	/** @var IUser|\PHPUnit\Framework\MockObject\MockObject */
	protected $user;

	/** @var IStorageFactory|\PHPUnit\Framework\MockObject\MockObject */
	protected $loader;

	protected function setUp(): void {
		parent::setUp();

		$this->config = $this->createMock(IConfig::class);
		$this->user = $this->createMock(IUser::class);
		$this->loader = $this->createMock(IStorageFactory::class);

		$this->provider = new ObjectHomeMountProvider($this->config);
	}

	public function testSingleBucket(): void {
		$this->config->expects($this->once())
			->method('getSystemValue')
			->with($this->equalTo('objectstore'), '')
			->willReturn([
				'class' => 'Test\Files\Mount\FakeObjectStore',
			]);

		$this->user->expects($this->never())->method($this->anything());
		$this->loader->expects($this->never())->method($this->anything());

		$config = $this->invokePrivate($this->provider, 'getSingleBucketObjectStoreConfig', [$this->user, $this->loader]);

		$this->assertArrayHasKey('class', $config);
		$this->assertEquals($config['class'], 'Test\Files\Mount\FakeObjectStore');
		$this->assertArrayHasKey('arguments', $config);
		$this->assertArrayHasKey('user', $config['arguments']);
		$this->assertSame($this->user, $config['arguments']['user']);
		$this->assertArrayHasKey('objectstore', $config['arguments']);
		$this->assertInstanceOf('Test\Files\Mount\FakeObjectStore', $config['arguments']['objectstore']);
	}

	public function testMultiBucket(): void {
		$this->config->expects($this->exactly(2))
			->method('getSystemValue')
			->with($this->equalTo('objectstore_multibucket'), '')
			->willReturn([
				'class' => 'Test\Files\Mount\FakeObjectStore',
			]);

		$this->user->method('getUID')
			->willReturn('uid');
		$this->loader->expects($this->never())->method($this->anything());

		$this->config->expects($this->once())
			->method('getUserValue')
			->with(
				$this->equalTo('uid'),
				$this->equalTo('homeobjectstore'),
				$this->equalTo('bucket'),
				$this->equalTo(null)
			)->willReturn(null);

		$this->config->expects($this->once())
			->method('setUserValue')
			->with(
				$this->equalTo('uid'),
				$this->equalTo('homeobjectstore'),
				$this->equalTo('bucket'),
				$this->equalTo('49'),
				$this->equalTo(null)
			);

		$config = $this->invokePrivate($this->provider, 'getMultiBucketObjectStoreConfig', [$this->user, $this->loader]);

		$this->assertArrayHasKey('class', $config);
		$this->assertEquals($config['class'], 'Test\Files\Mount\FakeObjectStore');
		$this->assertArrayHasKey('arguments', $config);
		$this->assertArrayHasKey('user', $config['arguments']);
		$this->assertSame($this->user, $config['arguments']['user']);
		$this->assertArrayHasKey('objectstore', $config['arguments']);
		$this->assertInstanceOf('Test\Files\Mount\FakeObjectStore', $config['arguments']['objectstore']);
		$this->assertArrayHasKey('bucket', $config['arguments']);
		$this->assertEquals('49', $config['arguments']['bucket']);
	}

	public function testMultiBucketWithPrefix(): void {
		$this->config->expects($this->exactly(2))
			->method('getSystemValue')
			->with('objectstore_multibucket')
			->willReturn([
				'class' => 'Test\Files\Mount\FakeObjectStore',
				'arguments' => [
					'bucket' => 'myBucketPrefix',
				],
			]);

		$this->user->method('getUID')
			->willReturn('uid');
		$this->loader->expects($this->never())->method($this->anything());

		$this->config->expects($this->once())
			->method('getUserValue')
			->with(
				$this->equalTo('uid'),
				$this->equalTo('homeobjectstore'),
				$this->equalTo('bucket'),
				$this->equalTo(null)
			)->willReturn(null);

		$this->config->expects($this->once())
			->method('setUserValue')
			->with(
				$this->equalTo('uid'),
				$this->equalTo('homeobjectstore'),
				$this->equalTo('bucket'),
				$this->equalTo('myBucketPrefix49'),
				$this->equalTo(null)
			);

		$config = $this->invokePrivate($this->provider, 'getMultiBucketObjectStoreConfig', [$this->user, $this->loader]);

		$this->assertArrayHasKey('class', $config);
		$this->assertEquals($config['class'], 'Test\Files\Mount\FakeObjectStore');
		$this->assertArrayHasKey('arguments', $config);
		$this->assertArrayHasKey('user', $config['arguments']);
		$this->assertSame($this->user, $config['arguments']['user']);
		$this->assertArrayHasKey('objectstore', $config['arguments']);
		$this->assertInstanceOf('Test\Files\Mount\FakeObjectStore', $config['arguments']['objectstore']);
		$this->assertArrayHasKey('bucket', $config['arguments']);
		$this->assertEquals('myBucketPrefix49', $config['arguments']['bucket']);
	}

	public function testMultiBucketBucketAlreadySet(): void {
		$this->config->expects($this->once())
			->method('getSystemValue')
			->with('objectstore_multibucket')
			->willReturn([
				'class' => 'Test\Files\Mount\FakeObjectStore',
				'arguments' => [
					'bucket' => 'myBucketPrefix',
				],
			]);

		$this->user->method('getUID')
			->willReturn('uid');
		$this->loader->expects($this->never())->method($this->anything());

		$this->config->expects($this->once())
			->method('getUserValue')
			->with(
				$this->equalTo('uid'),
				$this->equalTo('homeobjectstore'),
				$this->equalTo('bucket'),
				$this->equalTo(null)
			)->willReturn('awesomeBucket1');

		$this->config->expects($this->never())
			->method('setUserValue');

		$config = $this->invokePrivate($this->provider, 'getMultiBucketObjectStoreConfig', [$this->user, $this->loader]);

		$this->assertArrayHasKey('class', $config);
		$this->assertEquals($config['class'], 'Test\Files\Mount\FakeObjectStore');
		$this->assertArrayHasKey('arguments', $config);
		$this->assertArrayHasKey('user', $config['arguments']);
		$this->assertSame($this->user, $config['arguments']['user']);
		$this->assertArrayHasKey('objectstore', $config['arguments']);
		$this->assertInstanceOf('Test\Files\Mount\FakeObjectStore', $config['arguments']['objectstore']);
		$this->assertArrayHasKey('bucket', $config['arguments']);
		$this->assertEquals('awesomeBucket1', $config['arguments']['bucket']);
	}

	public function testMultiBucketConfigFirst(): void {
		$this->config->expects($this->exactly(2))
			->method('getSystemValue')
			->with('objectstore_multibucket')
			->willReturn([
				'class' => 'Test\Files\Mount\FakeObjectStore',
			]);

		$this->user->method('getUID')
			->willReturn('uid');
		$this->loader->expects($this->never())->method($this->anything());

		$mount = $this->provider->getHomeMountForUser($this->user, $this->loader);
		$this->assertInstanceOf('OC\Files\Mount\MountPoint', $mount);
	}

	public function testMultiBucketConfigFirstFallBackSingle(): void {
		$this->config->expects($this->exactly(2))
			->method('getSystemValue')
			->withConsecutive(
				[$this->equalTo('objectstore_multibucket')],
				[$this->equalTo('objectstore')],
			)->willReturnOnConsecutiveCalls(
				'',
				[
					'class' => 'Test\Files\Mount\FakeObjectStore',
				],
			);

		$this->user->method('getUID')
			->willReturn('uid');
		$this->loader->expects($this->never())->method($this->anything());

		$mount = $this->provider->getHomeMountForUser($this->user, $this->loader);
		$this->assertInstanceOf('OC\Files\Mount\MountPoint', $mount);
	}

	public function testNoObjectStore(): void {
		$this->config->expects($this->exactly(2))
			->method('getSystemValue')
			->willReturn('');

		$mount = $this->provider->getHomeMountForUser($this->user, $this->loader);
		$this->assertNull($mount);
	}
}

class FakeObjectStore {
	private $arguments;

	public function __construct(array $arguments) {
		$this->arguments = $arguments;
	}

	public function getArguments() {
		return $this->arguments;
	}
}