<?php

use PHPUnit\Framework\TestCase;

class baseprojectTest extends TestCase {


	public int $now;
	public array $user1;
	public array $user2;
	public array $user3;
	public array $project1;
	public array $group1;

	/**
	 * @throws ServerException
	 * @throws BadRequestException
	 * @throws NotFoundException
	 * @throws ForbiddenException
	 * @throws AuthorizationRequiredException
	 */
	public function setUp(): void {
		$now=time();
		$this->now=$now;
		database::connect();
		database::begin();
		session::init(new DummySession());
		session::becomeAdmin();
		$admin=baseuser::getFirstAdmin();
		session::setUserToSession($admin['name']);
		//User 1 will own the project
		$this->user1=user::create([
			'name'=>"user1_$now",
			'fullname'=>"user1_$now",
			'email'=>"user1_$now@bogus.bogus"
		])['created'];
		//User 2 will have permission to see the project
		$this->user2=user::create([
			'name'=>"user2_$now",
			'fullname'=>"user2_$now",
			'email'=>"user2_$now@bogus.bogus"
		])['created'];
		//User 3 will not have permission to see the project (unless made admin)
		$this->user3=user::create([
			'name'=>"user3_$now",
			'fullname'=>"user3_$now",
			'email'=>"user3_$now@bogus.bogus"
		])['created'];
		//Create the project
		$this->project1=baseproject::create([
			'name'=>"project1_$now",
			'description'=>"test $now",
			'owner'=>$this->user1['id']
		])['created'];
		//Create the usergroup
		database::query('INSERT INTO usergroup(name) VALUES("group1_'.$now.'")');
		$groupId=database::getLastInsertId();
		$this->group1=baseusergroup::getById($groupId);
		//Add user 1 to the group
		database::query('INSERT INTO groupmembership(userid,usergroupid) VALUES('.$this->user2['id'].','.$groupId.')');
		//Give the group all permissions on the project
		foreach (['read','create','update','delete'] as $type){
			permission::create([
				'projectid'=>$this->project1['id'],
				'usergroupid'=>$groupId,
				'type'=>$type
			]);
		}
		session::revokeAdmin();
	}

	public function tearDown(): void {
		database::abort();
		session::destroy();
	}

	public function testGetFieldValidations() {
		$result=baseproject::getFieldValidations();
		self::assertIsArray($result);
		self::assertArrayHasKey('name', $result);
		self::assertEquals(validator::REQUIRED, $result['name']);
	}

	public function testGetHelpTexts() {
		$result=baseproject::getFieldHelpTexts();
		self::assertIsArray($result);
		self::assertArrayHasKey('name', $result);
		self::assertStringContainsString('name', $result['name']);
	}

	/**
	 * @throws NotFoundException
	 * @throws BadRequestException
	 * @throws AuthorizationRequiredException
	 * @throws ServerException
	 */
	public function testGetByIdAsAdmin() {
		$projectId=$this->project1['id'];
		session::setUserToSession($this->user3['name']);
		session::becomeAdmin();
		$project=baseproject::getById($projectId);
		self::assertNotNull($project, 'Project not visible to admin, getById');
		self::assertEquals($projectId,$project['id']);
		database::query('UPDATE project SET isarchived=1 WHERE id=:id', [':id'=>$projectId]);
		session::setShowArchivedProjects(true);
		session::becomeAdmin();
		$project=baseproject::getById($projectId);
		self::assertNotNull($project, 'Archived project not visible to admin, getById, showArchivedProjects true');
		self::assertEquals($projectId,$project['id']);
		session::setShowArchivedProjects(false);
		session::becomeAdmin();
		$project=baseproject::getById($projectId);
		self::assertNull($project, 'Archived project visible to admin, getById, showArchivedProjects false');
	}

	/**
	 * @throws BadRequestException
	 * @throws AuthorizationRequiredException
	 * @throws ServerException
	 * @throws NotFoundException
	 */
	public function testGetByIdAsOwner() {
		$projectId=$this->project1['id'];
		session::setUserToSession($this->user1['name']);
		$project=baseproject::getById($projectId);
		self::assertNotNull($project, 'Project not visible to owner, getById');
		self::assertEquals($projectId,$project['id']);
		database::query('UPDATE project SET isarchived=1 WHERE id=:id', [':id'=>$projectId]);
		session::setShowArchivedProjects(true);
		$project=baseproject::getById($projectId);
		self::assertNotNull($project, 'Archived project not visible to owner, getById, showArchivedProjects true');
		self::assertEquals($projectId,$project['id']);
		session::setShowArchivedProjects(false);
		$project=baseproject::getById($projectId);
		self::assertNull($project, 'Archived project visible to owner, getById, showArchivedProjects false');
	}

	/**
	 * @throws BadRequestException
	 * @throws AuthorizationRequiredException
	 * @throws ServerException
	 * @throws NotFoundException
	 */
	public function testGetByIdAsOtherPermitted() {
		$projectId=$this->project1['id'];
		session::setUserToSession($this->user2['name']);
		$project=baseproject::getById($projectId);
		self::assertNotNull($project, 'Project not visible to permitted user, getById');
		self::assertEquals($projectId,$project['id']);
		database::query('UPDATE project SET isarchived=1 WHERE id=:id', [':id'=>$projectId]);
		session::setShowArchivedProjects(true);
		$project=baseproject::getById($projectId);
		self::assertNotNull($project, 'Archived project not visible to permitted user, getById, showArchivedProjects true');
		self::assertEquals($projectId,$project['id']);
		session::setShowArchivedProjects(false);
		$project=baseproject::getById($projectId);
		self::assertNull($project, 'Archived project visible to permitted user, getById, showArchivedProjects false');
	}

	/**
	 * @throws ServerException
	 * @throws BadRequestException
	 * @throws AuthorizationRequiredException
	 * @throws NotFoundException
	 */
	public function testGetByIdAsOtherNotPermitted() {
		$projectId=$this->project1['id'];
		session::setUserToSession($this->user3['name']);
		$project=baseproject::getById($projectId);
		self::assertNull($project, 'Project was visible to non-permitted user, getById');
		database::query('UPDATE project SET isarchived=1 WHERE id=:id', [':id'=>$projectId]);
		session::setShowArchivedProjects(true);
		$project=baseproject::getById($projectId);
		self::assertNull($project, 'Project was visible to non-permitted user, getById (archived; showArchivedProjects true');
		session::setShowArchivedProjects(false);
		$project=baseproject::getById($projectId);
		self::assertNull($project, 'Project was visible to non-permitted user, getById (archived; showArchivedProjects false');
	}

	/**
	 * @throws NotFoundException
	 * @throws BadRequestException
	 * @throws AuthorizationRequiredException
	 * @throws ServerException
	 */
	public function testGetByNameAsAdmin() {
		$projectName=$this->project1['name'];
		session::setUserToSession($this->user3['name']);
		session::becomeAdmin();
		$project=baseproject::getByName($projectName);
		self::assertNotNull($project, 'Project not visible to admin, getByName');
		self::assertEquals($projectName,$project['name']);
		database::query('UPDATE project SET isarchived=1 WHERE id=:id', [':id'=>$this->project1['id']]);
		session::setShowArchivedProjects(true);
		session::becomeAdmin();
		$project=baseproject::getByName($projectName);
		self::assertNotNull($project, 'Archived project not visible to admin, getByName, showArchivedProjects true');
		self::assertEquals($projectName,$project['name']);
		session::setShowArchivedProjects(false);
		session::becomeAdmin();
		$project=baseproject::getByName($projectName);
		self::assertNull($project, 'Archived project visible to admin, getByName, showArchivedProjects false');
	}

	/**
	 * @throws BadRequestException
	 * @throws AuthorizationRequiredException
	 * @throws ServerException
	 * @throws NotFoundException
	 */
	public function testGetByNameAsOwner() {
		$projectName=$this->project1['name'];
		session::setUserToSession($this->user1['name']);
		$project=baseproject::getByName($projectName);
		self::assertNotNull($project, 'Project not visible to owner, getByName');
		self::assertEquals($projectName,$project['name']);
		database::query('UPDATE project SET isarchived=1 WHERE id=:id', [':id'=>$this->project1['id']]);
		session::setShowArchivedProjects(true);
		$project=baseproject::getByName($projectName);
		self::assertNotNull($project, 'Archived project not visible to owner, getByName, showArchivedProjects true');
		self::assertEquals($projectName,$project['name']);
		session::setShowArchivedProjects(false);
		$project=baseproject::getByName($projectName);
		self::assertNull($project, 'Archived project visible to owner, getByName, showArchivedProjects false');
	}

	/**
	 * @throws BadRequestException
	 * @throws AuthorizationRequiredException
	 * @throws ServerException
	 * @throws NotFoundException
	 */
	public function testGetByNameAsOtherPermitted() {
		$projectName=$this->project1['name'];
		session::setUserToSession($this->user2['name']);
		$project=baseproject::getByName($projectName);
		self::assertNotNull($project, 'Project not visible to permitted user, getByName');
		self::assertEquals($projectName,$project['name']);
		database::query('UPDATE project SET isarchived=1 WHERE id=:id', [':id'=>$this->project1['id']]);
		session::setShowArchivedProjects(true);
		$project=baseproject::getByName($projectName);
		self::assertNotNull($project, 'Archived project not visible to permitted user, getByName, showArchivedProjects true');
		self::assertEquals($projectName,$project['name']);
		session::setShowArchivedProjects(false);
		$project=baseproject::getByName($projectName);
		self::assertNull($project, 'Archived project visible to permitted user, getByName, showArchivedProjects false');
	}

	/**
	 * @throws ServerException
	 * @throws BadRequestException
	 * @throws AuthorizationRequiredException
	 */
	public function testGetByNameAsOtherNotPermitted() {
		$projectId=$this->project1['id'];
		session::setUserToSession($this->user3['name']);
		$project=baseproject::getById($projectId);
		self::assertNull($project, 'Project was visible to non-permitted user, getByName');
		database::query('UPDATE project SET isarchived=1 WHERE id=:id', [':id'=>$projectId]);
		session::setShowArchivedProjects(true);
		$project=baseproject::getById($projectId);
		self::assertNull($project, 'Project was visible to non-permitted user, getByName (archived; showArchivedProjects true');
		session::setShowArchivedProjects(false);
		$project=baseproject::getById($projectId);
		self::assertNull($project, 'Project was visible to non-permitted user, getByName (archived; showArchivedProjects false');
	}

	/**
	 * @throws NotFoundException
	 * @throws BadRequestException
	 * @throws AuthorizationRequiredException
	 * @throws ServerException
	 */
	public function testGetByPropertyAsAdmin() {
		$projectId=$this->project1['id'];
		session::setUserToSession($this->user3['name']);
		session::becomeAdmin();
		$result=baseproject::getByProperty('description',$this->project1['description']);
		self::assertArrayHasKey('total',$result);
		self::assertArrayHasKey('rows',$result);
		self::assertEquals(1,$result['total']);
		self::assertEquals($projectId,$result['rows'][0]['id']);
		database::query('UPDATE project SET isarchived=1 WHERE id=:id', [':id'=>$this->project1['id']]);
		session::setShowArchivedProjects(true);
		session::becomeAdmin();
		$result=baseproject::getByProperty('description',$this->project1['description']);
		self::assertArrayHasKey('total',$result);
		self::assertArrayHasKey('rows',$result);
		self::assertEquals(1,$result['total']);
		self::assertEquals($projectId,$result['rows'][0]['id']);
		session::setShowArchivedProjects(false);
		session::becomeAdmin();
		$result=baseproject::getByProperty('description',$this->project1['description']);
		self::assertNull($result);
	}

	/**
	 * @throws BadRequestException
	 * @throws AuthorizationRequiredException
	 * @throws ServerException
	 * @throws NotFoundException
	 */
	public function testGetByPropertyAsOwner() {
		$projectId=$this->project1['id'];
		session::setUserToSession($this->user1['name']);
		$result=baseproject::getByProperty('description',$this->project1['description']);
		self::assertArrayHasKey('total',$result);
		self::assertArrayHasKey('rows',$result);
		self::assertEquals(1,$result['total']);
		self::assertEquals($projectId,$result['rows'][0]['id']);
		database::query('UPDATE project SET isarchived=1 WHERE id=:id', [':id'=>$this->project1['id']]);
		session::setShowArchivedProjects(true);
		$result=baseproject::getByProperty('description',$this->project1['description']);
		self::assertArrayHasKey('total',$result);
		self::assertArrayHasKey('rows',$result);
		self::assertEquals(1,$result['total']);
		self::assertEquals($projectId,$result['rows'][0]['id']);
		session::setShowArchivedProjects(false);
		$result=baseproject::getByProperty('description',$this->project1['description']);
		self::assertNull($result);
	}

	/**
	 * @throws BadRequestException
	 * @throws AuthorizationRequiredException
	 * @throws ServerException
	 * @throws NotFoundException
	 */
	public function testGetByPropertyAsOtherPermitted() {
		$projectId=$this->project1['id'];
		session::setUserToSession($this->user2['name']);
		$result=baseproject::getByProperty('description',$this->project1['description']);
		self::assertNotNull($result,'Project not visible to permitted user, getByProperty');
		self::assertArrayHasKey('total',$result);
		self::assertArrayHasKey('rows',$result);
		self::assertEquals(1,$result['total']);
		self::assertEquals($projectId,$result['rows'][0]['id']);
		database::query('UPDATE project SET isarchived=1 WHERE id=:id', [':id'=>$this->project1['id']]);
		session::setShowArchivedProjects(true);
		$result=baseproject::getByProperty('description',$this->project1['description']);
		self::assertArrayHasKey('total',$result);
		self::assertArrayHasKey('rows',$result);
		self::assertEquals(1,$result['total']);
		self::assertEquals($projectId,$result['rows'][0]['id']);
		session::setShowArchivedProjects(false);
		$result=baseproject::getByProperty('description',$this->project1['description']);
		self::assertNull($result);
	}

	/**
	 * @throws ServerException
	 * @throws BadRequestException
	 * @throws AuthorizationRequiredException
	 */
	public function testGetByPropertyAsOtherNotPermitted() {
		session::setUserToSession($this->user3['name']);
		$result=baseproject::getByProperty('description',$this->project1['description']);
		self::assertNull($result);
	}

	/**
	 * @throws BadRequestException
	 * @throws ForbiddenException
	 * @throws AuthorizationRequiredException
	 * @throws ServerException
	 * @throws NotFoundException
	 */
	public function testChangeOwnerAsAdmin() {
		$projectId=$this->project1['id'];
		session::setUserToSession($this->user3['name']);
		session::becomeAdmin();
		$project=baseproject::getById($projectId);
		self::assertIsArray($project);
		self::assertEquals($project['owner'], $this->user1['id']);
		$newOwner=$this->user3;
		$result=baseproject::update($projectId,['owner'=>$newOwner['id']]);
		self::assertArrayHasKey('updated', $result);
		$project=baseproject::getById($projectId);
		self::assertEquals($project['owner'], $this->user3['id']);
	}
	/**
	 * @throws BadRequestException
	 * @throws ForbiddenException
	 * @throws AuthorizationRequiredException
	 * @throws ServerException
	 * @throws NotFoundException
	 */
	public function testChangeOwnerAsOwner() {
		self::assertFalse(session::isAdmin());
		$projectId=$this->project1['id'];
		session::setUserToSession($this->user1['name']);
		$project=baseproject::getById($projectId);
		self::assertIsArray($project);
		self::assertEquals($project['owner'], $this->user1['id']);
		$newOwner=$this->user3;
		$result=baseproject::update($projectId,['owner'=>$newOwner['id']]);
		self::assertArrayHasKey('updated', $result);
		$project=baseproject::getById($projectId);
		self::assertEquals($project['owner'], $this->user3['id']);
	}

	/**
	 * @throws BadRequestException
	 * @throws ForbiddenException
	 * @throws AuthorizationRequiredException
	 * @throws ServerException
	 * @throws NotFoundException
	 */
	public function testChangeOwnerAsUserWithWritePermissions() {
		self::assertFalse(session::isAdmin());
		$projectId=$this->project1['id'];
		session::setUserToSession($this->user2['name']);
		$project=baseproject::getById($projectId);
		self::assertIsArray($project);
		self::assertEquals($project['owner'], $this->user1['id']);
		$newOwner=$this->user3;
		self::expectException('ForbiddenException');
		baseproject::update($projectId,['owner'=>$newOwner['id']]);
	}
	/**
	 * @throws BadRequestException
	 * @throws ForbiddenException
	 * @throws AuthorizationRequiredException
	 * @throws ServerException
	 * @throws NotFoundException
	 */
	public function testChangeOwnerAsUserWithNoPermissions() {
		self::assertFalse(session::isAdmin());
		$projectId=$this->project1['id'];
		session::setUserToSession($this->user3['name']);
		$newOwner=$this->user3;
		self::expectException('ForbiddenException');
		baseproject::update($projectId,['owner'=>$newOwner['id']]);
	}

}